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