Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <svgio/svgreader/svgimagenode.hxx>
21 : #include <svgio/svgreader/svgdocument.hxx>
22 : #include <sax/tools/converter.hxx>
23 : #include <tools/stream.hxx>
24 : #include <vcl/bitmapex.hxx>
25 : #include <svtools/filter.hxx>
26 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
27 : #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
28 : #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
29 : #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
30 : #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
31 : #include <basegfx/polygon/b2dpolygontools.hxx>
32 : #include <basegfx/polygon/b2dpolygon.hxx>
33 : #include <rtl/uri.hxx>
34 : #include <drawinglayer/geometry/viewinformation2d.hxx>
35 :
36 : //////////////////////////////////////////////////////////////////////////////
37 :
38 : namespace svgio
39 : {
40 : namespace svgreader
41 : {
42 0 : SvgImageNode::SvgImageNode(
43 : SvgDocument& rDocument,
44 : SvgNode* pParent)
45 : : SvgNode(SVGTokenRect, rDocument, pParent),
46 : maSvgStyleAttributes(*this),
47 : maSvgAspectRatio(),
48 : mpaTransform(0),
49 : maX(0),
50 : maY(0),
51 : maWidth(0),
52 : maHeight(0),
53 : maXLink(),
54 : maUrl(),
55 : maMimeType(),
56 0 : maData()
57 : {
58 0 : }
59 :
60 0 : SvgImageNode::~SvgImageNode()
61 : {
62 0 : if(mpaTransform) delete mpaTransform;
63 0 : }
64 :
65 0 : const SvgStyleAttributes* SvgImageNode::getSvgStyleAttributes() const
66 : {
67 0 : static rtl::OUString aClassStr(rtl::OUString::createFromAscii("image"));
68 0 : maSvgStyleAttributes.checkForCssStyle(aClassStr);
69 :
70 0 : return &maSvgStyleAttributes;
71 : }
72 :
73 0 : void SvgImageNode::parseAttribute(const rtl::OUString& rTokenName, SVGToken aSVGToken, const rtl::OUString& aContent)
74 : {
75 : // call parent
76 0 : SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
77 :
78 : // read style attributes
79 0 : maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent);
80 :
81 : // parse own
82 0 : switch(aSVGToken)
83 : {
84 : case SVGTokenStyle:
85 : {
86 0 : maSvgStyleAttributes.readStyle(aContent);
87 0 : break;
88 : }
89 : case SVGTokenPreserveAspectRatio:
90 : {
91 0 : setSvgAspectRatio(readSvgAspectRatio(aContent));
92 0 : break;
93 : }
94 : case SVGTokenTransform:
95 : {
96 0 : const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
97 :
98 0 : if(!aMatrix.isIdentity())
99 : {
100 0 : setTransform(&aMatrix);
101 : }
102 0 : break;
103 : }
104 : case SVGTokenX:
105 : {
106 0 : SvgNumber aNum;
107 :
108 0 : if(readSingleNumber(aContent, aNum))
109 : {
110 0 : setX(aNum);
111 : }
112 : break;
113 : }
114 : case SVGTokenY:
115 : {
116 0 : SvgNumber aNum;
117 :
118 0 : if(readSingleNumber(aContent, aNum))
119 : {
120 0 : setY(aNum);
121 : }
122 : break;
123 : }
124 : case SVGTokenWidth:
125 : {
126 0 : SvgNumber aNum;
127 :
128 0 : if(readSingleNumber(aContent, aNum))
129 : {
130 0 : if(aNum.isPositive())
131 : {
132 0 : setWidth(aNum);
133 : }
134 : }
135 : break;
136 : }
137 : case SVGTokenHeight:
138 : {
139 0 : SvgNumber aNum;
140 :
141 0 : if(readSingleNumber(aContent, aNum))
142 : {
143 0 : if(aNum.isPositive())
144 : {
145 0 : setHeight(aNum);
146 : }
147 : }
148 : break;
149 : }
150 : case SVGTokenXlinkHref:
151 : {
152 0 : const sal_Int32 nLen(aContent.getLength());
153 :
154 0 : if(nLen)
155 : {
156 0 : readImageLink(aContent, maXLink, maUrl, maMimeType, maData);
157 : }
158 0 : break;
159 : }
160 : default:
161 : {
162 0 : break;
163 : }
164 : }
165 0 : }
166 :
167 0 : void extractFromGraphic(
168 : const Graphic& rGraphic,
169 : drawinglayer::primitive2d::Primitive2DSequence& rEmbedded,
170 : basegfx::B2DRange& rViewBox,
171 : BitmapEx& rBitmapEx)
172 : {
173 0 : if(GRAPHIC_BITMAP == rGraphic.GetType())
174 : {
175 0 : if(rGraphic.getSvgData().get())
176 : {
177 : // embedded Svg
178 0 : rEmbedded = rGraphic.getSvgData()->getPrimitive2DSequence();
179 :
180 : // fill aViewBox
181 0 : rViewBox = rGraphic.getSvgData()->getRange();
182 : }
183 : else
184 : {
185 : // get bitmap
186 0 : rBitmapEx = rGraphic.GetBitmapEx();
187 : }
188 : }
189 : else
190 : {
191 : // evtl. convert to bitmap
192 0 : rBitmapEx = rGraphic.GetBitmapEx();
193 : }
194 0 : }
195 :
196 0 : void SvgImageNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool /*bReferenced*/) const
197 : {
198 : // get size range and create path
199 0 : const SvgStyleAttributes* pStyle = getSvgStyleAttributes();
200 :
201 0 : if(pStyle && getWidth().isSet() && getHeight().isSet())
202 : {
203 0 : const double fWidth(getWidth().solve(*this, xcoordinate));
204 0 : const double fHeight(getHeight().solve(*this, ycoordinate));
205 :
206 0 : if(fWidth > 0.0 && fHeight > 0.0)
207 : {
208 0 : BitmapEx aBitmapEx;
209 0 : drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
210 :
211 : // prepare Target and ViewBox for evtl. AspectRatio mappings
212 0 : const double fX(getX().isSet() ? getX().solve(*this, xcoordinate) : 0.0);
213 0 : const double fY(getY().isSet() ? getY().solve(*this, ycoordinate) : 0.0);
214 0 : const basegfx::B2DRange aTarget(fX, fY, fX + fWidth, fY + fHeight);
215 0 : basegfx::B2DRange aViewBox(aTarget);
216 :
217 0 : if(maMimeType.getLength() && maData.getLength())
218 : {
219 : // use embedded base64 encoded data
220 0 : ::com::sun::star::uno::Sequence< sal_Int8 > aPass;
221 0 : ::sax::Converter::decodeBase64(aPass, maData);
222 :
223 0 : if(aPass.hasElements())
224 : {
225 0 : SvMemoryStream aStream(aPass.getArray(), aPass.getLength(), STREAM_READ);
226 0 : Graphic aGraphic;
227 :
228 0 : if(GRFILTER_OK == GraphicFilter::GetGraphicFilter().ImportGraphic(
229 : aGraphic,
230 : String(),
231 0 : aStream))
232 : {
233 0 : extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
234 0 : }
235 0 : }
236 : }
237 0 : else if(maUrl.getLength())
238 : {
239 0 : const rtl::OUString& rPath = getDocument().getAbsolutePath();
240 0 : const rtl::OUString aAbsUrl(rtl::Uri::convertRelToAbs(rPath, maUrl));
241 :
242 0 : if(aAbsUrl.getLength())
243 : {
244 0 : SvFileStream aStream(aAbsUrl, STREAM_STD_READ);
245 0 : Graphic aGraphic;
246 :
247 0 : if(GRFILTER_OK == GraphicFilter::GetGraphicFilter().ImportGraphic(
248 : aGraphic,
249 : aAbsUrl,
250 0 : aStream))
251 : {
252 0 : extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
253 0 : }
254 0 : }
255 : }
256 0 : else if(maXLink.getLength())
257 : {
258 0 : const SvgNode* mpXLink = getDocument().findSvgNodeById(maXLink);
259 :
260 0 : if(mpXLink)
261 : {
262 0 : mpXLink->decomposeSvgNode(aNewTarget, true);
263 :
264 0 : if(aNewTarget.hasElements())
265 : {
266 : aViewBox = drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
267 : aNewTarget,
268 0 : drawinglayer::geometry::ViewInformation2D());
269 : }
270 : }
271 : }
272 :
273 0 : if(!aBitmapEx.IsEmpty())
274 : {
275 : // create content from created bitmap
276 0 : aNewTarget.realloc(1);
277 0 : aNewTarget[0] = new drawinglayer::primitive2d::BitmapPrimitive2D(
278 : aBitmapEx,
279 0 : basegfx::B2DHomMatrix());
280 :
281 : // fill aViewBox. No size set yet, use unit size
282 0 : aViewBox = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
283 : }
284 :
285 0 : if(aNewTarget.hasElements())
286 : {
287 0 : if(aTarget.equal(aViewBox))
288 : {
289 : // just add to rTarget
290 0 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
291 : }
292 : else
293 : {
294 : // create mapping
295 0 : const SvgAspectRatio& rRatio = getSvgAspectRatio();
296 :
297 0 : if(rRatio.isSet())
298 : {
299 : // let mapping be created from SvgAspectRatio
300 0 : const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createMapping(aTarget, aViewBox));
301 :
302 0 : if(!aEmbeddingTransform.isIdentity())
303 : {
304 : const drawinglayer::primitive2d::Primitive2DReference xRef(
305 : new drawinglayer::primitive2d::TransformPrimitive2D(
306 : aEmbeddingTransform,
307 0 : aNewTarget));
308 :
309 0 : aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
310 : }
311 :
312 0 : if(!rRatio.isMeetOrSlice())
313 : {
314 : // need to embed in MaskPrimitive2D to ensure clipping
315 : const drawinglayer::primitive2d::Primitive2DReference xMask(
316 : new drawinglayer::primitive2d::MaskPrimitive2D(
317 : basegfx::B2DPolyPolygon(
318 : basegfx::tools::createPolygonFromRect(aTarget)),
319 0 : aNewTarget));
320 :
321 0 : aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
322 0 : }
323 : }
324 : else
325 : {
326 : // choose default mapping
327 0 : const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createLinearMapping(aTarget, aViewBox));
328 :
329 0 : if(!aEmbeddingTransform.isIdentity())
330 : {
331 : const drawinglayer::primitive2d::Primitive2DReference xRef(
332 : new drawinglayer::primitive2d::TransformPrimitive2D(
333 : aEmbeddingTransform,
334 0 : aNewTarget));
335 :
336 0 : aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
337 0 : }
338 : }
339 :
340 : // embed and add to rTarget, take local extra-transform into account
341 0 : pStyle->add_postProcess(rTarget, aNewTarget, getTransform());
342 : }
343 0 : }
344 : }
345 : }
346 0 : }
347 :
348 : } // end of namespace svgreader
349 : } // end of namespace svgio
350 :
351 : //////////////////////////////////////////////////////////////////////////////
352 : // eof
353 :
354 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|