LCOV - code coverage report
Current view: top level - svgio/source/svgreader - svgsvgnode.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 104 256 40.6 %
Date: 2014-11-03 Functions: 7 10 70.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10