1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_svgio.hxx" 24 25 #include <svgio/svgreader/svgsvgnode.hxx> 26 #include <drawinglayer/geometry/viewinformation2d.hxx> 27 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 28 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 29 #include <basegfx/polygon/b2dpolygontools.hxx> 30 #include <basegfx/polygon/b2dpolygon.hxx> 31 #include <basegfx/matrix/b2dhommatrixtools.hxx> 32 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 33 #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx> 34 35 ////////////////////////////////////////////////////////////////////////////// 36 37 namespace svgio 38 { 39 namespace svgreader 40 { SvgSvgNode(SvgDocument & rDocument,SvgNode * pParent)41 SvgSvgNode::SvgSvgNode( 42 SvgDocument& rDocument, 43 SvgNode* pParent) 44 : SvgNode(SVGTokenSvg, rDocument, pParent), 45 maSvgStyleAttributes(*this), 46 mpViewBox(0), 47 maSvgAspectRatio(), 48 maX(), 49 maY(), 50 maWidth(), 51 maHeight(), 52 maVersion(), 53 mbStyleAttributesInitialized(false) // #125258# 54 { 55 } 56 57 // #125258# initializeStyleAttributes()58 void SvgSvgNode::initializeStyleAttributes() 59 { 60 if(!mbStyleAttributesInitialized) 61 { 62 // #125258# determine if initial values need to be initialized with hard values 63 // for the case that this is the outmost SVG statement and it has no parent 64 // stale (CssStyle for svg may be defined) 65 bool bSetInitialValues(true); 66 67 if(getParent()) 68 { 69 // #125258# no initial values when it's a SVG element embedded in SVG 70 bSetInitialValues = false; 71 } 72 73 if(bSetInitialValues) 74 { 75 const SvgStyleAttributes* pStyles = getSvgStyleAttributes(); 76 77 if(pStyles && pStyles->getParentStyle()) 78 { 79 // #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 ~SvgSvgNode()99 SvgSvgNode::~SvgSvgNode() 100 { 101 if(mpViewBox) delete mpViewBox; 102 } 103 getSvgStyleAttributes() const104 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 parseAttribute(const rtl::OUString & rTokenName,SVGToken aSVGToken,const rtl::OUString & aContent)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 seekReferenceWidth(double & fWidth,bool & bHasFound) const206 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 seekReferenceHeight(double & fHeight,bool & bHasFound) const248 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 decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence & rTarget,bool bReferenced) const291 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 unneccessary 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 getCurrentViewPort() const654 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