Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / svgio / source / svgreader / svgimagenode.cxx
blob647048e078504a361366b6619390028e4abbc55e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svgimagenode.hxx>
21 #include <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 #include <comphelper/base64.hxx>
37 namespace svgio
39 namespace svgreader
41 SvgImageNode::SvgImageNode(
42 SvgDocument& rDocument,
43 SvgNode* pParent)
44 : SvgNode(SVGTokenRect, rDocument, pParent),
45 maSvgStyleAttributes(*this),
46 maSvgAspectRatio(),
47 mpaTransform(nullptr),
48 maX(0),
49 maY(0),
50 maWidth(0),
51 maHeight(0),
52 maXLink(),
53 maUrl(),
54 maMimeType(),
55 maData()
59 SvgImageNode::~SvgImageNode()
63 const SvgStyleAttributes* SvgImageNode::getSvgStyleAttributes() const
65 return checkForCssStyle("image", maSvgStyleAttributes);
68 void SvgImageNode::parseAttribute(const OUString& rTokenName, SVGToken aSVGToken, const OUString& aContent)
70 // call parent
71 SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
73 // read style attributes
74 maSvgStyleAttributes.parseStyleAttribute(aSVGToken, aContent, false);
76 // parse own
77 switch(aSVGToken)
79 case SVGTokenStyle:
81 readLocalCssStyle(aContent);
82 break;
84 case SVGTokenPreserveAspectRatio:
86 maSvgAspectRatio = readSvgAspectRatio(aContent);
87 break;
89 case SVGTokenTransform:
91 const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
93 if(!aMatrix.isIdentity())
95 setTransform(&aMatrix);
97 break;
99 case SVGTokenX:
101 SvgNumber aNum;
103 if(readSingleNumber(aContent, aNum))
105 maX = aNum;
107 break;
109 case SVGTokenY:
111 SvgNumber aNum;
113 if(readSingleNumber(aContent, aNum))
115 maY = aNum;
117 break;
119 case SVGTokenWidth:
121 SvgNumber aNum;
123 if(readSingleNumber(aContent, aNum))
125 if(aNum.isPositive())
127 maWidth = aNum;
130 break;
132 case SVGTokenHeight:
134 SvgNumber aNum;
136 if(readSingleNumber(aContent, aNum))
138 if(aNum.isPositive())
140 maHeight = aNum;
143 break;
145 case SVGTokenXlinkHref:
147 const sal_Int32 nLen(aContent.getLength());
149 if(nLen)
151 readImageLink(aContent, maXLink, maUrl, maMimeType, maData);
153 break;
155 default:
157 break;
162 void extractFromGraphic(
163 const Graphic& rGraphic,
164 drawinglayer::primitive2d::Primitive2DContainer& rEmbedded,
165 basegfx::B2DRange& rViewBox,
166 BitmapEx& rBitmapEx)
168 if(GraphicType::Bitmap == rGraphic.GetType())
170 if(rGraphic.getVectorGraphicData().get())
172 // embedded Svg
173 rEmbedded = rGraphic.getVectorGraphicData()->getPrimitive2DSequence();
175 // fill aViewBox
176 rViewBox = rGraphic.getVectorGraphicData()->getRange();
178 else
180 // get bitmap
181 rBitmapEx = rGraphic.GetBitmapEx();
184 else
186 // evtl. convert to bitmap
187 rBitmapEx = rGraphic.GetBitmapEx();
191 void SvgImageNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DContainer& rTarget, bool /*bReferenced*/) const
193 // get size range and create path
194 const SvgStyleAttributes* pStyle = getSvgStyleAttributes();
196 if(pStyle && getWidth().isSet() && getHeight().isSet())
198 const double fWidth(getWidth().solve(*this, xcoordinate));
199 const double fHeight(getHeight().solve(*this, ycoordinate));
201 if(fWidth > 0.0 && fHeight > 0.0)
203 BitmapEx aBitmapEx;
204 drawinglayer::primitive2d::Primitive2DContainer aNewTarget;
206 // prepare Target and ViewBox for evtl. AspectRatio mappings
207 const double fX(getX().isSet() ? getX().solve(*this, xcoordinate) : 0.0);
208 const double fY(getY().isSet() ? getY().solve(*this, ycoordinate) : 0.0);
209 const basegfx::B2DRange aTarget(fX, fY, fX + fWidth, fY + fHeight);
210 basegfx::B2DRange aViewBox(aTarget);
212 if(!maMimeType.isEmpty() && !maData.isEmpty())
214 // use embedded base64 encoded data
215 css::uno::Sequence< sal_Int8 > aPass;
216 ::comphelper::Base64::decode(aPass, maData);
218 if(aPass.hasElements())
220 SvMemoryStream aStream(aPass.getArray(), aPass.getLength(), StreamMode::READ);
221 Graphic aGraphic;
223 if(ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(
224 aGraphic,
225 OUString(),
226 aStream))
228 extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
232 else if(!maUrl.isEmpty())
234 const OUString& rPath = getDocument().getAbsolutePath();
235 OUString aAbsUrl;
236 try {
237 aAbsUrl = rtl::Uri::convertRelToAbs(rPath, maUrl);
238 } catch (rtl::MalformedUriException & e) {
239 SAL_WARN(
240 "svg",
241 "caught rtl::MalformedUriException \""
242 << e.getMessage() << "\"");
245 if (!aAbsUrl.isEmpty() && rPath != aAbsUrl)
247 SvFileStream aStream(aAbsUrl, StreamMode::STD_READ);
248 Graphic aGraphic;
250 if(ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(
251 aGraphic,
252 aAbsUrl,
253 aStream))
255 extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
259 else if(!maXLink.isEmpty())
261 const SvgNode* pXLink = getDocument().findSvgNodeById(maXLink);
263 if(pXLink && Display_none != pXLink->getDisplay())
265 pXLink->decomposeSvgNode(aNewTarget, true);
267 if(!aNewTarget.empty())
269 aViewBox = aNewTarget.getB2DRange(drawinglayer::geometry::ViewInformation2D());
274 if(!aBitmapEx.IsEmpty() && 0 != aBitmapEx.GetSizePixel().Width() && 0 != aBitmapEx.GetSizePixel().Height())
276 // calculate centered unit size
277 const double fAspectRatio = static_cast<double>(aBitmapEx.GetSizePixel().Width()) / static_cast<double>(aBitmapEx.GetSizePixel().Height());
279 if(basegfx::fTools::equal(fAspectRatio, 0.0))
281 // use unit range
282 aViewBox = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
284 else if(basegfx::fTools::more(fAspectRatio, 0.0))
286 // width bigger height
287 const double fHalfHeight((1.0 / fAspectRatio) * 0.5);
288 aViewBox = basegfx::B2DRange(
289 0.0,
290 0.5 - fHalfHeight,
291 1.0,
292 0.5 + fHalfHeight);
294 else
296 // height bigger width
297 const double fHalfWidth(fAspectRatio * 0.5);
298 aViewBox = basegfx::B2DRange(
299 0.5 - fHalfWidth,
300 0.0,
301 0.5 + fHalfWidth,
302 1.0);
305 // create content from created bitmap, use calculated unit range size
306 // as transformation to map the picture data correctly
307 aNewTarget.resize(1);
308 aNewTarget[0] = new drawinglayer::primitive2d::BitmapPrimitive2D(
309 aBitmapEx,
310 basegfx::utils::createScaleTranslateB2DHomMatrix(
311 aViewBox.getRange(),
312 aViewBox.getMinimum()));
315 if(!aNewTarget.empty())
317 if(aTarget.equal(aViewBox))
319 // just add to rTarget
320 rTarget.append(aNewTarget);
322 else
324 // create mapping
325 const SvgAspectRatio& rRatio = maSvgAspectRatio;
327 // even when ratio is not set, use the defaults
328 // let mapping be created from SvgAspectRatio
329 const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createMapping(aTarget, aViewBox));
331 if(!aEmbeddingTransform.isIdentity())
333 const drawinglayer::primitive2d::Primitive2DReference xRef(
334 new drawinglayer::primitive2d::TransformPrimitive2D(
335 aEmbeddingTransform,
336 aNewTarget));
338 aNewTarget = drawinglayer::primitive2d::Primitive2DContainer { xRef };
341 if(!rRatio.isMeetOrSlice())
343 // need to embed in MaskPrimitive2D to ensure clipping
344 const drawinglayer::primitive2d::Primitive2DReference xMask(
345 new drawinglayer::primitive2d::MaskPrimitive2D(
346 basegfx::B2DPolyPolygon(
347 basegfx::utils::createPolygonFromRect(aTarget)),
348 aNewTarget));
350 aNewTarget = drawinglayer::primitive2d::Primitive2DContainer { xMask };
353 // embed and add to rTarget, take local extra-transform into account
354 pStyle->add_postProcess(rTarget, aNewTarget, getTransform());
361 } // end of namespace svgreader
362 } // end of namespace svgio
364 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */