Updated core
[LibreOffice.git] / svgio / source / svgreader / svgsvgnode.cxx
blobdcc0bf9ee82369def403ab3a110046ec0fca6ea2
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 <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>
30 namespace svgio
32 namespace svgreader
34 SvgSvgNode::SvgSvgNode(
35 SvgDocument& rDocument,
36 SvgNode* pParent)
37 : SvgNode(SVGTokenSvg, rDocument, pParent),
38 maSvgStyleAttributes(*this),
39 mpViewBox(0),
40 maSvgAspectRatio(),
41 maX(),
42 maY(),
43 maWidth(),
44 maHeight(),
45 maVersion(),
46 mbStyleAttributesInitialized(false) // #i125258#
50 // #i125258#
51 void SvgSvgNode::initializeStyleAttributes()
53 if(!mbStyleAttributesInitialized)
55 // #i125258# determine if initial values need to be initialized with hard values
56 // for the case that this is the outmost SVG statement and it has no parent
57 // stale (CssStyle for svg may be defined)
58 bool bSetInitialValues(true);
60 if(getParent())
62 // #i125258# no initial values when it's a SVG element embedded in SVG
63 bSetInitialValues = false;
66 if(bSetInitialValues)
68 const SvgStyleAttributes* pStyles = getSvgStyleAttributes();
70 if(pStyles && pStyles->getParentStyle())
72 // SVG has a parent style (probably CssStyle), check if fill is set there anywhere
73 // already. If yes, do not set the default fill (black)
74 bool bFillSet(false);
75 const SvgStyleAttributes* pParentStyle = pStyles->getParentStyle();
77 while(pParentStyle && !bFillSet)
79 bFillSet = pParentStyle->isFillSet();
80 pParentStyle = pParentStyle->getParentStyle();
83 if(bFillSet)
85 // #125258# no initial values when SVG has a parent style at which a fill
86 // is already set
87 bSetInitialValues = false;
92 if(bSetInitialValues)
94 // #i125258# only set if not yet initialized (SvgSvgNode::parseAttribute is already done,
95 // just setting may revert an already set valid value)
96 if(!maSvgStyleAttributes.isFillSet())
98 // #i125258# initial fill is black (see SVG1.1 spec)
99 maSvgStyleAttributes.setFill(SvgPaint(basegfx::BColor(0.0, 0.0, 0.0), true, true));
103 mbStyleAttributesInitialized = true;
107 SvgSvgNode::~SvgSvgNode()
109 if(mpViewBox) delete mpViewBox;
112 const SvgStyleAttributes* SvgSvgNode::getSvgStyleAttributes() const
114 // #i125258# svg node can have CssStyles, too, so check for it here
115 return checkForCssStyle(OUString("svg"), maSvgStyleAttributes);
118 void SvgSvgNode::parseAttribute(const OUString& rTokenName, SVGToken aSVGToken, const OUString& aContent)
120 // call parent
121 SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
123 // read style attributes
124 maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent, false);
126 // parse own
127 switch(aSVGToken)
129 case SVGTokenStyle:
131 readLocalCssStyle(aContent);
132 break;
134 case SVGTokenViewBox:
136 const basegfx::B2DRange aRange(readViewBox(aContent, *this));
138 if(!aRange.isEmpty())
140 setViewBox(&aRange);
142 break;
144 case SVGTokenPreserveAspectRatio:
146 setSvgAspectRatio(readSvgAspectRatio(aContent));
147 break;
149 case SVGTokenX:
151 SvgNumber aNum;
153 if(readSingleNumber(aContent, aNum))
155 setX(aNum);
157 break;
159 case SVGTokenY:
161 SvgNumber aNum;
163 if(readSingleNumber(aContent, aNum))
165 setY(aNum);
167 break;
169 case SVGTokenWidth:
171 SvgNumber aNum;
173 if(readSingleNumber(aContent, aNum))
175 if(aNum.isPositive())
177 setWidth(aNum);
180 break;
182 case SVGTokenHeight:
184 SvgNumber aNum;
186 if(readSingleNumber(aContent, aNum))
188 if(aNum.isPositive())
190 setHeight(aNum);
193 break;
195 case SVGTokenVersion:
197 SvgNumber aNum;
199 if(readSingleNumber(aContent, aNum))
201 setVersion(aNum);
203 break;
205 default:
207 break;
212 void SvgSvgNode::seekReferenceWidth(double& fWidth, bool& bHasFound) const
214 if (!getParent() || bHasFound)
216 return;
218 const SvgSvgNode* pParentSvgSvgNode = 0;
219 // enclosing svg might have relative width, need to cumulate them till they are
220 // resolved somewhere up in the node tree
221 double fPercentage(1.0);
222 for(const SvgNode* pParent = getParent(); pParent && !bHasFound; pParent = pParent->getParent())
224 // dynamic_cast results Null-pointer for not SvgSvgNode and so skips them in if condition
225 pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
226 if (pParentSvgSvgNode)
228 if (pParentSvgSvgNode->getViewBox())
230 // viewbox values are already in 'user unit'.
231 fWidth = pParentSvgSvgNode->getViewBox()->getWidth() * fPercentage;
232 bHasFound = true;
234 else
236 // take absolute value or cummulate percentage
237 if (pParentSvgSvgNode->getWidth().isSet())
239 if (Unit_percent == pParentSvgSvgNode->getWidth().getUnit())
241 fPercentage *= pParentSvgSvgNode->getWidth().getNumber() * 0.01;
243 else
245 fWidth = pParentSvgSvgNode->getWidth().solveNonPercentage(*pParentSvgSvgNode) * fPercentage;
246 bHasFound = true;
248 } // not set => width=100% => factor 1, no need for else
254 void SvgSvgNode::seekReferenceHeight(double& fHeight, bool& bHasFound) const
256 if (!getParent() || bHasFound)
258 return;
260 const SvgSvgNode* pParentSvgSvgNode = 0;
261 // enclosing svg might have relative width and height, need to cumulate them till they are
262 // resolved somewhere up in the node tree
263 double fPercentage(1.0);
264 for(const SvgNode* pParent = getParent(); pParent && !bHasFound; pParent = pParent->getParent())
266 // dynamic_cast results Null-pointer for not SvgSvgNode and so skips them in if condition
267 pParentSvgSvgNode = dynamic_cast< const SvgSvgNode* >(pParent);
268 if (pParentSvgSvgNode)
270 if (pParentSvgSvgNode->getViewBox())
272 // viewbox values are already in 'user unit'.
273 fHeight = pParentSvgSvgNode->getViewBox()->getHeight() * fPercentage;
274 bHasFound = true;
276 else
278 // take absolute value or cummulate percentage
279 if (pParentSvgSvgNode->getHeight().isSet())
281 if (Unit_percent == pParentSvgSvgNode->getHeight().getUnit())
283 fPercentage *= pParentSvgSvgNode->getHeight().getNumber() * 0.01;
285 else
287 fHeight = pParentSvgSvgNode->getHeight().solveNonPercentage(*pParentSvgSvgNode) * fPercentage;
288 bHasFound = true;
290 } // not set => height=100% => factor 1, no need for else
296 // ToDo: Consider attribute overflow in method decomposeSvgNode
297 void SvgSvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
299 drawinglayer::primitive2d::Primitive2DSequence aSequence;
301 // #i125258# check now if we need to init some style settings locally. Do not do this
302 // in the constructor, there is not yet informatikon e.g. about existing CssStyles.
303 // Here all nodes are read and interpreted
304 const_cast< SvgSvgNode* >(this)->initializeStyleAttributes();
306 // decompose children
307 SvgNode::decomposeSvgNode(aSequence, bReferenced);
309 if(aSequence.hasElements())
311 if(getParent())
313 // #i122594# if width/height is not given, it's 100% (see 5.1.2 The 'svg' element in SVG1.1 spec).
314 // If it is relative, the question is to what. The previous implementatin assumed relative to the
315 // local ViewBox which is implied by (4.2 Basic data types):
317 // "Note that the non-property <length> definition also allows a percentage unit identifier.
318 // The meaning of a percentage length value depends on the attribute for which the percentage
319 // length value has been specified. Two common cases are: (a) when a percentage length value
320 // represents a percentage of the viewport width or height (refer to the section that discusses
321 // units in general), and (b) when a percentage length value represents a percentage of the
322 // bounding box width or height on a given object (refer to the section that describes object
323 // bounding box units)."
325 // Comparisons with commom browsers show, that it's mostly interpreted relative to the viewport
326 // of the parent, and so does the new implementation.
328 // Extract known viewport data
329 // bXXXIsAbsolute tracks whether relative values could be resolved to absolute values
331 // If width or height is not provided, the default 100% is used, see SVG 1.1 section 5.1.2
332 // value 0.0 here is only to initialize variable
333 bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
334 double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
336 bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
337 double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
339 // If x or y not provided, then default 0.0 is used, see SVG 1.1 Section 5.1.2
340 bool bXIsAbsolute((getX().isSet() && Unit_percent != getX().getUnit()) || !getX().isSet());
341 double fX( bXIsAbsolute && getX().isSet() ? getX().solveNonPercentage(*this) : 0.0);
343 bool bYIsAbsolute((getY().isSet() && Unit_percent != getY().getUnit()) || !getY().isSet());
344 double fY( bYIsAbsolute && getY().isSet() ? getY().solveNonPercentage(*this) : 0.0);
346 if ( !bXIsAbsolute || !bWidthIsAbsolute)
348 // get width of enclosing svg and resolve percentage in x and width;
349 double fWReference(0.0);
350 bool bHasFoundWidth(false);
351 seekReferenceWidth(fWReference, bHasFoundWidth);
352 if (!bHasFoundWidth)
354 // Even outermost svg has not all information to resolve relative values,
355 // I use content itself as fallback to set missing values for viewport
356 // Any better idea for such ill structures svg documents?
357 const basegfx::B2DRange aChildRange(
358 drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
359 aSequence,
360 drawinglayer::geometry::ViewInformation2D()));
361 fWReference = aChildRange.getWidth();
363 // referenced values are already in 'user unit'
364 if (!bXIsAbsolute)
366 fX = getX().getNumber() * 0.01 * fWReference;
368 if (!bWidthIsAbsolute)
370 fW = (getWidth().isSet() ? getWidth().getNumber() *0.01 : 1.0) * fWReference;
374 if ( !bYIsAbsolute || !bHeightIsAbsolute)
376 // get height of enclosing svg and resolve percentage in y and height
377 double fHReference(0.0);
378 bool bHasFoundHeight(false);
379 seekReferenceHeight(fHReference, bHasFoundHeight);
380 if (!bHasFoundHeight)
382 // Even outermost svg has not all information to resolve relative values,
383 // I use content itself as fallback to set missing values for viewport
384 // Any better idea for such ill structures svg documents?
385 const basegfx::B2DRange aChildRange(
386 drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
387 aSequence,
388 drawinglayer::geometry::ViewInformation2D()));
389 fHReference = aChildRange.getHeight();
392 // referenced values are already in 'user unit'
393 if (!bYIsAbsolute)
395 fY = getY().getNumber() * 0.01 * fHReference;
397 if (!bHeightIsAbsolute)
399 fH = (getHeight().isSet() ? getHeight().getNumber() *0.01 : 1.0) * fHReference;
403 if(getViewBox())
405 // SVG 1.1 defines in section 7.7 that a negative value for width or height
406 // in viewBox is an error and that 0.0 disables rendering
407 if(basegfx::fTools::more(getViewBox()->getWidth(),0.0) && basegfx::fTools::more(getViewBox()->getHeight(),0.0))
409 // create target range homing x,y, width and height as calculated above
410 const basegfx::B2DRange aTarget(fX, fY, fX + fW, fY + fH);
412 if(aTarget.equal(*getViewBox()))
414 // no mapping needed, append
415 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSequence);
417 else
419 // create mapping
420 // #i122610 SVG 1.1 defines in section 5.1.2 that if the attribute perserveAspectRatio is not specified,
421 // then the effect is as if a value of 'xMidYMid meet' were specified.
422 SvgAspectRatio aRatioDefault(Align_xMidYMid,false,true);
423 const SvgAspectRatio& rRatio = getSvgAspectRatio().isSet()? getSvgAspectRatio() : aRatioDefault;
425 // let mapping be created from SvgAspectRatio
426 const basegfx::B2DHomMatrix aEmbeddingTransform(
427 rRatio.createMapping(aTarget, *getViewBox()));
429 // prepare embedding in transformation
430 const drawinglayer::primitive2d::Primitive2DReference xRef(
431 new drawinglayer::primitive2d::TransformPrimitive2D(
432 aEmbeddingTransform,
433 aSequence));
435 if(rRatio.isMeetOrSlice())
437 // embed in transformation
438 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef);
440 else
442 // need to embed in MaskPrimitive2D, too
443 const drawinglayer::primitive2d::Primitive2DReference xMask(
444 new drawinglayer::primitive2d::MaskPrimitive2D(
445 basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aTarget)),
446 drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1)));
448 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMask);
453 else // no viewBox attribute
455 // Svg defines that a negative value is an error and that 0.0 disables rendering
456 if(basegfx::fTools::more(fW, 0.0) && basegfx::fTools::more(fH, 0.0))
458 if(!basegfx::fTools::equalZero(fX) || !basegfx::fTools::equalZero(fY))
460 // embed in transform
461 const drawinglayer::primitive2d::Primitive2DReference xRef(
462 new drawinglayer::primitive2d::TransformPrimitive2D(
463 basegfx::tools::createTranslateB2DHomMatrix(fX, fY),
464 aSequence));
466 aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
469 // embed in MaskPrimitive2D to clip
470 const drawinglayer::primitive2d::Primitive2DReference xMask(
471 new drawinglayer::primitive2d::MaskPrimitive2D(
472 basegfx::B2DPolyPolygon(
473 basegfx::tools::createPolygonFromRect(
474 basegfx::B2DRange(fX, fY, fX + fW, fY + fH))),
475 aSequence));
477 // append
478 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMask);
482 else // Outermost SVG element
484 double fW = 0.0; // effective value depends on viewBox
485 double fH = 0.0;
487 // Svg defines that a negative value is an error and that 0.0 disables rendering
488 // isPositive() not usable because it allows 0.0 in contrast to mathematical definition of 'positive'
489 const bool bWidthInvalid(getWidth().isSet() && basegfx::fTools::lessOrEqual(getWidth().getNumber(), 0.0));
490 const bool bHeightInvalid(getHeight().isSet() && basegfx::fTools::lessOrEqual(getHeight().getNumber(), 0.0));
491 if(!bWidthInvalid && !bHeightInvalid)
493 basegfx::B2DRange aSvgCanvasRange; // effective value depends on viewBox
494 if(getViewBox())
496 // SVG 1.1 defines in section 7.7 that a negative value for width or height
497 // in viewBox is an error and that 0.0 disables rendering
498 const double fViewBoxWidth = getViewBox()->getWidth();
499 const double fViewBoxHeight = getViewBox()->getHeight();
500 if(basegfx::fTools::more(fViewBoxWidth,0.0) && basegfx::fTools::more(fViewBoxHeight,0.0))
502 // The intrinsic aspect ratio of the svg element is given by absolute values of both width and height
503 // or if one or both of them is relative by the width and height of the viewBox
504 // see SVG 1.1 section 7.12
505 const bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
506 const bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
507 if(bWidthIsAbsolute && bHeightIsAbsolute)
509 fW =getWidth().solveNonPercentage(*this);
510 fH =getHeight().solveNonPercentage(*this);
512 else if (bWidthIsAbsolute)
514 fW = getWidth().solveNonPercentage(*this);
515 fH = fW * fViewBoxWidth / fViewBoxHeight ;
517 else if (bHeightIsAbsolute)
519 fH = getHeight().solveNonPercentage(*this);
520 fW = fH * fViewBoxWidth / fViewBoxHeight ;
522 else
524 fW = fViewBoxWidth;
525 fH = fViewBoxHeight;
527 // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
528 aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
530 // create mapping
531 // SVG 1.1 defines in section 5.1.2 that if the attribute perserveAspectRatio is not specified,
532 // then the effect is as if a value of 'xMidYMid meet' were specified.
533 SvgAspectRatio aRatioDefault(Align_xMidYMid,false,true);
534 const SvgAspectRatio& rRatio = getSvgAspectRatio().isSet()? getSvgAspectRatio() : aRatioDefault;
536 basegfx::B2DHomMatrix aViewBoxMapping;
537 aViewBoxMapping = rRatio.createMapping(aSvgCanvasRange, *getViewBox());
538 // no need to check ratio here for slice, the outermost Svg will
539 // be clipped anyways (see below)
541 // scale content to viewBox definitions
542 const drawinglayer::primitive2d::Primitive2DReference xTransform(
543 new drawinglayer::primitive2d::TransformPrimitive2D(
544 aViewBoxMapping,
545 aSequence));
547 aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
550 else // no viewbox
552 // There exists no parent to resolve relative width or height.
553 // Use child size as fallback.
554 const bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
555 const bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
556 if (bWidthIsAbsolute && bHeightIsAbsolute)
558 fW =getWidth().solveNonPercentage(*this);
559 fH =getHeight().solveNonPercentage(*this);
562 else
564 const basegfx::B2DRange aChildRange(
565 drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
566 aSequence,
567 drawinglayer::geometry::ViewInformation2D()));
568 const double fChildWidth(aChildRange.getWidth());
569 const double fChildHeight(aChildRange.getHeight());
570 fW = bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : fChildWidth;
571 fH = bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : fChildHeight;
573 // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
574 aSvgCanvasRange = basegfx::B2DRange(0.0, 0.0, fW, fH);
577 // to be completely correct in Svg sense it is necessary to clip
578 // the whole content to the given canvas. I choose here to do this
579 // initially despite I found various examples of Svg files out there
580 // which have no correct values for this clipping. It's correct
581 // due to the Svg spec.
582 bool bDoCorrectCanvasClipping(true);
584 if(bDoCorrectCanvasClipping)
586 // different from Svg we have the possibility with primitives to get
587 // a correct bounding box for the geometry. Get it for evtl. taking action
588 const basegfx::B2DRange aContentRange(
589 drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(
590 aSequence,
591 drawinglayer::geometry::ViewInformation2D()));
593 if(aSvgCanvasRange.isInside(aContentRange))
595 // no clip needed, but an invisible HiddenGeometryPrimitive2D
596 // to allow getting the full Svg range using the primitive mechanisms.
597 // This is needed since e.g. an SdrObject using this as graphic will
598 // create a mapping transformation to exactly map the content to it's
599 // real life size
600 const drawinglayer::primitive2d::Primitive2DReference xLine(
601 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
602 basegfx::tools::createPolygonFromRect(
603 aSvgCanvasRange),
604 basegfx::BColor(0.0, 0.0, 0.0)));
605 const drawinglayer::primitive2d::Primitive2DReference xHidden(
606 new drawinglayer::primitive2d::HiddenGeometryPrimitive2D(
607 drawinglayer::primitive2d::Primitive2DSequence(&xLine, 1)));
609 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aSequence, xHidden);
611 else if(aSvgCanvasRange.overlaps(aContentRange))
613 // Clip is necessary. This will make Svg images evtl. smaller
614 // than wanted from Svg (the free space which may be around it is
615 // conform to the Svg spec), but avoids an expensive and unnecessary
616 // clip. Keep the full Svg range here to get the correct mappings
617 // to objects using this. Optimizations can be done in the processors
618 const drawinglayer::primitive2d::Primitive2DReference xMask(
619 new drawinglayer::primitive2d::MaskPrimitive2D(
620 basegfx::B2DPolyPolygon(
621 basegfx::tools::createPolygonFromRect(
622 aSvgCanvasRange)),
623 aSequence));
625 aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
627 else
629 // not inside, no overlap. Empty Svg
630 aSequence.realloc(0);
634 if(aSequence.hasElements())
636 // embed in transform primitive to scale to 1/100th mm
637 // where 1 inch == 25.4 mm to get from Svg coordinates (px) to
638 // drawinglayer coordinates
639 const double fScaleTo100thmm(25.4 * 100.0 / F_SVG_PIXEL_PER_INCH);
640 const basegfx::B2DHomMatrix aTransform(
641 basegfx::tools::createScaleB2DHomMatrix(
642 fScaleTo100thmm,
643 fScaleTo100thmm));
645 const drawinglayer::primitive2d::Primitive2DReference xTransform(
646 new drawinglayer::primitive2d::TransformPrimitive2D(
647 aTransform,
648 aSequence));
650 aSequence = drawinglayer::primitive2d::Primitive2DSequence(&xTransform, 1);
652 // append to result
653 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSequence);
660 const basegfx::B2DRange SvgSvgNode::getCurrentViewPort() const
662 if(getViewBox())
664 return *(getViewBox());
666 else // viewport should be given by x, y, width, and height
668 // Extract known viewport data
669 // bXXXIsAbsolute tracks whether relative values could be resolved to absolute values
670 if (getParent())
672 // If width or height is not provided, the default 100% is used, see SVG 1.1 section 5.1.2
673 // value 0.0 here is only to initialize variable
674 bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
675 double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
676 bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
677 double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
679 // If x or y not provided, then default 0.0 is used, see SVG 1.1 Section 5.1.2
680 bool bXIsAbsolute((getX().isSet() && Unit_percent != getX().getUnit()) || !getX().isSet());
681 double fX( bXIsAbsolute && getX().isSet() ? getX().solveNonPercentage(*this) : 0.0);
683 bool bYIsAbsolute((getY().isSet() && Unit_percent != getY().getUnit()) || !getY().isSet());
684 double fY( bYIsAbsolute && getY().isSet() ? getY().solveNonPercentage(*this) : 0.0);
686 if (bXIsAbsolute && bYIsAbsolute && bWidthIsAbsolute && bHeightIsAbsolute)
688 return basegfx::B2DRange(fX, fY, fX+fW, fY+fH);
690 else // try to resolve relative values
692 if (!bXIsAbsolute || !bWidthIsAbsolute)
694 // get width of enclosing svg and resolve percentage in x and width
695 double fWReference(0.0);
696 bool bHasFoundWidth(false);
697 seekReferenceWidth(fWReference, bHasFoundWidth);
698 // referenced values are already in 'user unit'
699 if (!bXIsAbsolute && bHasFoundWidth)
701 fX = getX().getNumber() * 0.01 * fWReference;
702 bXIsAbsolute = true;
704 if (!bWidthIsAbsolute && bHasFoundWidth)
706 fW = (getWidth().isSet() ? getWidth().getNumber() *0.01 : 1.0) * fWReference;
707 bWidthIsAbsolute = true;
710 if (!bYIsAbsolute || !bHeightIsAbsolute)
712 // get height of enclosing svg and resolve percentage in y and height
713 double fHReference(0.0);
714 bool bHasFoundHeight(false);
715 seekReferenceHeight(fHReference, bHasFoundHeight);
716 // referenced values are already in 'user unit'
717 if (!bYIsAbsolute && bHasFoundHeight)
719 fY = getY().getNumber() * 0.01 * fHReference;
720 bYIsAbsolute = true;
722 if (!bHeightIsAbsolute && bHasFoundHeight)
724 fH = (getHeight().isSet() ? getHeight().getNumber() *0.01 : 1.0) * fHReference;
725 bHeightIsAbsolute = true;
729 if (bXIsAbsolute && bYIsAbsolute && bWidthIsAbsolute && bHeightIsAbsolute)
731 return basegfx::B2DRange(fX, fY, fX+fW, fY+fH);
733 else // relative values could not be resolved, there exists no fallback
735 return SvgNode::getCurrentViewPort();
739 else //outermost svg
741 // If width or height is not provided, the default would be 100%, see SVG 1.1 section 5.1.2
742 // But here it cannot be resolved and no fallback exists.
743 // SVG 1.1 defines in section 5.1.2 that x,y has no meanig for the outermost SVG element.
744 bool bWidthIsAbsolute(getWidth().isSet() && Unit_percent != getWidth().getUnit());
745 double fW( bWidthIsAbsolute ? getWidth().solveNonPercentage(*this) : 0.0);
746 bool bHeightIsAbsolute(getHeight().isSet() && Unit_percent != getHeight().getUnit());
747 double fH( bHeightIsAbsolute ? getHeight().solveNonPercentage(*this) : 0.0);
748 if (bWidthIsAbsolute && bHeightIsAbsolute)
750 return basegfx::B2DRange(0.0, 0.0, fW, fH);
752 else // no fallback exists
754 return SvgNode::getCurrentViewPort();
757 // ToDo: Is it possible to decompose and use the bounding box of the children, if even the
758 // outermost svg has no information to resolve percentage? Is it worth, how expensive is it?
763 } // end of namespace svgreader
764 } // end of namespace svgio
766 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */