LibreOffice Module svgio (master) 1
svgimagenode.cxx
Go to the documentation of this file.
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 <svgimagenode.hxx>
21#include <svgdocument.hxx>
22#include <tools/stream.hxx>
23#include <vcl/bitmapex.hxx>
24#include <vcl/graphicfilter.hxx>
31#include <rtl/uri.hxx>
32#include <sal/log.hxx>
34#include <comphelper/base64.hxx>
36
37namespace svgio::svgreader
38{
40 SvgDocument& rDocument,
41 SvgNode* pParent)
42 : SvgNode(SVGToken::Rect, rDocument, pParent),
43 maSvgStyleAttributes(*this),
44 maX(0),
45 maY(0),
46 maWidth(0),
47 maHeight(0)
48 {
49 }
50
52 {
53 }
54
56 {
58 }
59
60 void SvgImageNode::parseAttribute(const OUString& rTokenName, SVGToken aSVGToken, const OUString& aContent)
61 {
62 // call parent
63 SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
64
65 // read style attributes
66 maSvgStyleAttributes.parseStyleAttribute(aSVGToken, aContent);
67
68 // parse own
69 switch(aSVGToken)
70 {
71 case SVGToken::Style:
72 {
73 readLocalCssStyle(aContent);
74 break;
75 }
77 {
79 break;
80 }
82 {
83 const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
84
85 if(!aMatrix.isIdentity())
86 {
87 setTransform(aMatrix);
88 }
89 break;
90 }
91 case SVGToken::X:
92 {
93 SvgNumber aNum;
94
95 if(readSingleNumber(aContent, aNum))
96 {
97 maX = aNum;
98 }
99 break;
100 }
101 case SVGToken::Y:
102 {
103 SvgNumber aNum;
104
105 if(readSingleNumber(aContent, aNum))
106 {
107 maY = aNum;
108 }
109 break;
110 }
111 case SVGToken::Width:
112 {
113 SvgNumber aNum;
114
115 if(readSingleNumber(aContent, aNum))
116 {
117 if(aNum.isPositive())
118 {
119 maWidth = aNum;
120 }
121 }
122 break;
123 }
124 case SVGToken::Height:
125 {
126 SvgNumber aNum;
127
128 if(readSingleNumber(aContent, aNum))
129 {
130 if(aNum.isPositive())
131 {
132 maHeight = aNum;
133 }
134 }
135 break;
136 }
137 case SVGToken::Href:
139 {
140 const sal_Int32 nLen(aContent.getLength());
141
142 if(nLen)
143 {
144 readImageLink(aContent, maXLink, maUrl, maData);
145 }
146 break;
147 }
148 default:
149 {
150 break;
151 }
152 }
153 }
154
156 const Graphic& rGraphic,
158 basegfx::B2DRange& rViewBox,
159 BitmapEx& rBitmapEx)
160 {
161 if(GraphicType::Bitmap == rGraphic.GetType())
162 {
163 if(rGraphic.getVectorGraphicData())
164 {
165 // embedded Svg
166 rEmbedded = rGraphic.getVectorGraphicData()->getPrimitive2DSequence();
167
168 // fill aViewBox
169 rViewBox = rGraphic.getVectorGraphicData()->getRange();
170 }
171 else
172 {
173 // get bitmap
174 rBitmapEx = rGraphic.GetBitmapEx();
175 }
176 }
177 else
178 {
179 // evtl. convert to bitmap
180 rBitmapEx = rGraphic.GetBitmapEx();
181 }
182 }
183
185 {
186 // get size range and create path
188
189 if(!(pStyle && getWidth().isSet() && getHeight().isSet()))
190 return;
191
192 const double fWidth(getWidth().solve(*this, NumberType::xcoordinate));
193 const double fHeight(getHeight().solve(*this, NumberType::ycoordinate));
194
195 if(fWidth <= 0.0 || fHeight <= 0.0)
196 return;
197
198 BitmapEx aBitmapEx;
200
201 // prepare Target and ViewBox for evtl. AspectRatio mappings
202 const double fX(getX().isSet() ? getX().solve(*this, NumberType::xcoordinate) : 0.0);
203 const double fY(getY().isSet() ? getY().solve(*this, NumberType::ycoordinate) : 0.0);
204 const basegfx::B2DRange aTarget(fX, fY, fX + fWidth, fY + fHeight);
205 basegfx::B2DRange aViewBox(aTarget);
206
207 if(!maData.isEmpty())
208 {
209 // use embedded base64 encoded data
210 css::uno::Sequence< sal_Int8 > aPass;
212
213 if(aPass.hasElements())
214 {
215 SvMemoryStream aStream(aPass.getArray(), aPass.getLength(), StreamMode::READ);
216 Graphic aGraphic;
217
218 if(ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(
219 aGraphic,
220 u"",
221 aStream))
222 {
223 extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
224 }
225 }
226 }
227 else if(!maUrl.isEmpty())
228 {
229 const OUString& rPath = getDocument().getAbsolutePath();
230 OUString aAbsUrl;
231 try {
232 aAbsUrl = rtl::Uri::convertRelToAbs(rPath, maUrl);
233 } catch (rtl::MalformedUriException & e) {
234 SAL_WARN(
235 "svg",
236 "caught rtl::MalformedUriException \""
237 << e.getMessage() << "\"");
238 }
239
240 if (!aAbsUrl.isEmpty() && rPath != aAbsUrl)
241 {
242 SvFileStream aStream(aAbsUrl, StreamMode::STD_READ);
243 Graphic aGraphic;
244
245 if(ERRCODE_NONE == GraphicFilter::GetGraphicFilter().ImportGraphic(
246 aGraphic,
247 aAbsUrl,
248 aStream))
249 {
250 extractFromGraphic(aGraphic, aNewTarget, aViewBox, aBitmapEx);
251 }
252 }
253 }
254 else if(!maXLink.isEmpty())
255 {
256 const SvgNode* pXLink = getDocument().findSvgNodeById(maXLink);
257
258 if(pXLink && Display::None != pXLink->getDisplay())
259 {
260 pXLink->decomposeSvgNode(aNewTarget, true);
261
262 if(!aNewTarget.empty())
263 {
265 }
266 }
267 }
268
269 if(!aBitmapEx.IsEmpty() && 0 != aBitmapEx.GetSizePixel().Width() && 0 != aBitmapEx.GetSizePixel().Height())
270 {
271 // calculate centered unit size
272 const double fAspectRatio = static_cast<double>(aBitmapEx.GetSizePixel().Width()) / static_cast<double>(aBitmapEx.GetSizePixel().Height());
273
274 if(basegfx::fTools::equal(fAspectRatio, 0.0))
275 {
276 // use unit range
277 aViewBox = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
278 }
279 else if(basegfx::fTools::more(fAspectRatio, 0.0))
280 {
281 // width bigger height
282 const double fHalfHeight((1.0 / fAspectRatio) * 0.5);
283 aViewBox = basegfx::B2DRange(
284 0.0,
285 0.5 - fHalfHeight,
286 1.0,
287 0.5 + fHalfHeight);
288 }
289 else
290 {
291 // height bigger width
292 const double fHalfWidth(fAspectRatio * 0.5);
293 aViewBox = basegfx::B2DRange(
294 0.5 - fHalfWidth,
295 0.0,
296 0.5 + fHalfWidth,
297 1.0);
298 }
299
300 // create content from created bitmap, use calculated unit range size
301 // as transformation to map the picture data correctly
302 aNewTarget.resize(1);
304 aBitmapEx,
306 aViewBox.getRange(),
307 aViewBox.getMinimum()));
308 }
309
310 if(aNewTarget.empty())
311 return;
312
313 // create mapping
314 const SvgAspectRatio& rRatio = maSvgAspectRatio;
315
316 // even when ratio is not set, use the defaults
317 // let mapping be created from SvgAspectRatio
318 const basegfx::B2DHomMatrix aEmbeddingTransform(rRatio.createMapping(aTarget, aViewBox));
319
320 if(!aEmbeddingTransform.isIdentity())
321 {
324 aEmbeddingTransform,
325 std::move(aNewTarget)));
326
328 }
329
330 if(!rRatio.isMeetOrSlice())
331 {
332 // need to embed in MaskPrimitive2D to ensure clipping
337 std::move(aNewTarget)));
338
340 }
341
342 // embed and add to rTarget, take local extra-transform into account
343 pStyle->add_postProcess(rTarget, std::move(aNewTarget), getTransform());
344 }
345
346} // end of namespace svgio::svgreader
347
348/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsEmpty() const
const Size & GetSizePixel() const
static GraphicFilter & GetGraphicFilter()
GraphicType GetType() const
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
const std::shared_ptr< VectorGraphicData > & getVectorGraphicData() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
bool isIdentity() const
B2DVector getRange() const
B2DPoint getMinimum() const
static void decode(css::uno::Sequence< sal_Int8 > &aPass, std::u16string_view sBuffer)
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
basegfx::B2DHomMatrix createMapping(const basegfx::B2DRange &rTarget, const basegfx::B2DRange &rSource) const
Definition: svgtools.cxx:206
const OUString & getAbsolutePath() const
Definition: svgdocument.hxx:77
const SvgNode * findSvgNodeById(const OUString &rStr) const
find a node by its Id
Definition: svgdocument.cxx:56
virtual const SvgStyleAttributes * getSvgStyleAttributes() const override
SvgStyleAttributes maSvgStyleAttributes
use styles
SvgAspectRatio maSvgAspectRatio
variable scan values, dependent of given XAttributeList
const SvgNumber & getX() const
x content, set if found in current context
virtual ~SvgImageNode() override
virtual void parseAttribute(const OUString &rTokenName, SVGToken aSVGToken, const OUString &aContent) override
const SvgNumber & getHeight() const
height content, set if found in current context
const SvgNumber & getWidth() const
width content, set if found in current context
const std::optional< basegfx::B2DHomMatrix > & getTransform() const
transform content, set if found in current context
virtual void decomposeSvgNode(drawinglayer::primitive2d::Primitive2DContainer &rTarget, bool bReferenced) const override
SvgImageNode(SvgDocument &rDocument, SvgNode *pParent)
const SvgNumber & getY() const
y content, set if found in current context
void setTransform(const std::optional< basegfx::B2DHomMatrix > &pMatrix)
Display getDisplay() const
Display access #i121656#.
Definition: svgnode.hxx:182
virtual void decomposeSvgNode(drawinglayer::primitive2d::Primitive2DContainer &rTarget, bool bReferenced) const
Definition: svgnode.cxx:584
const SvgStyleAttributes * checkForCssStyle(const SvgStyleAttributes &rOriginal) const
helper to evtl. link to css style
Definition: svgnode.cxx:335
virtual void parseAttribute(const OUString &rTokenName, SVGToken aSVGToken, const OUString &aContent)
Definition: svgnode.cxx:534
void readLocalCssStyle(std::u16string_view aContent)
scan helper to read and interpret a local CssStyle to mpLocalCssStyle
Definition: svgnode.cxx:412
const SvgDocument & getDocument() const
Definition: svgnode.hxx:157
void add_postProcess(drawinglayer::primitive2d::Primitive2DContainer &rTarget, drawinglayer::primitive2d::Primitive2DContainer &&rSource, const std::optional< basegfx::B2DHomMatrix > &pTransform) const
void parseStyleAttribute(SVGToken aSVGToken, const OUString &rContent)
local attribute scanner
float u
#define ERRCODE_NONE
FilterGroup & rTarget
bool solve(Matrix &matrix, int rows, int cols, Vector &result, BaseType minPivot)
#define SAL_WARN(area, stream)
bool more(const T &rfValA, const T &rfValB)
bool equal(T const &rfValA, T const &rfValB)
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
static void extractFromGraphic(const Graphic &rGraphic, drawinglayer::primitive2d::Primitive2DContainer &rEmbedded, basegfx::B2DRange &rViewBox, BitmapEx &rBitmapEx)
bool readSingleNumber(std::u16string_view rCandidate, SvgNumber &aNum)
Definition: svgtools.cxx:1076
basegfx::B2DHomMatrix readTransform(std::u16string_view rCandidate, InfoProvider const &rInfoProvider)
Definition: svgtools.cxx:871
SvgAspectRatio readSvgAspectRatio(std::u16string_view rCandidate)
Definition: svgtools.cxx:1197
void readImageLink(const OUString &rCandidate, OUString &rXLink, OUString &rUrl, OUString &rData)
Definition: svgtools.cxx:1346