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