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