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/svgsvgnode.hxx>
21 : #include <drawinglayer/geometry/viewinformation2d.hxx>
22 : #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
23 : #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
24 : #include <basegfx/polygon/b2dpolygontools.hxx>
25 : #include <basegfx/polygon/b2dpolygon.hxx>
26 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
27 : #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
28 : #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
29 :
30 :
31 :
32 : namespace svgio
33 : {
34 : namespace svgreader
35 : {
36 0 : SvgSvgNode::SvgSvgNode(
37 : SvgDocument& rDocument,
38 : SvgNode* pParent)
39 : : SvgNode(SVGTokenSvg, rDocument, pParent),
40 : maSvgStyleAttributes(*this),
41 : mpViewBox(0),
42 : maSvgAspectRatio(),
43 : maX(),
44 : maY(),
45 : maWidth(),
46 : maHeight(),
47 0 : maVersion()
48 : {
49 0 : if(!getParent())
50 : {
51 : // initial fill is black
52 0 : maSvgStyleAttributes.setFill(SvgPaint(basegfx::BColor(0.0, 0.0, 0.0), true, true));
53 : }
54 0 : }
55 :
56 0 : SvgSvgNode::~SvgSvgNode()
57 : {
58 0 : if(mpViewBox) delete mpViewBox;
59 0 : }
60 :
61 0 : const SvgStyleAttributes* SvgSvgNode::getSvgStyleAttributes() const
62 : {
63 0 : return checkForCssStyle(OUString("svg"), maSvgStyleAttributes);
64 : }
65 :
66 0 : void SvgSvgNode::parseAttribute(const OUString& rTokenName, SVGToken aSVGToken, const OUString& aContent)
67 : {
68 : // call parent
69 0 : SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
70 :
71 : // read style attributes
72 0 : maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent);
73 :
74 : // parse own
75 0 : switch(aSVGToken)
76 : {
77 : case SVGTokenStyle:
78 : {
79 0 : maSvgStyleAttributes.readStyle(aContent);
80 0 : break;
81 : }
82 : case SVGTokenViewBox:
83 : {
84 0 : const basegfx::B2DRange aRange(readViewBox(aContent, *this));
85 :
86 0 : if(!aRange.isEmpty())
87 : {
88 0 : setViewBox(&aRange);
89 : }
90 0 : break;
91 : }
92 : case SVGTokenPreserveAspectRatio:
93 : {
94 0 : setSvgAspectRatio(readSvgAspectRatio(aContent));
95 0 : break;
96 : }
97 : case SVGTokenX:
98 : {
99 0 : SvgNumber aNum;
100 :
101 0 : if(readSingleNumber(aContent, aNum))
102 : {
103 0 : setX(aNum);
104 : }
105 0 : break;
106 : }
107 : case SVGTokenY:
108 : {
109 0 : SvgNumber aNum;
110 :
111 0 : if(readSingleNumber(aContent, aNum))
112 : {
113 0 : setY(aNum);
114 : }
115 0 : break;
116 : }
117 : case SVGTokenWidth:
118 : {
119 0 : SvgNumber aNum;
120 :
121 0 : if(readSingleNumber(aContent, aNum))
122 : {
123 0 : if(aNum.isPositive())
124 : {
125 0 : setWidth(aNum);
126 : }
127 : }
128 0 : break;
129 : }
130 : case SVGTokenHeight:
131 : {
132 0 : SvgNumber aNum;
133 :
134 0 : if(readSingleNumber(aContent, aNum))
135 : {
136 0 : if(aNum.isPositive())
137 : {
138 0 : setHeight(aNum);
139 : }
140 : }
141 0 : break;
142 : }
143 : case SVGTokenVersion:
144 : {
145 0 : SvgNumber aNum;
146 :
147 0 : if(readSingleNumber(aContent, aNum))
148 : {
149 0 : setVersion(aNum);
150 : }
151 0 : break;
152 : }
153 : default:
154 : {
155 0 : break;
156 : }
157 : }
158 0 : }
159 :
160 0 : void SvgSvgNode::seekReferenceWidth(double& fWidth, bool& bHasFound) const
161 : {
162 0 : if (!getParent() || bHasFound)
163 : {
164 0 : return;
165 : }
166 0 : const SvgSvgNode* pParentSvgSvgNode = 0;
167 : // enclosing svg might have relative width, need to cumulate them till they are
168 : // resolved somewhere up in the node tree
169 0 : double fPercentage(1.0);
170 0 : for(const SvgNode* pParent = getParent(); pParent && !bHasFound; pParent = pParent->getParent())
171 : {
172 : // dynamic_cast results Null-pointer for not SvgSvgNode and so skips them in if condition
173 0 : pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
174 0 : if (pParentSvgSvgNode)
175 : {
176 0 : if (pParentSvgSvgNode->getViewBox())
177 : {
178 : // viewbox values are already in 'user unit'.
179 0 : fWidth = pParentSvgSvgNode->getViewBox()->getWidth() * fPercentage;
180 0 : bHasFound = true;
181 : }
182 : else
183 : {
184 : // take absolute value or cummulate percentage
185 0 : if (pParentSvgSvgNode->getWidth().isSet())
186 : {
187 0 : if (Unit_percent == pParentSvgSvgNode->getWidth().getUnit())
188 : {
189 0 : fPercentage *= pParentSvgSvgNode->getWidth().getNumber() * 0.01;
190 : }
191 : else
192 : {
193 0 : fWidth = pParentSvgSvgNode->getWidth().solveNonPercentage(*pParentSvgSvgNode) * fPercentage;
194 0 : bHasFound = true;
195 : }
196 : } // not set => width=100% => factor 1, no need for else
197 : }
198 : }
199 : }
200 : }
201 :
202 0 : void SvgSvgNode::seekReferenceHeight(double& fHeight, bool& bHasFound) const
203 : {
204 0 : if (!getParent() || bHasFound)
205 : {
206 0 : return;
207 : }
208 0 : const SvgSvgNode* pParentSvgSvgNode = 0;
209 : // enclosing svg might have relative width and height, need to cumulate them till they are
210 : // resolved somewhere up in the node tree
211 0 : double fPercentage(1.0);
212 0 : for(const SvgNode* pParent = getParent(); pParent && !bHasFound; pParent = pParent->getParent())
213 : {
214 : // dynamic_cast results Null-pointer for not SvgSvgNode and so skips them in if condition
215 0 : pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
216 0 : if (pParentSvgSvgNode)
217 : {
218 0 : if (pParentSvgSvgNode->getViewBox())
219 : {
220 : // viewbox values are already in 'user unit'.
221 0 : fHeight = pParentSvgSvgNode->getViewBox()->getHeight() * fPercentage;
222 0 : bHasFound = true;
223 : }
224 : else
225 : {
226 : // take absolute value or cummulate percentage
227 0 : if (pParentSvgSvgNode->getHeight().isSet())
228 : {
229 0 : if (Unit_percent == pParentSvgSvgNode->getHeight().getUnit())
230 : {
231 0 : fPercentage *= pParentSvgSvgNode->getHeight().getNumber() * 0.01;
232 : }
233 : else
234 : {
235 0 : fHeight = pParentSvgSvgNode->getHeight().solveNonPercentage(*pParentSvgSvgNode) * fPercentage;
236 0 : bHasFound = true;
237 : }
238 : } // not set => height=100% => factor 1, no need for else
239 : }
240 : }
241 : }
242 : }
243 :
244 : // ToDo: Consider attribute overflow in method decomposeSvgNode
245 0 : void SvgSvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
246 : {
247 0 : drawinglayer::primitive2d::Primitive2DSequence aSequence;
248 :
249 : // decompose children
250 0 : SvgNode::decomposeSvgNode(aSequence, bReferenced);
251 :
252 0 : if(aSequence.hasElements())
253 : {
254 0 : if(getParent())
255 : {
256 : // #i122594# if width/height is not given, it's 100% (see 5.1.2 The 'svg' element in SVG1.1 spec).
257 : // If it is relative, the question is to what. The previous implementatin assumed relative to the
258 : // local ViewBox which is implied by (4.2 Basic data types):
259 :
260 : // "Note that the non-property <length> definition also allows a percentage unit identifier.
261 : // The meaning of a percentage length value depends on the attribute for which the percentage
262 : // length value has been specified. Two common cases are: (a) when a percentage length value
263 : // represents a percentage of the viewport width or height (refer to the section that discusses
264 : // units in general), and (b) when a percentage length value represents a percentage of the
265 : // bounding box width or height on a given object (refer to the section that describes object
266 : // bounding box units)."
267 :
268 : // Comparisons with commom browsers show, that it's mostly interpreted relative to the viewport
269 : // of the parent, and so does the new implementation.
270 :
271 : // Extract known viewport data
272 : // bXXXIsAbsolute tracks whether relative values could be resolved to absolute values
273 :
274 : // If width or height is not provided, the default 100% is used, see SVG 1.1 section 5.1.2
275 : // value 0.0 here is only to initialize variable
276 0 : bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
277 0 : double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
278 :
279 0 : bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
280 0 : double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
281 :
282 : // If x or y not provided, then default 0.0 is used, see SVG 1.1 Section 5.1.2
283 0 : bool bXIsAbsolute((getX().isSet() && Unit_percent != getX().getUnit()) || !getX().isSet());
284 0 : double fX( bXIsAbsolute && getX().isSet() ? getX().solveNonPercentage(*this) : 0.0);
285 :
286 0 : bool bYIsAbsolute((getY().isSet() && Unit_percent != getY().getUnit()) || !getY().isSet());
287 0 : double fY( bYIsAbsolute && getY().isSet() ? getY().solveNonPercentage(*this) : 0.0);
288 :
289 0 : if ( !bXIsAbsolute || !bWidthIsAbsolute)
290 : {
291 : // get width of enclosing svg and resolve percentage in x and width;
292 0 : double fWReference(0.0);
293 0 : bool bHasFoundWidth(false);
294 0 : seekReferenceWidth(fWReference, bHasFoundWidth);
295 0 : if (!bHasFoundWidth)
296 : {
297 : // Even outermost svg has not all information to resolve relative values,
298 : // I use content itself as fallback to set missing values for viewport
299 : // Any better idea for such ill structures svg documents?
300 : const basegfx::B2DRange aChildRange(
301 : drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
302 : aSequence,
303 0 : drawinglayer::geometry::ViewInformation2D()));
304 0 : fWReference = aChildRange.getWidth();
305 : }
306 : // referenced values are already in 'user unit'
307 0 : if (!bXIsAbsolute)
308 : {
309 0 : fX = getX().getNumber() * 0.01 * fWReference;
310 : }
311 0 : if (!bWidthIsAbsolute)
312 : {
313 0 : fW = (getWidth().isSet() ? getWidth().getNumber() *0.01 : 1.0) * fWReference;
314 : }
315 : }
316 :
317 0 : if ( !bYIsAbsolute || !bHeightIsAbsolute)
318 : {
319 : // get height of enclosing svg and resolve percentage in y and height
320 0 : double fHReference(0.0);
321 0 : bool bHasFoundHeight(false);
322 0 : seekReferenceHeight(fHReference, bHasFoundHeight);
323 0 : if (!bHasFoundHeight)
324 : {
325 : // Even outermost svg has not all information to resolve relative values,
326 : // I use content itself as fallback to set missing values for viewport
327 : // Any better idea for such ill structures svg documents?
328 : const basegfx::B2DRange aChildRange(
329 : drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
330 : aSequence,
331 0 : drawinglayer::geometry::ViewInformation2D()));
332 0 : fHReference = aChildRange.getHeight();
333 : }
334 :
335 : // referenced values are already in 'user unit'
336 0 : if (!bYIsAbsolute)
337 : {
338 0 : fY = getY().getNumber() * 0.01 * fHReference;
339 : }
340 0 : if (!bHeightIsAbsolute)
341 : {
342 0 : fH = (getHeight().isSet() ? getHeight().getNumber() *0.01 : 1.0) * fHReference;
343 : }
344 : }
345 :
346 0 : if(getViewBox())
347 : {
348 : // SVG 1.1 defines in section 7.7 that a negative value for width or height
349 : // in viewBox is an error and that 0.0 disables rendering
350 0 : if(basegfx::fTools::more(getViewBox()->getWidth(),0.0) && basegfx::fTools::more(getViewBox()->getHeight(),0.0))
351 : {
352 : // create target range homing x,y, width and height as calculated above
353 0 : const basegfx::B2DRange aTarget(fX, fY, fX + fW, fY + fH);
354 :
355 0 : if(aTarget.equal(*getViewBox()))
356 : {
357 : // no mapping needed, append
358 0 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSequence);
359 : }
360 : else
361 : {
362 : // create mapping
363 : // #i122610 SVG 1.1 defines in section 5.1.2 that if the attribute perserveAspectRatio is not specified,
364 : // then the effect is as if a value of 'xMidYMid meet' were specified.
365 0 : SvgAspectRatio aRatioDefault(Align_xMidYMid,false,true);
366 0 : const SvgAspectRatio& rRatio = getSvgAspectRatio().isSet()? getSvgAspectRatio() : aRatioDefault;
367 :
368 : // let mapping be created from SvgAspectRatio
369 : const basegfx::B2DHomMatrix aEmbeddingTransform(
370 0 : rRatio.createMapping(aTarget, *getViewBox()));
371 :
372 : // prepare embedding in transformation
373 : const drawinglayer::primitive2d::Primitive2DReference xRef(
374 : new drawinglayer::primitive2d::TransformPrimitive2D(
375 : aEmbeddingTransform,
376 0 : aSequence));
377 :
378 0 : if(rRatio.isMeetOrSlice())
379 : {
380 : // embed in transformation
381 0 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef);
382 : }
383 : else
384 : {
385 : // need to embed in MaskPrimitive2D, too
386 : const drawinglayer::primitive2d::Primitive2DReference xMask(
387 : new drawinglayer::primitive2d::MaskPrimitive2D(
388 : basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aTarget)),
389 0 : drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1)));
390 :
391 0 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMask);
392 0 : }
393 : }
394 : }
395 : }
396 : else // no viewBox attribute
397 : {
398 : // Svg defines that a negative value is an error and that 0.0 disables rendering
399 0 : if(basegfx::fTools::more(fW, 0.0) && basegfx::fTools::more(fH, 0.0))
400 : {
401 0 : if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
402 : {
403 : // embed in transform
404 : const drawinglayer::primitive2d::Primitive2DReference xRef(
405 : new drawinglayer::primitive2d::TransformPrimitive2D(
406 : basegfx::tools::createTranslateB2DHomMatrix(fX, fY),
407 0 : aSequence));
408 :
409 0 : aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
410 : }
411 :
412 : // embed in MaskPrimitive2D to clip
413 : const drawinglayer::primitive2d::Primitive2DReference xMask(
414 : new drawinglayer::primitive2d::MaskPrimitive2D(
415 : basegfx::B2DPolyPolygon(
416 : basegfx::tools::createPolygonFromRect(
417 : basegfx::B2DRange(fX, fY, fX + fW, fY + fH))),
418 0 : aSequence));
419 :
420 : // append
421 0 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMask);
422 : }
423 : }
424 : }
425 : else // Outermost SVG element
426 : {
427 0 : double fW = 0.0; // effective value depends on viewBox
428 0 : double fH = 0.0;
429 :
430 : // Svg defines that a negative value is an error and that 0.0 disables rendering
431 : // isPositive() not usable because it allows 0.0 in contrast to mathematical definition of 'positive'
432 0 : const bool bWidthInvalid(getWidth().isSet() && basegfx::fTools::lessOrEqual(getWidth().getNumber(), 0.0));
433 0 : const bool bHeightInvalid(getHeight().isSet() && basegfx::fTools::lessOrEqual(getHeight().getNumber(), 0.0));
434 0 : if(!bWidthInvalid && !bHeightInvalid)
435 : {
436 0 : basegfx::B2DRange aSvgCanvasRange; // effective value depends on viewBox
437 0 : if(getViewBox())
438 : {
439 : // SVG 1.1 defines in section 7.7 that a negative value for width or height
440 : // in viewBox is an error and that 0.0 disables rendering
441 0 : const double fViewBoxWidth = getViewBox()->getWidth();
442 0 : const double fViewBoxHeight = getViewBox()->getHeight();
443 0 : if(basegfx::fTools::more(fViewBoxWidth,0.0) && basegfx::fTools::more(fViewBoxHeight,0.0))
444 : {
445 : // The intrinsic aspect ratio of the svg element is given by absolute values of both width and height
446 : // or if one or both of them is relative by the width and height of the viewBox
447 : // see SVG 1.1 section 7.12
448 0 : const bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
449 0 : const bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
450 0 : if(bWidthIsAbsolute && bHeightIsAbsolute)
451 : {
452 0 : fW =getWidth().solveNonPercentage(*this);
453 0 : fH =getHeight().solveNonPercentage(*this);
454 : }
455 0 : else if (bWidthIsAbsolute)
456 : {
457 0 : fW = getWidth().solveNonPercentage(*this);
458 0 : fH = fW * fViewBoxWidth / fViewBoxHeight ;
459 : }
460 0 : else if (bHeightIsAbsolute)
461 : {
462 0 : fH = getHeight().solveNonPercentage(*this);
463 0 : fW = fH * fViewBoxWidth / fViewBoxHeight ;
464 : }
465 : else
466 : {
467 0 : fW = fViewBoxWidth;
468 0 : fH = fViewBoxHeight;
469 : }
470 : // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
471 0 : aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
472 :
473 : // create mapping
474 : // SVG 1.1 defines in section 5.1.2 that if the attribute perserveAspectRatio is not specified,
475 : // then the effect is as if a value of 'xMidYMid meet' were specified.
476 0 : SvgAspectRatio aRatioDefault(Align_xMidYMid,false,true);
477 0 : const SvgAspectRatio& rRatio = getSvgAspectRatio().isSet()? getSvgAspectRatio() : aRatioDefault;
478 :
479 0 : basegfx::B2DHomMatrix aViewBoxMapping;
480 0 : aViewBoxMapping = rRatio.createMapping(aSvgCanvasRange, *getViewBox());
481 : // no need to check ratio here for slice, the outermost Svg will
482 : // be clipped anyways (see below)
483 :
484 : // scale content to viewBox definitions
485 : const drawinglayer::primitive2d::Primitive2DReference xTransform(
486 : new drawinglayer::primitive2d::TransformPrimitive2D(
487 : aViewBoxMapping,
488 0 : aSequence));
489 :
490 0 : aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
491 : }
492 : }
493 : else // no viewbox
494 : {
495 : // There exists no parent to resolve relative width or height.
496 : // Use child size as fallback.
497 0 : const bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
498 0 : const bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
499 0 : if (bWidthIsAbsolute && bHeightIsAbsolute)
500 : {
501 0 : fW =getWidth().solveNonPercentage(*this);
502 0 : fH =getHeight().solveNonPercentage(*this);
503 :
504 : }
505 : else
506 : {
507 : const basegfx::B2DRange aChildRange(
508 : drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
509 : aSequence,
510 0 : drawinglayer::geometry::ViewInformation2D()));
511 0 : const double fChildWidth(aChildRange.getWidth());
512 0 : const double fChildHeight(aChildRange.getHeight());
513 0 : fW = bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : fChildWidth;
514 0 : fH = bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : fChildHeight;
515 : }
516 : // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
517 0 : aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
518 : }
519 :
520 : // to be completely correct in Svg sense it is necessary to clip
521 : // the whole content to the given canvas. I choose here to do this
522 : // initially despite I found various examples of Svg files out there
523 : // which have no correct values for this clipping. It's correct
524 : // due to the Svg spec.
525 0 : bool bDoCorrectCanvasClipping(true);
526 :
527 0 : if(bDoCorrectCanvasClipping)
528 : {
529 : // different from Svg we have the possibility with primitives to get
530 : // a correct bounding box for the geometry. Get it for evtl. taking action
531 : const basegfx::B2DRange aContentRange(
532 : drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
533 : aSequence,
534 0 : drawinglayer::geometry::ViewInformation2D()));
535 :
536 0 : if(aSvgCanvasRange.isInside(aContentRange))
537 : {
538 : // no clip needed, but an invisible HiddenGeometryPrimitive2D
539 : // to allow getting the full Svg range using the primitive mechanisms.
540 : // This is needed since e.g. an SdrObject using this as graphic will
541 : // create a mapping transformation to exactly map the content to it's
542 : // real life size
543 : const drawinglayer::primitive2d::Primitive2DReference xLine(
544 : new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
545 : basegfx::tools::createPolygonFromRect(
546 : aSvgCanvasRange),
547 0 : basegfx::BColor(0.0, 0.0, 0.0)));
548 : const drawinglayer::primitive2d::Primitive2DReference xHidden(
549 : new drawinglayer::primitive2d::HiddenGeometryPrimitive2D(
550 0 : drawinglayer::primitive2d::Primitive2DSequence(&xLine, 1)));
551 :
552 0 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aSequence, xHidden);
553 : }
554 0 : else if(aSvgCanvasRange.overlaps(aContentRange))
555 : {
556 : // Clip is necessary. This will make Svg images evtl. smaller
557 : // than wanted from Svg (the free space which may be around it is
558 : // conform to the Svg spec), but avoids an expensive and unnecessary
559 : // clip. Keep the full Svg range here to get the correct mappings
560 : // to objects using this. Optimizations can be done in the processors
561 : const drawinglayer::primitive2d::Primitive2DReference xMask(
562 : new drawinglayer::primitive2d::MaskPrimitive2D(
563 : basegfx::B2DPolyPolygon(
564 : basegfx::tools::createPolygonFromRect(
565 : aSvgCanvasRange)),
566 0 : aSequence));
567 :
568 0 : aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
569 : }
570 : else
571 : {
572 : // not inside, no overlap. Empty Svg
573 0 : aSequence.realloc(0);
574 : }
575 : }
576 :
577 0 : if(aSequence.hasElements())
578 : {
579 : // embed in transform primitive to scale to 1/100th mm
580 : // where 1 inch == 25.4 mm to get from Svg coordinates (px) to
581 : // drawinglayer coordinates
582 0 : const double fScaleTo100thmm(25.4 * 100.0 / F_SVG_PIXEL_PER_INCH);
583 : const basegfx::B2DHomMatrix aTransform(
584 : basegfx::tools::createScaleB2DHomMatrix(
585 : fScaleTo100thmm,
586 0 : fScaleTo100thmm));
587 :
588 : const drawinglayer::primitive2d::Primitive2DReference xTransform(
589 : new drawinglayer::primitive2d::TransformPrimitive2D(
590 : aTransform,
591 0 : aSequence));
592 :
593 0 : aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
594 :
595 : // append to result
596 0 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSequence);
597 : }
598 : }
599 : }
600 0 : }
601 0 : }
602 :
603 0 : const basegfx::B2DRange SvgSvgNode::getCurrentViewPort() const
604 : {
605 0 : if(getViewBox())
606 : {
607 0 : return *(getViewBox());
608 : }
609 : else // viewport should be given by x, y, width, and height
610 : {
611 : // Extract known viewport data
612 : // bXXXIsAbsolute tracks whether relative values could be resolved to absolute values
613 0 : if (getParent())
614 : {
615 : // If width or height is not provided, the default 100% is used, see SVG 1.1 section 5.1.2
616 : // value 0.0 here is only to initialize variable
617 0 : bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
618 0 : double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
619 0 : bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
620 0 : double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
621 :
622 : // If x or y not provided, then default 0.0 is used, see SVG 1.1 Section 5.1.2
623 0 : bool bXIsAbsolute((getX().isSet() && Unit_percent != getX().getUnit()) || !getX().isSet());
624 0 : double fX( bXIsAbsolute && getX().isSet() ? getX().solveNonPercentage(*this) : 0.0);
625 :
626 0 : bool bYIsAbsolute((getY().isSet() && Unit_percent != getY().getUnit()) || !getY().isSet());
627 0 : double fY( bYIsAbsolute && getY().isSet() ? getY().solveNonPercentage(*this) : 0.0);
628 :
629 0 : if (bXIsAbsolute && bYIsAbsolute && bWidthIsAbsolute && bHeightIsAbsolute)
630 : {
631 0 : return basegfx::B2DRange(fX, fY, fX+fW, fY+fH);
632 : }
633 : else // try to resolve relative values
634 : {
635 0 : if (!bXIsAbsolute || !bWidthIsAbsolute)
636 : {
637 : // get width of enclosing svg and resolve percentage in x and width
638 0 : double fWReference(0.0);
639 0 : bool bHasFoundWidth(false);
640 0 : seekReferenceWidth(fWReference, bHasFoundWidth);
641 : // referenced values are already in 'user unit'
642 0 : if (!bXIsAbsolute && bHasFoundWidth)
643 : {
644 0 : fX = getX().getNumber() * 0.01 * fWReference;
645 0 : bXIsAbsolute = true;
646 : }
647 0 : if (!bWidthIsAbsolute && bHasFoundWidth)
648 : {
649 0 : fW = (getWidth().isSet() ? getWidth().getNumber() *0.01 : 1.0) * fWReference;
650 0 : bWidthIsAbsolute = true;
651 : }
652 : }
653 0 : if (!bYIsAbsolute || !bHeightIsAbsolute)
654 : {
655 : // get height of enclosing svg and resolve percentage in y and height
656 0 : double fHReference(0.0);
657 0 : bool bHasFoundHeight(false);
658 0 : seekReferenceHeight(fHReference, bHasFoundHeight);
659 : // referenced values are already in 'user unit'
660 0 : if (!bYIsAbsolute && bHasFoundHeight)
661 : {
662 0 : fY = getY().getNumber() * 0.01 * fHReference;
663 0 : bYIsAbsolute = true;
664 : }
665 0 : if (!bHeightIsAbsolute && bHasFoundHeight)
666 : {
667 0 : fH = (getHeight().isSet() ? getHeight().getNumber() *0.01 : 1.0) * fHReference;
668 0 : bHeightIsAbsolute = true;
669 : }
670 : }
671 :
672 0 : if (bXIsAbsolute && bYIsAbsolute && bWidthIsAbsolute && bHeightIsAbsolute)
673 : {
674 0 : return basegfx::B2DRange(fX, fY, fX+fW, fY+fH);
675 : }
676 : else // relative values could not be resolved, there exists no fallback
677 : {
678 0 : return SvgNode::getCurrentViewPort();
679 : }
680 : }
681 : }
682 : else //outermost svg
683 : {
684 : // If width or height is not provided, the default would be 100%, see SVG 1.1 section 5.1.2
685 : // But here it cannot be resolved and no fallback exists.
686 : // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
687 0 : bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
688 0 : double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
689 0 : bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
690 0 : double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
691 0 : if (bWidthIsAbsolute && bHeightIsAbsolute)
692 : {
693 0 : return basegfx::B2DRange(0.0, 0.0, fW, fH);
694 : }
695 : else // no fallback exists
696 : {
697 0 : return SvgNode::getCurrentViewPort();
698 : }
699 : }
700 : // ToDo: Is it possible to decompose and use the bounding box of the children, if even the
701 : // outermost svg has no information to resolve percentage? Is it worth, how expensive is it?
702 :
703 : }
704 : }
705 :
706 : } // end of namespace svgreader
707 : } // end of namespace svgio
708 :
709 :
710 : // eof
711 :
712 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|