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