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 <vcl/graphicfilter.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 : return checkForCssStyle(OUString("image"), maSvgStyleAttributes);
68 : }
69 :
70 0 : void SvgImageNode::parseAttribute(const OUString& rTokenName, SVGToken aSVGToken, const OUString& aContent)
71 : {
72 : // call parent
73 0 : SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
74 :
75 : // read style attributes
76 0 : maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent);
77 :
78 : // parse own
79 0 : switch(aSVGToken)
80 : {
81 : case SVGTokenStyle:
82 : {
83 0 : maSvgStyleAttributes.readStyle(aContent);
84 0 : break;
85 : }
86 : case SVGTokenPreserveAspectRatio:
87 : {
88 0 : setSvgAspectRatio(readSvgAspectRatio(aContent));
89 0 : break;
90 : }
91 : case SVGTokenTransform:
92 : {
93 0 : const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
94 :
95 0 : if(!aMatrix.isIdentity())
96 : {
97 0 : setTransform(&aMatrix);
98 : }
99 0 : break;
100 : }
101 : case SVGTokenX:
102 : {
103 0 : SvgNumber aNum;
104 :
105 0 : if(readSingleNumber(aContent, aNum))
106 : {
107 0 : setX(aNum);
108 : }
109 0 : break;
110 : }
111 : case SVGTokenY:
112 : {
113 0 : SvgNumber aNum;
114 :
115 0 : if(readSingleNumber(aContent, aNum))
116 : {
117 0 : setY(aNum);
118 : }
119 0 : break;
120 : }
121 : case SVGTokenWidth:
122 : {
123 0 : SvgNumber aNum;
124 :
125 0 : if(readSingleNumber(aContent, aNum))
126 : {
127 0 : if(aNum.isPositive())
128 : {
129 0 : setWidth(aNum);
130 : }
131 : }
132 0 : break;
133 : }
134 : case SVGTokenHeight:
135 : {
136 0 : SvgNumber aNum;
137 :
138 0 : if(readSingleNumber(aContent, aNum))
139 : {
140 0 : if(aNum.isPositive())
141 : {
142 0 : setHeight(aNum);
143 : }
144 : }
145 0 : break;
146 : }
147 : case SVGTokenXlinkHref:
148 : {
149 0 : const sal_Int32 nLen(aContent.getLength());
150 :
151 0 : if(nLen)
152 : {
153 0 : readImageLink(aContent, maXLink, maUrl, maMimeType, maData);
154 : }
155 0 : break;
156 : }
157 : default:
158 : {
159 0 : break;
160 : }
161 : }
162 0 : }
163 :
164 0 : void extractFromGraphic(
165 : const Graphic& rGraphic,
166 : drawinglayer::primitive2d::Primitive2DSequence& rEmbedded,
167 : basegfx::B2DRange& rViewBox,
168 : BitmapEx& rBitmapEx)
169 : {
170 0 : if(GRAPHIC_BITMAP == rGraphic.GetType())
171 : {
172 0 : if(rGraphic.getSvgData().get())
173 : {
174 : // embedded Svg
175 0 : rEmbedded = rGraphic.getSvgData()->getPrimitive2DSequence();
176 :
177 : // fill aViewBox
178 0 : rViewBox = rGraphic.getSvgData()->getRange();
179 : }
180 : else
181 : {
182 : // get bitmap
183 0 : rBitmapEx = rGraphic.GetBitmapEx();
184 : }
185 : }
186 : else
187 : {
188 : // evtl. convert to bitmap
189 0 : rBitmapEx = rGraphic.GetBitmapEx();
190 : }
191 0 : }
192 :
193 0 : void SvgImageNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool /*bReferenced*/) const
194 : {
195 : // get size range and create path
196 0 : const SvgStyleAttributes* pStyle = getSvgStyleAttributes();
197 :
198 0 : if(pStyle && getWidth().isSet() && getHeight().isSet())
199 : {
200 0 : const double fWidth(getWidth().solve(*this, xcoordinate));
201 0 : const double fHeight(getHeight().solve(*this, ycoordinate));
202 :
203 0 : if(fWidth > 0.0 && fHeight > 0.0)
204 : {
205 0 : BitmapEx aBitmapEx;
206 0 : drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
207 :
208 : // prepare Target and ViewBox for evtl. AspectRatio mappings
209 0 : const double fX(getX().isSet() ? getX().solve(*this, xcoordinate) : 0.0);
210 0 : const double fY(getY().isSet() ? getY().solve(*this, ycoordinate) : 0.0);
211 0 : const basegfx::B2DRange aTarget(fX, fY, fX + fWidth, fY + fHeight);
212 0 : basegfx::B2DRange aViewBox(aTarget);
213 :
214 0 : if(!maMimeType.isEmpty() && !maData.isEmpty())
215 : {
216 : // use embedded base64 encoded data
217 0 : ::com::sun::star::uno::Sequence< sal_Int8 > aPass;
218 0 : ::sax::Converter::decodeBase64(aPass, maData);
219 :
220 0 : if(aPass.hasElements())
221 : {
222 0 : SvMemoryStream aStream(aPass.getArray(), aPass.getLength(), STREAM_READ);
223 0 : Graphic aGraphic;
224 :
225 0 : if(GRFILTER_OK == GraphicFilter::GetGraphicFilter().ImportGraphic(
226 : aGraphic,
227 : OUString(),
228 0 : aStream))
229 : {
230 0 : extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
231 0 : }
232 0 : }
233 : }
234 0 : else if(!maUrl.isEmpty())
235 : {
236 0 : const OUString& rPath = getDocument().getAbsolutePath();
237 0 : OUString aAbsUrl;
238 : try {
239 0 : aAbsUrl = rtl::Uri::convertRelToAbs(rPath, maUrl);
240 0 : } catch (rtl::MalformedUriException & e) {
241 : SAL_WARN(
242 : "svg",
243 : "caught rtl::MalformedUriException \""
244 : << e.getMessage() << "\"");
245 : }
246 :
247 0 : if (!aAbsUrl.isEmpty())
248 : {
249 0 : SvFileStream aStream(aAbsUrl, STREAM_STD_READ);
250 0 : Graphic aGraphic;
251 :
252 0 : if(GRFILTER_OK == GraphicFilter::GetGraphicFilter().ImportGraphic(
253 : aGraphic,
254 : aAbsUrl,
255 0 : aStream))
256 : {
257 0 : extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
258 0 : }
259 0 : }
260 : }
261 0 : else if(!maXLink.isEmpty())
262 : {
263 0 : const SvgNode* mpXLink = getDocument().findSvgNodeById(maXLink);
264 :
265 0 : if(mpXLink && Display_none != mpXLink->getDisplay())
266 : {
267 0 : mpXLink->decomposeSvgNode(aNewTarget, true);
268 :
269 0 : if(aNewTarget.hasElements())
270 : {
271 : aViewBox = drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
272 : aNewTarget,
273 0 : drawinglayer::geometry::ViewInformation2D());
274 : }
275 : }
276 : }
277 :
278 0 : if(!aBitmapEx.IsEmpty())
279 : {
280 : // create content from created bitmap
281 0 : aNewTarget.realloc(1);
282 0 : aNewTarget[0] = new drawinglayer::primitive2d::BitmapPrimitive2D(
283 : aBitmapEx,
284 0 : basegfx::B2DHomMatrix());
285 :
286 : // fill aViewBox. No size set yet, use unit size
287 0 : aViewBox = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
288 : }
289 :
290 0 : if(aNewTarget.hasElements())
291 : {
292 0 : if(aTarget.equal(aViewBox))
293 : {
294 : // just add to rTarget
295 0 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
296 : }
297 : else
298 : {
299 : // create mapping
300 0 : const SvgAspectRatio& rRatio = getSvgAspectRatio();
301 :
302 0 : if(rRatio.isSet())
303 : {
304 : // let mapping be created from SvgAspectRatio
305 0 : const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createMapping(aTarget, aViewBox));
306 :
307 0 : if(!aEmbeddingTransform.isIdentity())
308 : {
309 : const drawinglayer::primitive2d::Primitive2DReference xRef(
310 : new drawinglayer::primitive2d::TransformPrimitive2D(
311 : aEmbeddingTransform,
312 0 : aNewTarget));
313 :
314 0 : aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
315 : }
316 :
317 0 : if(!rRatio.isMeetOrSlice())
318 : {
319 : // need to embed in MaskPrimitive2D to ensure clipping
320 : const drawinglayer::primitive2d::Primitive2DReference xMask(
321 : new drawinglayer::primitive2d::MaskPrimitive2D(
322 : basegfx::B2DPolyPolygon(
323 : basegfx::tools::createPolygonFromRect(aTarget)),
324 0 : aNewTarget));
325 :
326 0 : aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
327 0 : }
328 : }
329 : else
330 : {
331 : // choose default mapping
332 0 : const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createLinearMapping(aTarget, aViewBox));
333 :
334 0 : if(!aEmbeddingTransform.isIdentity())
335 : {
336 : const drawinglayer::primitive2d::Primitive2DReference xRef(
337 : new drawinglayer::primitive2d::TransformPrimitive2D(
338 : aEmbeddingTransform,
339 0 : aNewTarget));
340 :
341 0 : aNewTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
342 0 : }
343 : }
344 :
345 : // embed and add to rTarget, take local extra-transform into account
346 0 : pStyle->add_postProcess(rTarget, aNewTarget, getTransform());
347 : }
348 0 : }
349 : }
350 : }
351 0 : }
352 :
353 : } // end of namespace svgreader
354 : } // end of namespace svgio
355 :
356 :
357 : // eof
358 :
359 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|