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