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 <sal/config.h> 21 22 #include <algorithm> 23 24 #include <svgstyleattributes.hxx> 25 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 26 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> 27 #include <svgnode.hxx> 28 #include <svgdocument.hxx> 29 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> 30 #include <svggradientnode.hxx> 31 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 32 #include <basegfx/vector/b2enums.hxx> 33 #include <drawinglayer/processor2d/linegeometryextractor2d.hxx> 34 #include <drawinglayer/processor2d/textaspolygonextractor2d.hxx> 35 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 36 #include <svgclippathnode.hxx> 37 #include <svgmasknode.hxx> 38 #include <basegfx/polygon/b2dpolypolygontools.hxx> 39 #include <svgmarkernode.hxx> 40 #include <basegfx/curve/b2dcubicbezier.hxx> 41 #include <svgpatternnode.hxx> 42 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx> 43 #include <basegfx/polygon/b2dpolygontools.hxx> 44 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 45 #include <drawinglayer/primitive2d/pagehierarchyprimitive2d.hxx> 46 47 const int nStyleDepthLimit = 1024; 48 49 namespace svgio 50 { 51 namespace svgreader 52 { 53 static basegfx::B2DLineJoin StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin) 54 { 55 if(StrokeLinejoin_round == aStrokeLinejoin) 56 { 57 return basegfx::B2DLineJoin::Round; 58 } 59 else if(StrokeLinejoin_bevel == aStrokeLinejoin) 60 { 61 return basegfx::B2DLineJoin::Bevel; 62 } 63 64 return basegfx::B2DLineJoin::Miter; 65 } 66 67 static css::drawing::LineCap StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap) 68 { 69 switch(aStrokeLinecap) 70 { 71 default: /* StrokeLinecap_notset, StrokeLinecap_butt */ 72 { 73 return css::drawing::LineCap_BUTT; 74 } 75 case StrokeLinecap_round: 76 { 77 return css::drawing::LineCap_ROUND; 78 } 79 case StrokeLinecap_square: 80 { 81 return css::drawing::LineCap_SQUARE; 82 } 83 } 84 } 85 86 FontStretch getWider(FontStretch aSource) 87 { 88 switch(aSource) 89 { 90 case FontStretch_ultra_condensed: aSource = FontStretch_extra_condensed; break; 91 case FontStretch_extra_condensed: aSource = FontStretch_condensed; break; 92 case FontStretch_condensed: aSource = FontStretch_semi_condensed; break; 93 case FontStretch_semi_condensed: aSource = FontStretch_normal; break; 94 case FontStretch_normal: aSource = FontStretch_semi_expanded; break; 95 case FontStretch_semi_expanded: aSource = FontStretch_expanded; break; 96 case FontStretch_expanded: aSource = FontStretch_extra_expanded; break; 97 case FontStretch_extra_expanded: aSource = FontStretch_ultra_expanded; break; 98 default: break; 99 } 100 101 return aSource; 102 } 103 104 FontStretch getNarrower(FontStretch aSource) 105 { 106 switch(aSource) 107 { 108 case FontStretch_extra_condensed: aSource = FontStretch_ultra_condensed; break; 109 case FontStretch_condensed: aSource = FontStretch_extra_condensed; break; 110 case FontStretch_semi_condensed: aSource = FontStretch_condensed; break; 111 case FontStretch_normal: aSource = FontStretch_semi_condensed; break; 112 case FontStretch_semi_expanded: aSource = FontStretch_normal; break; 113 case FontStretch_expanded: aSource = FontStretch_semi_expanded; break; 114 case FontStretch_extra_expanded: aSource = FontStretch_expanded; break; 115 case FontStretch_ultra_expanded: aSource = FontStretch_extra_expanded; break; 116 default: break; 117 } 118 119 return aSource; 120 } 121 122 FontWeight getBolder(FontWeight aSource) 123 { 124 switch(aSource) 125 { 126 case FontWeight_100: aSource = FontWeight_200; break; 127 case FontWeight_200: aSource = FontWeight_300; break; 128 case FontWeight_300: aSource = FontWeight_400; break; 129 case FontWeight_400: aSource = FontWeight_500; break; 130 case FontWeight_500: aSource = FontWeight_600; break; 131 case FontWeight_600: aSource = FontWeight_700; break; 132 case FontWeight_700: aSource = FontWeight_800; break; 133 case FontWeight_800: aSource = FontWeight_900; break; 134 default: break; 135 } 136 137 return aSource; 138 } 139 140 FontWeight getLighter(FontWeight aSource) 141 { 142 switch(aSource) 143 { 144 case FontWeight_200: aSource = FontWeight_100; break; 145 case FontWeight_300: aSource = FontWeight_200; break; 146 case FontWeight_400: aSource = FontWeight_300; break; 147 case FontWeight_500: aSource = FontWeight_400; break; 148 case FontWeight_600: aSource = FontWeight_500; break; 149 case FontWeight_700: aSource = FontWeight_600; break; 150 case FontWeight_800: aSource = FontWeight_700; break; 151 case FontWeight_900: aSource = FontWeight_800; break; 152 default: break; 153 } 154 155 return aSource; 156 } 157 158 ::FontWeight getVclFontWeight(FontWeight aSource) 159 { 160 ::FontWeight nRetval(WEIGHT_NORMAL); 161 162 switch(aSource) 163 { 164 case FontWeight_100: nRetval = WEIGHT_ULTRALIGHT; break; 165 case FontWeight_200: nRetval = WEIGHT_LIGHT; break; 166 case FontWeight_300: nRetval = WEIGHT_SEMILIGHT; break; 167 case FontWeight_400: nRetval = WEIGHT_NORMAL; break; 168 case FontWeight_500: nRetval = WEIGHT_MEDIUM; break; 169 case FontWeight_600: nRetval = WEIGHT_SEMIBOLD; break; 170 case FontWeight_700: nRetval = WEIGHT_BOLD; break; 171 case FontWeight_800: nRetval = WEIGHT_ULTRABOLD; break; 172 case FontWeight_900: nRetval = WEIGHT_BLACK; break; 173 default: break; 174 } 175 176 return nRetval; 177 } 178 179 void SvgStyleAttributes::readCssStyle(const OUString& rCandidate) 180 { 181 const sal_Int32 nLen(rCandidate.getLength()); 182 sal_Int32 nPos(0); 183 184 while(nPos < nLen) 185 { 186 // get TokenName 187 OUStringBuffer aTokenName; 188 skip_char(rCandidate, u' ', nPos, nLen); 189 copyString(rCandidate, nPos, aTokenName, nLen); 190 191 if (aTokenName.isEmpty()) 192 { 193 // if no TokenName advance one by force to avoid death loop, continue 194 OSL_ENSURE(false, "Could not interpret on current position, advancing one byte (!)"); 195 nPos++; 196 continue; 197 } 198 199 // get TokenValue 200 OUStringBuffer aTokenValue; 201 skip_char(rCandidate, u' ', u':', nPos, nLen); 202 copyToLimiter(rCandidate, u';', nPos, aTokenValue, nLen); 203 skip_char(rCandidate, u' ', u';', nPos, nLen); 204 205 if (aTokenValue.isEmpty()) 206 { 207 // no value - continue 208 continue; 209 } 210 211 // generate OUStrings 212 const OUString aOUTokenName(aTokenName.makeStringAndClear()); 213 OUString aOUTokenValue(aTokenValue.makeStringAndClear()); 214 215 // check for '!important' CssStyle mark, currently not supported 216 // but needs to be extracted for correct parsing 217 OUString aTokenImportant("!important"); 218 const sal_Int32 nIndexTokenImportant(aOUTokenValue.indexOf(aTokenImportant)); 219 220 if(-1 != nIndexTokenImportant) 221 { 222 // if there currently just remove it and remove spaces to have the value only 223 OUString aNewOUTokenValue; 224 225 if(nIndexTokenImportant > 0) 226 { 227 // copy content before token 228 aNewOUTokenValue += aOUTokenValue.copy(0, nIndexTokenImportant); 229 } 230 231 if(aOUTokenValue.getLength() > nIndexTokenImportant + aTokenImportant.getLength()) 232 { 233 // copy content after token 234 aNewOUTokenValue += aOUTokenValue.copy(nIndexTokenImportant + aTokenImportant.getLength()); 235 } 236 237 // remove spaces 238 aOUTokenValue = aNewOUTokenValue.trim(); 239 } 240 241 // valid token-value pair, parse it 242 parseStyleAttribute(StrToSVGToken(aOUTokenName, true), aOUTokenValue, true); 243 } 244 } 245 246 const SvgStyleAttributes* SvgStyleAttributes::getParentStyle() const 247 { 248 if(getCssStyleParent()) 249 { 250 return getCssStyleParent(); 251 } 252 253 if(mrOwner.supportsParentStyle() && mrOwner.getParent()) 254 { 255 return mrOwner.getParent()->getSvgStyleAttributes(); 256 } 257 258 return nullptr; 259 } 260 261 void SvgStyleAttributes::add_text( 262 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 263 drawinglayer::primitive2d::Primitive2DContainer const & rSource) const 264 { 265 if(rSource.empty()) 266 return; 267 268 // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D 269 // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill()) 270 // set. When another fill is used and also evtl. stroke is set it gets necessary to 271 // dismantle to geometry and add needed primitives 272 const basegfx::BColor* pFill = getFill(); 273 const SvgGradientNode* pFillGradient = getSvgGradientNodeFill(); 274 const SvgPatternNode* pFillPattern = getSvgPatternNodeFill(); 275 const basegfx::BColor* pStroke = getStroke(); 276 const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke(); 277 const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke(); 278 basegfx::B2DPolyPolygon aMergedArea; 279 280 if(pFillGradient || pFillPattern || pStroke || pStrokeGradient || pStrokePattern) 281 { 282 // text geometry is needed, create 283 // use neutral ViewInformation and create LineGeometryExtractor2D 284 const drawinglayer::geometry::ViewInformation2D aViewInformation2D; 285 drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D); 286 287 // process 288 aExtractor.process(rSource); 289 290 // get results 291 const drawinglayer::processor2d::TextAsPolygonDataNodeVector& rResult = aExtractor.getTarget(); 292 const sal_uInt32 nResultCount(rResult.size()); 293 basegfx::B2DPolyPolygonVector aTextFillVector; 294 aTextFillVector.reserve(nResultCount); 295 296 for(sal_uInt32 a(0); a < nResultCount; a++) 297 { 298 const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a]; 299 300 if(rCandidate.getIsFilled()) 301 { 302 aTextFillVector.push_back(rCandidate.getB2DPolyPolygon()); 303 } 304 } 305 306 if(!aTextFillVector.empty()) 307 { 308 aMergedArea = basegfx::utils::mergeToSinglePolyPolygon(aTextFillVector); 309 } 310 } 311 312 const bool bStrokeUsed(pStroke || pStrokeGradient || pStrokePattern); 313 314 // add fill. Use geometry even for simple color fill when stroke 315 // is used, else text rendering and the geometry-based stroke will 316 // normally not really match optically due to diverse system text 317 // renderers 318 if(aMergedArea.count() && (pFillGradient || pFillPattern || bStrokeUsed)) 319 { 320 // create text fill content based on geometry 321 add_fill(aMergedArea, rTarget, aMergedArea.getB2DRange()); 322 } 323 else if(pFill) 324 { 325 // add the already prepared primitives for single color fill 326 rTarget.append(rSource); 327 } 328 329 // add stroke 330 if(aMergedArea.count() && bStrokeUsed) 331 { 332 // create text stroke content 333 add_stroke(aMergedArea, rTarget, aMergedArea.getB2DRange()); 334 } 335 } 336 337 void SvgStyleAttributes::add_fillGradient( 338 const basegfx::B2DPolyPolygon& rPath, 339 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 340 const SvgGradientNode& rFillGradient, 341 const basegfx::B2DRange& rGeoRange) const 342 { 343 // create fill content 344 drawinglayer::primitive2d::SvgGradientEntryVector aSvgGradientEntryVector; 345 346 // get the color stops 347 rFillGradient.collectGradientEntries(aSvgGradientEntryVector); 348 349 if(aSvgGradientEntryVector.empty()) 350 return; 351 352 basegfx::B2DHomMatrix aGeoToUnit; 353 basegfx::B2DHomMatrix aGradientTransform; 354 355 if(rFillGradient.getGradientTransform()) 356 { 357 aGradientTransform = *rFillGradient.getGradientTransform(); 358 } 359 360 if(userSpaceOnUse == rFillGradient.getGradientUnits()) 361 { 362 aGeoToUnit.translate(-rGeoRange.getMinX(), -rGeoRange.getMinY()); 363 aGeoToUnit.scale(1.0 / rGeoRange.getWidth(), 1.0 / rGeoRange.getHeight()); 364 } 365 366 if(SVGTokenLinearGradient == rFillGradient.getType()) 367 { 368 basegfx::B2DPoint aStart(0.0, 0.0); 369 basegfx::B2DPoint aEnd(1.0, 0.0); 370 371 if(userSpaceOnUse == rFillGradient.getGradientUnits()) 372 { 373 // all possible units 374 aStart.setX(rFillGradient.getX1().solve(mrOwner, xcoordinate)); 375 aStart.setY(rFillGradient.getY1().solve(mrOwner, ycoordinate)); 376 aEnd.setX(rFillGradient.getX2().solve(mrOwner, xcoordinate)); 377 aEnd.setY(rFillGradient.getY2().solve(mrOwner, ycoordinate)); 378 } 379 else 380 { 381 // fractions or percent relative to object bounds 382 const SvgNumber X1(rFillGradient.getX1()); 383 const SvgNumber Y1(rFillGradient.getY1()); 384 const SvgNumber X2(rFillGradient.getX2()); 385 const SvgNumber Y2(rFillGradient.getY2()); 386 387 aStart.setX(Unit_percent == X1.getUnit() ? X1.getNumber() * 0.01 : X1.getNumber()); 388 aStart.setY(Unit_percent == Y1.getUnit() ? Y1.getNumber() * 0.01 : Y1.getNumber()); 389 aEnd.setX(Unit_percent == X2.getUnit() ? X2.getNumber() * 0.01 : X2.getNumber()); 390 aEnd.setY(Unit_percent == Y2.getUnit() ? Y2.getNumber() * 0.01 : Y2.getNumber()); 391 } 392 393 if(!aGeoToUnit.isIdentity()) 394 { 395 aStart *= aGeoToUnit; 396 aEnd *= aGeoToUnit; 397 } 398 399 rTarget.push_back( 400 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D( 401 aGradientTransform, 402 rPath, 403 aSvgGradientEntryVector, 404 aStart, 405 aEnd, 406 userSpaceOnUse != rFillGradient.getGradientUnits(), 407 rFillGradient.getSpreadMethod())); 408 } 409 else 410 { 411 basegfx::B2DPoint aStart(0.5, 0.5); 412 basegfx::B2DPoint aFocal; 413 double fRadius(0.5); 414 const SvgNumber* pFx = rFillGradient.getFx(); 415 const SvgNumber* pFy = rFillGradient.getFy(); 416 const bool bFocal(pFx || pFy); 417 418 if(userSpaceOnUse == rFillGradient.getGradientUnits()) 419 { 420 // all possible units 421 aStart.setX(rFillGradient.getCx().solve(mrOwner, xcoordinate)); 422 aStart.setY(rFillGradient.getCy().solve(mrOwner, ycoordinate)); 423 fRadius = rFillGradient.getR().solve(mrOwner); 424 425 if(bFocal) 426 { 427 aFocal.setX(pFx ? pFx->solve(mrOwner, xcoordinate) : aStart.getX()); 428 aFocal.setY(pFy ? pFy->solve(mrOwner, ycoordinate) : aStart.getY()); 429 } 430 } 431 else 432 { 433 // fractions or percent relative to object bounds 434 const SvgNumber Cx(rFillGradient.getCx()); 435 const SvgNumber Cy(rFillGradient.getCy()); 436 const SvgNumber R(rFillGradient.getR()); 437 438 aStart.setX(Unit_percent == Cx.getUnit() ? Cx.getNumber() * 0.01 : Cx.getNumber()); 439 aStart.setY(Unit_percent == Cy.getUnit() ? Cy.getNumber() * 0.01 : Cy.getNumber()); 440 fRadius = (Unit_percent == R.getUnit()) ? R.getNumber() * 0.01 : R.getNumber(); 441 442 if(bFocal) 443 { 444 aFocal.setX(pFx ? (Unit_percent == pFx->getUnit() ? pFx->getNumber() * 0.01 : pFx->getNumber()) : aStart.getX()); 445 aFocal.setY(pFy ? (Unit_percent == pFy->getUnit() ? pFy->getNumber() * 0.01 : pFy->getNumber()) : aStart.getY()); 446 } 447 } 448 449 if(!aGeoToUnit.isIdentity()) 450 { 451 aStart *= aGeoToUnit; 452 fRadius = (aGeoToUnit * basegfx::B2DVector(fRadius, 0.0)).getLength(); 453 454 if(bFocal) 455 { 456 aFocal *= aGeoToUnit; 457 } 458 } 459 460 rTarget.push_back( 461 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D( 462 aGradientTransform, 463 rPath, 464 aSvgGradientEntryVector, 465 aStart, 466 fRadius, 467 userSpaceOnUse != rFillGradient.getGradientUnits(), 468 rFillGradient.getSpreadMethod(), 469 bFocal ? &aFocal : nullptr)); 470 } 471 } 472 473 void SvgStyleAttributes::add_fillPatternTransform( 474 const basegfx::B2DPolyPolygon& rPath, 475 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 476 const SvgPatternNode& rFillPattern, 477 const basegfx::B2DRange& rGeoRange) const 478 { 479 // prepare fill polyPolygon with given pattern, check for patternTransform 480 if(rFillPattern.getPatternTransform() && !rFillPattern.getPatternTransform()->isIdentity()) 481 { 482 // PatternTransform is active; Handle by filling the inverse transformed 483 // path and back-transforming the result 484 basegfx::B2DPolyPolygon aPath(rPath); 485 basegfx::B2DHomMatrix aInv(*rFillPattern.getPatternTransform()); 486 drawinglayer::primitive2d::Primitive2DContainer aNewTarget; 487 488 aInv.invert(); 489 aPath.transform(aInv); 490 add_fillPattern(aPath, aNewTarget, rFillPattern, aPath.getB2DRange()); 491 492 if(!aNewTarget.empty()) 493 { 494 rTarget.push_back( 495 new drawinglayer::primitive2d::TransformPrimitive2D( 496 *rFillPattern.getPatternTransform(), 497 aNewTarget)); 498 } 499 } 500 else 501 { 502 // no patternTransform, create fillPattern directly 503 add_fillPattern(rPath, rTarget, rFillPattern, rGeoRange); 504 } 505 } 506 507 void SvgStyleAttributes::add_fillPattern( 508 const basegfx::B2DPolyPolygon& rPath, 509 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 510 const SvgPatternNode& rFillPattern, 511 const basegfx::B2DRange& rGeoRange) const 512 { 513 // fill polyPolygon with given pattern 514 const drawinglayer::primitive2d::Primitive2DContainer& rPrimitives = rFillPattern.getPatternPrimitives(); 515 516 if(rPrimitives.empty()) 517 return; 518 519 double fTargetWidth(rGeoRange.getWidth()); 520 double fTargetHeight(rGeoRange.getHeight()); 521 522 if(!(fTargetWidth > 0.0 && fTargetHeight > 0.0)) 523 return; 524 525 // get relative values from pattern 526 double fX(0.0); 527 double fY(0.0); 528 double fW(0.0); 529 double fH(0.0); 530 531 rFillPattern.getValuesRelative(fX, fY, fW, fH, rGeoRange, mrOwner); 532 533 if(!(fW > 0.0 && fH > 0.0)) 534 return; 535 536 // build the reference range relative to the rGeoRange 537 const basegfx::B2DRange aReferenceRange(fX, fY, fX + fW, fY + fH); 538 539 // find out how the content is mapped to the reference range 540 basegfx::B2DHomMatrix aMapPrimitivesToUnitRange; 541 const basegfx::B2DRange* pViewBox = rFillPattern.getViewBox(); 542 543 if(pViewBox) 544 { 545 // use viewBox/preserveAspectRatio 546 const SvgAspectRatio& rRatio = rFillPattern.getSvgAspectRatio(); 547 const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); 548 549 if(rRatio.isSet()) 550 { 551 // let mapping be created from SvgAspectRatio 552 aMapPrimitivesToUnitRange = rRatio.createMapping(aUnitRange, *pViewBox); 553 } 554 else 555 { 556 // choose default mapping 557 aMapPrimitivesToUnitRange = SvgAspectRatio::createLinearMapping(aUnitRange, *pViewBox); 558 } 559 } 560 else 561 { 562 // use patternContentUnits 563 const SvgUnits aPatternContentUnits(rFillPattern.getPatternContentUnits() ? *rFillPattern.getPatternContentUnits() : userSpaceOnUse); 564 565 if(userSpaceOnUse == aPatternContentUnits) 566 { 567 // create relative mapping to unit coordinates 568 aMapPrimitivesToUnitRange.scale(1.0 / (fW * fTargetWidth), 1.0 / (fH * fTargetHeight)); 569 } 570 else 571 { 572 aMapPrimitivesToUnitRange.scale(1.0 / fW, 1.0 / fH); 573 } 574 } 575 576 // apply aMapPrimitivesToUnitRange to content when used 577 drawinglayer::primitive2d::Primitive2DContainer aPrimitives(rPrimitives); 578 579 if(!aMapPrimitivesToUnitRange.isIdentity()) 580 { 581 const drawinglayer::primitive2d::Primitive2DReference xRef( 582 new drawinglayer::primitive2d::TransformPrimitive2D( 583 aMapPrimitivesToUnitRange, 584 aPrimitives)); 585 586 aPrimitives = drawinglayer::primitive2d::Primitive2DContainer { xRef }; 587 } 588 589 // embed in PatternFillPrimitive2D 590 rTarget.push_back( 591 new drawinglayer::primitive2d::PatternFillPrimitive2D( 592 rPath, 593 aPrimitives, 594 aReferenceRange)); 595 } 596 597 void SvgStyleAttributes::add_fill( 598 const basegfx::B2DPolyPolygon& rPath, 599 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 600 const basegfx::B2DRange& rGeoRange) const 601 { 602 const basegfx::BColor* pFill = getFill(); 603 const SvgGradientNode* pFillGradient = getSvgGradientNodeFill(); 604 const SvgPatternNode* pFillPattern = getSvgPatternNodeFill(); 605 606 if(!(pFill || pFillGradient || pFillPattern)) 607 return; 608 609 const double fFillOpacity(getFillOpacity().solve(mrOwner)); 610 611 if(!basegfx::fTools::more(fFillOpacity, 0.0)) 612 return; 613 614 drawinglayer::primitive2d::Primitive2DContainer aNewFill; 615 616 if(pFillGradient) 617 { 618 // create fill content with SVG gradient primitive 619 add_fillGradient(rPath, aNewFill, *pFillGradient, rGeoRange); 620 } 621 else if(pFillPattern) 622 { 623 // create fill content with SVG pattern primitive 624 add_fillPatternTransform(rPath, aNewFill, *pFillPattern, rGeoRange); 625 } 626 else // if(pFill) 627 { 628 // create fill content 629 aNewFill.resize(1); 630 aNewFill[0] = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 631 rPath, 632 *pFill); 633 } 634 635 if(aNewFill.empty()) 636 return; 637 638 if(basegfx::fTools::less(fFillOpacity, 1.0)) 639 { 640 // embed in UnifiedTransparencePrimitive2D 641 rTarget.push_back( 642 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 643 aNewFill, 644 1.0 - fFillOpacity)); 645 } 646 else 647 { 648 // append 649 rTarget.append(aNewFill); 650 } 651 } 652 653 void SvgStyleAttributes::add_stroke( 654 const basegfx::B2DPolyPolygon& rPath, 655 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 656 const basegfx::B2DRange& rGeoRange) const 657 { 658 const basegfx::BColor* pStroke = getStroke(); 659 const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke(); 660 const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke(); 661 662 if(!(pStroke || pStrokeGradient || pStrokePattern)) 663 return; 664 665 drawinglayer::primitive2d::Primitive2DContainer aNewStroke; 666 const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner)); 667 668 if(!basegfx::fTools::more(fStrokeOpacity, 0.0)) 669 return; 670 671 // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all 672 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner) : 1.0); 673 674 if(!basegfx::fTools::more(fStrokeWidth, 0.0)) 675 return; 676 677 drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive; 678 679 // if we have a line with two identical points it is not really a line, 680 // but used by SVG sometimes to paint a single dot.In that case, create 681 // the geometry for a single dot 682 if(1 == rPath.count()) 683 { 684 const basegfx::B2DPolygon& aSingle(rPath.getB2DPolygon(0)); 685 686 if(2 == aSingle.count() && aSingle.getB2DPoint(0).equal(aSingle.getB2DPoint(1))) 687 { 688 aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 689 basegfx::B2DPolyPolygon( 690 basegfx::utils::createPolygonFromCircle( 691 aSingle.getB2DPoint(0), 692 fStrokeWidth * (1.44 * 0.5))), 693 pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0)); 694 } 695 } 696 697 if(!aNewLinePrimitive.is()) 698 { 699 // get LineJoin, LineCap and stroke array 700 const basegfx::B2DLineJoin aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin())); 701 const css::drawing::LineCap aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap())); 702 ::std::vector< double > aDashArray; 703 704 if(!getStrokeDasharray().empty()) 705 { 706 aDashArray = solveSvgNumberVector(getStrokeDasharray(), mrOwner); 707 } 708 709 // convert svg:stroke-miterlimit to LineAttrute:mfMiterMinimumAngle 710 // The default needs to be set explicitly, because svg default <> Draw default 711 double fMiterMinimumAngle; 712 if (getStrokeMiterLimit().isSet()) 713 { 714 fMiterMinimumAngle = 2.0 * asin(1.0/getStrokeMiterLimit().getNumber()); 715 } 716 else 717 { 718 fMiterMinimumAngle = 2.0 * asin(0.25); // 1.0/default 4.0 719 } 720 721 // todo: Handle getStrokeDashOffset() 722 723 // prepare line attribute 724 const drawinglayer::attribute::LineAttribute aLineAttribute( 725 pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0), 726 fStrokeWidth, 727 aB2DLineJoin, 728 aLineCap, 729 fMiterMinimumAngle); 730 731 if(aDashArray.empty()) 732 { 733 aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( 734 rPath, 735 aLineAttribute); 736 } 737 else 738 { 739 const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(aDashArray); 740 741 aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( 742 rPath, 743 aLineAttribute, 744 aStrokeAttribute); 745 } 746 } 747 748 if(pStrokeGradient || pStrokePattern) 749 { 750 // put primitive into Primitive2DReference and Primitive2DSequence 751 const drawinglayer::primitive2d::Primitive2DContainer aSeq { aNewLinePrimitive }; 752 753 // use neutral ViewInformation and create LineGeometryExtractor2D 754 const drawinglayer::geometry::ViewInformation2D aViewInformation2D; 755 drawinglayer::processor2d::LineGeometryExtractor2D aExtractor(aViewInformation2D); 756 757 // process 758 aExtractor.process(aSeq); 759 760 // check for fill rsults 761 const basegfx::B2DPolyPolygonVector& rLineFillVector(aExtractor.getExtractedLineFills()); 762 763 if(!rLineFillVector.empty()) 764 { 765 const basegfx::B2DPolyPolygon aMergedArea( 766 basegfx::utils::mergeToSinglePolyPolygon( 767 rLineFillVector)); 768 769 if(aMergedArea.count()) 770 { 771 if(pStrokeGradient) 772 { 773 // create fill content with SVG gradient primitive. Use original GeoRange, 774 // e.g. from circle without LineWidth 775 add_fillGradient(aMergedArea, aNewStroke, *pStrokeGradient, rGeoRange); 776 } 777 else // if(pStrokePattern) 778 { 779 // create fill content with SVG pattern primitive. Use GeoRange 780 // from the expanded data, e.g. circle with extended geo by half linewidth 781 add_fillPatternTransform(aMergedArea, aNewStroke, *pStrokePattern, aMergedArea.getB2DRange()); 782 } 783 } 784 } 785 } 786 else // if(pStroke) 787 { 788 aNewStroke.push_back(aNewLinePrimitive); 789 } 790 791 if(aNewStroke.empty()) 792 return; 793 794 if(basegfx::fTools::less(fStrokeOpacity, 1.0)) 795 { 796 // embed in UnifiedTransparencePrimitive2D 797 rTarget.push_back( 798 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 799 aNewStroke, 800 1.0 - fStrokeOpacity)); 801 } 802 else 803 { 804 // append 805 rTarget.append(aNewStroke); 806 } 807 } 808 809 bool SvgStyleAttributes::prepare_singleMarker( 810 drawinglayer::primitive2d::Primitive2DContainer& rMarkerPrimitives, 811 basegfx::B2DHomMatrix& rMarkerTransform, 812 basegfx::B2DRange& rClipRange, 813 const SvgMarkerNode& rMarker) const 814 { 815 // reset return values 816 rMarkerTransform.identity(); 817 rClipRange.reset(); 818 819 // get marker primitive representation 820 rMarkerPrimitives = rMarker.getMarkerPrimitives(); 821 822 if(!rMarkerPrimitives.empty()) 823 { 824 basegfx::B2DRange aPrimitiveRange(0.0, 0.0, 1.0, 1.0); 825 const basegfx::B2DRange* pViewBox = rMarker.getViewBox(); 826 827 if(pViewBox) 828 { 829 aPrimitiveRange = *pViewBox; 830 } 831 832 if(aPrimitiveRange.getWidth() > 0.0 && aPrimitiveRange.getHeight() > 0.0) 833 { 834 double fTargetWidth(rMarker.getMarkerWidth().isSet() ? rMarker.getMarkerWidth().solve(mrOwner, xcoordinate) : 3.0); 835 double fTargetHeight(rMarker.getMarkerHeight().isSet() ? rMarker.getMarkerHeight().solve(mrOwner, xcoordinate) : 3.0); 836 const bool bStrokeWidth(SvgMarkerNode::MarkerUnits::strokeWidth == rMarker.getMarkerUnits()); 837 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner) : 1.0); 838 839 if(bStrokeWidth) 840 { 841 // relative to strokeWidth 842 fTargetWidth *= fStrokeWidth; 843 fTargetHeight *= fStrokeWidth; 844 } 845 846 if(fTargetWidth > 0.0 && fTargetHeight > 0.0) 847 { 848 // create mapping 849 const basegfx::B2DRange aTargetRange(0.0, 0.0, fTargetWidth, fTargetHeight); 850 const SvgAspectRatio& rRatio = rMarker.getSvgAspectRatio(); 851 852 if(rRatio.isSet()) 853 { 854 // let mapping be created from SvgAspectRatio 855 rMarkerTransform = rRatio.createMapping(aTargetRange, aPrimitiveRange); 856 857 if(rRatio.isMeetOrSlice()) 858 { 859 // need to clip 860 rClipRange = aPrimitiveRange; 861 } 862 } 863 else 864 { 865 if(!pViewBox) 866 { 867 if(bStrokeWidth) 868 { 869 // adapt to strokewidth if needed 870 rMarkerTransform.scale(fStrokeWidth, fStrokeWidth); 871 } 872 } 873 else 874 { 875 // choose default mapping 876 rMarkerTransform = SvgAspectRatio::createLinearMapping(aTargetRange, aPrimitiveRange); 877 } 878 } 879 880 // get and apply reference point. Initially it's in marker local coordinate system 881 basegfx::B2DPoint aRefPoint( 882 rMarker.getRefX().isSet() ? rMarker.getRefX().solve(mrOwner, xcoordinate) : 0.0, 883 rMarker.getRefY().isSet() ? rMarker.getRefY().solve(mrOwner, ycoordinate) : 0.0); 884 885 // apply MarkerTransform to have it in mapped coordinates 886 aRefPoint *= rMarkerTransform; 887 888 // apply by moving RepPoint to (0.0) 889 rMarkerTransform.translate(-aRefPoint.getX(), -aRefPoint.getY()); 890 891 return true; 892 } 893 } 894 } 895 896 return false; 897 } 898 899 void SvgStyleAttributes::add_markers( 900 const basegfx::B2DPolyPolygon& rPath, 901 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 902 const basegfx::utils::PointIndexSet* pHelpPointIndices) const 903 { 904 // try to access linked markers 905 const SvgMarkerNode* pStart = accessMarkerStartXLink(); 906 const SvgMarkerNode* pMid = accessMarkerMidXLink(); 907 const SvgMarkerNode* pEnd = accessMarkerEndXLink(); 908 909 if(!(pStart || pMid || pEnd)) 910 return; 911 912 const sal_uInt32 nSubPathCount(rPath.count()); 913 914 if(!nSubPathCount) 915 return; 916 917 // remember prepared marker; pStart, pMid and pEnd may all be equal when 918 // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end', 919 // see 'case SVGTokenMarker' in this file; thus in this case only one common 920 // marker in primitive form will be prepared 921 const SvgMarkerNode* pPrepared = nullptr; 922 923 // values for the prepared marker, results of prepare_singleMarker 924 drawinglayer::primitive2d::Primitive2DContainer aPreparedMarkerPrimitives; 925 basegfx::B2DHomMatrix aPreparedMarkerTransform; 926 basegfx::B2DRange aPreparedMarkerClipRange; 927 928 for (sal_uInt32 a(0); a < nSubPathCount; a++) 929 { 930 // iterate over sub-paths 931 const basegfx::B2DPolygon& aSubPolygonPath(rPath.getB2DPolygon(a)); 932 const sal_uInt32 nSubPolygonPointCount(aSubPolygonPath.count()); 933 const bool bSubPolygonPathIsClosed(aSubPolygonPath.isClosed()); 934 935 if(nSubPolygonPointCount) 936 { 937 // for each sub-path, create one marker per point (when closed, two markers 938 // need to pe created for the 1st point) 939 const sal_uInt32 nTargetMarkerCount(bSubPolygonPathIsClosed ? nSubPolygonPointCount + 1 : nSubPolygonPointCount); 940 941 for (sal_uInt32 b(0); b < nTargetMarkerCount; b++) 942 { 943 const bool bIsFirstMarker(!a && !b); 944 const bool bIsLastMarker(nSubPathCount - 1 == a && nTargetMarkerCount - 1 == b); 945 const SvgMarkerNode* pNeeded = nullptr; 946 947 if(bIsFirstMarker) 948 { 949 // 1st point in 1st sub-polygon, use pStart 950 pNeeded = pStart; 951 } 952 else if(bIsLastMarker) 953 { 954 // last point in last sub-polygon, use pEnd 955 pNeeded = pEnd; 956 } 957 else 958 { 959 // anything in-between, use pMid 960 pNeeded = pMid; 961 } 962 963 if(pHelpPointIndices && !pHelpPointIndices->empty()) 964 { 965 const basegfx::utils::PointIndexSet::const_iterator aFound( 966 pHelpPointIndices->find(basegfx::utils::PointIndex(a, b))); 967 968 if(aFound != pHelpPointIndices->end()) 969 { 970 // this point is a pure helper point; do not create a marker for it 971 continue; 972 } 973 } 974 975 if(!pNeeded) 976 { 977 // no marker needs to be created for this point 978 continue; 979 } 980 981 if(pPrepared != pNeeded) 982 { 983 // if needed marker is not yet prepared, do it now 984 if(prepare_singleMarker(aPreparedMarkerPrimitives, aPreparedMarkerTransform, aPreparedMarkerClipRange, *pNeeded)) 985 { 986 pPrepared = pNeeded; 987 } 988 else 989 { 990 // error: could not prepare given marker 991 OSL_ENSURE(false, "OOps, could not prepare given marker as primitives (!)"); 992 pPrepared = nullptr; 993 continue; 994 } 995 } 996 997 // prepare complete transform 998 basegfx::B2DHomMatrix aCombinedTransform(aPreparedMarkerTransform); 999 1000 // get rotation 1001 if(pPrepared->getOrientAuto()) 1002 { 1003 const sal_uInt32 nPointIndex(b % nSubPolygonPointCount); 1004 1005 // get entering and leaving tangents; this will search backward/forward 1006 // in the polygon to find tangents unequal to zero, skipping empty edges 1007 // see basegfx descriptions) 1008 // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker 1009 // and entering tangent for end marker. To achieve this (if wanted) it is possible 1010 // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker. 1011 // This is not done here, see comment 14 in task #1232379# 1012 // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute 1013 basegfx::B2DVector aEntering( 1014 basegfx::utils::getTangentEnteringPoint( 1015 aSubPolygonPath, 1016 nPointIndex)); 1017 basegfx::B2DVector aLeaving( 1018 basegfx::utils::getTangentLeavingPoint( 1019 aSubPolygonPath, 1020 nPointIndex)); 1021 const bool bEntering(!aEntering.equalZero()); 1022 const bool bLeaving(!aLeaving.equalZero()); 1023 1024 if(bEntering || bLeaving) 1025 { 1026 basegfx::B2DVector aSum(0.0, 0.0); 1027 1028 if(bEntering) 1029 { 1030 aSum += aEntering.normalize(); 1031 } 1032 1033 if(bLeaving) 1034 { 1035 aSum += aLeaving.normalize(); 1036 } 1037 1038 if(!aSum.equalZero()) 1039 { 1040 const double fAngle(atan2(aSum.getY(), aSum.getX())); 1041 1042 // apply rotation 1043 aCombinedTransform.rotate(fAngle); 1044 } 1045 } 1046 } 1047 else 1048 { 1049 // apply rotation 1050 aCombinedTransform.rotate(pPrepared->getAngle()); 1051 } 1052 1053 // get and apply target position 1054 const basegfx::B2DPoint aPoint(aSubPolygonPath.getB2DPoint(b % nSubPolygonPointCount)); 1055 1056 aCombinedTransform.translate(aPoint.getX(), aPoint.getY()); 1057 1058 // prepare marker 1059 drawinglayer::primitive2d::Primitive2DReference xMarker( 1060 new drawinglayer::primitive2d::TransformPrimitive2D( 1061 aCombinedTransform, 1062 aPreparedMarkerPrimitives)); 1063 1064 if(!aPreparedMarkerClipRange.isEmpty()) 1065 { 1066 // marker needs to be clipped, it's bigger as the mapping 1067 basegfx::B2DPolyPolygon aClipPolygon(basegfx::utils::createPolygonFromRect(aPreparedMarkerClipRange)); 1068 1069 aClipPolygon.transform(aCombinedTransform); 1070 xMarker = new drawinglayer::primitive2d::MaskPrimitive2D( 1071 aClipPolygon, 1072 drawinglayer::primitive2d::Primitive2DContainer { xMarker }); 1073 } 1074 1075 // add marker 1076 rTarget.push_back(xMarker); 1077 } 1078 } 1079 } 1080 } 1081 1082 void SvgStyleAttributes::add_path( 1083 const basegfx::B2DPolyPolygon& rPath, 1084 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 1085 const basegfx::utils::PointIndexSet* pHelpPointIndices) const 1086 { 1087 if(!rPath.count()) 1088 { 1089 // no geometry at all 1090 return; 1091 } 1092 1093 const basegfx::B2DRange aGeoRange(rPath.getB2DRange()); 1094 1095 if(aGeoRange.isEmpty()) 1096 { 1097 // no geometry range 1098 return; 1099 } 1100 1101 const double fOpacity(getOpacity().solve(mrOwner)); 1102 1103 if(basegfx::fTools::equalZero(fOpacity)) 1104 { 1105 // not visible 1106 return; 1107 } 1108 1109 // check if it's a line 1110 const bool bNoWidth(basegfx::fTools::equalZero(aGeoRange.getWidth())); 1111 const bool bNoHeight(basegfx::fTools::equalZero(aGeoRange.getHeight())); 1112 const bool bIsTwoPointLine(1 == rPath.count() 1113 && !rPath.areControlPointsUsed() 1114 && 2 == rPath.getB2DPolygon(0).count()); 1115 const bool bIsLine(bIsTwoPointLine || bNoWidth || bNoHeight); 1116 1117 if(!bIsLine) 1118 { 1119 // create fill 1120 basegfx::B2DPolyPolygon aPath(rPath); 1121 const bool bNeedToCheckClipRule(SVGTokenPath == mrOwner.getType() || SVGTokenPolygon == mrOwner.getType()); 1122 const bool bClipPathIsNonzero(bNeedToCheckClipRule && mbIsClipPathContent && FillRule_nonzero == maClipRule); 1123 const bool bFillRuleIsNonzero(bNeedToCheckClipRule && !mbIsClipPathContent && FillRule_nonzero == getFillRule()); 1124 1125 if(bClipPathIsNonzero || bFillRuleIsNonzero) 1126 { 1127 if(getFill() || getSvgGradientNodeFill() || getSvgPatternNodeFill()) { 1128 // nonzero is wanted, solve geometrically (see description on basegfx) 1129 // basegfx::utils::createNonzeroConform() is expensive for huge paths 1130 // and is only needed if path will be filled later on 1131 aPath = basegfx::utils::createNonzeroConform(aPath); 1132 } 1133 } 1134 1135 add_fill(aPath, rTarget, aGeoRange); 1136 } 1137 1138 // create stroke 1139 add_stroke(rPath, rTarget, aGeoRange); 1140 1141 // Svg supports markers for path, polygon, polyline and line 1142 if(SVGTokenPath == mrOwner.getType() || // path 1143 SVGTokenPolygon == mrOwner.getType() || // polygon, polyline 1144 SVGTokenLine == mrOwner.getType()) // line 1145 { 1146 // try to add markers 1147 add_markers(rPath, rTarget, pHelpPointIndices); 1148 } 1149 } 1150 1151 void SvgStyleAttributes::add_postProcess( 1152 drawinglayer::primitive2d::Primitive2DContainer& rTarget, 1153 const drawinglayer::primitive2d::Primitive2DContainer& rSource, 1154 const basegfx::B2DHomMatrix* pTransform) const 1155 { 1156 if(rSource.empty()) 1157 return; 1158 1159 const double fOpacity(getOpacity().solve(mrOwner)); 1160 1161 if(basegfx::fTools::equalZero(fOpacity)) 1162 { 1163 return; 1164 } 1165 1166 drawinglayer::primitive2d::Primitive2DContainer aSource(rSource); 1167 1168 if(basegfx::fTools::less(fOpacity, 1.0)) 1169 { 1170 // embed in UnifiedTransparencePrimitive2D 1171 const drawinglayer::primitive2d::Primitive2DReference xRef( 1172 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 1173 aSource, 1174 1.0 - fOpacity)); 1175 1176 aSource = drawinglayer::primitive2d::Primitive2DContainer { xRef }; 1177 } 1178 1179 if(pTransform) 1180 { 1181 // create embedding group element with transformation. This applies the given 1182 // transformation to the graphical content, but *not* to mask and/or clip (as needed) 1183 const drawinglayer::primitive2d::Primitive2DReference xRef( 1184 new drawinglayer::primitive2d::TransformPrimitive2D( 1185 *pTransform, 1186 aSource)); 1187 1188 aSource = drawinglayer::primitive2d::Primitive2DContainer { xRef }; 1189 } 1190 1191 const SvgClipPathNode* pClip = accessClipPathXLink(); 1192 while(pClip) 1193 { 1194 // #i124852# transform may be needed when userSpaceOnUse 1195 pClip->apply(aSource, pTransform); 1196 pClip = pClip->getSvgStyleAttributes()->accessClipPathXLink(); 1197 } 1198 1199 if(!aSource.empty()) // test again, applied clipPath may have lead to empty geometry 1200 { 1201 const SvgMaskNode* pMask = accessMaskXLink(); 1202 if(pMask) 1203 { 1204 // #i124852# transform may be needed when userSpaceOnUse 1205 pMask->apply(aSource, pTransform); 1206 } 1207 } 1208 1209 // This is part of the SVG import of self-written SVGs from 1210 // Draw/Impress containing multiple Slides/Pages. To be able 1211 // to later 'break' these to multiple Pages if wanted, embed 1212 // each Page-Content in an identifiable Primitive Grouping 1213 // Object. 1214 // This is the case when the current Node is a GroupNode, has 1215 // class="Page" set, has a parent that also is a GroupNode 1216 // at which class="Slide" is set. 1217 // Multiple Slides/Pages are possible for Draw and Impress. 1218 if(SVGTokenG == mrOwner.getType() && mrOwner.getClass()) 1219 { 1220 const OUString aOwnerClass(*mrOwner.getClass()); 1221 1222 if("Page" == aOwnerClass) 1223 { 1224 const SvgNode* pParent(mrOwner.getParent()); 1225 1226 if(nullptr != pParent && SVGTokenG == pParent->getType() && pParent->getClass()) 1227 { 1228 const OUString aParentClass(*pParent->getClass()); 1229 1230 if("Slide" == aParentClass) 1231 { 1232 // embed to grouping primitive to identify the 1233 // Slide/Page information 1234 const drawinglayer::primitive2d::Primitive2DReference xRef( 1235 new drawinglayer::primitive2d::PageHierarchyPrimitive2D( 1236 aSource)); 1237 1238 aSource = drawinglayer::primitive2d::Primitive2DContainer { xRef }; 1239 } 1240 } 1241 } 1242 } 1243 1244 if(!aSource.empty()) // test again, applied mask may have lead to empty geometry 1245 { 1246 // append to current target 1247 rTarget.append(aSource); 1248 } 1249 } 1250 1251 SvgStyleAttributes::SvgStyleAttributes(SvgNode& rOwner) 1252 : mrOwner(rOwner), 1253 mpCssStyleParent(nullptr), 1254 maFill(), 1255 maStroke(), 1256 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true), 1257 maStrokeWidth(), 1258 maStopOpacity(), 1259 mpSvgGradientNodeFill(nullptr), 1260 mpSvgGradientNodeStroke(nullptr), 1261 mpSvgPatternNodeFill(nullptr), 1262 mpSvgPatternNodeStroke(nullptr), 1263 maFillOpacity(), 1264 maStrokeDasharray(), 1265 maStrokeDashOffset(), 1266 maStrokeLinecap(StrokeLinecap_notset), 1267 maStrokeLinejoin(StrokeLinejoin_notset), 1268 maStrokeMiterLimit(), 1269 maStrokeOpacity(), 1270 maFontFamily(), 1271 maFontSize(), 1272 maFontSizeNumber(), 1273 maFontStretch(FontStretch_notset), 1274 maFontStyle(FontStyle_notset), 1275 maFontWeight(FontWeight_notset), 1276 maTextAlign(TextAlign_notset), 1277 maTextDecoration(TextDecoration_notset), 1278 maTextAnchor(TextAnchor_notset), 1279 maColor(), 1280 maOpacity(), 1281 maVisibility(Visibility_notset), 1282 maTitle(), 1283 maDesc(), 1284 maClipPathXLink(), 1285 mpClipPathXLink(nullptr), 1286 maMaskXLink(), 1287 mpMaskXLink(nullptr), 1288 maMarkerStartXLink(), 1289 mpMarkerStartXLink(nullptr), 1290 maMarkerMidXLink(), 1291 mpMarkerMidXLink(nullptr), 1292 maMarkerEndXLink(), 1293 mpMarkerEndXLink(nullptr), 1294 maFillRule(FillRule_notset), 1295 maClipRule(FillRule_nonzero), 1296 maBaselineShift(BaselineShift_Baseline), 1297 maBaselineShiftNumber(0), 1298 maResolvingParent(30, 0), 1299 mbIsClipPathContent(SVGTokenClipPathNode == mrOwner.getType()), 1300 mbStrokeDasharraySet(false) 1301 { 1302 const SvgStyleAttributes* pParentStyle = getParentStyle(); 1303 if(!mbIsClipPathContent) 1304 { 1305 if(pParentStyle) 1306 { 1307 mbIsClipPathContent = pParentStyle->mbIsClipPathContent; 1308 } 1309 } 1310 } 1311 1312 SvgStyleAttributes::~SvgStyleAttributes() 1313 { 1314 } 1315 1316 void SvgStyleAttributes::parseStyleAttribute( 1317 SVGToken aSVGToken, 1318 const OUString& aContent, 1319 bool bCaseIndependent) 1320 { 1321 switch(aSVGToken) 1322 { 1323 case SVGTokenFill: 1324 { 1325 SvgPaint aSvgPaint; 1326 OUString aURL; 1327 SvgNumber aOpacity; 1328 1329 if(readSvgPaint(aContent, aSvgPaint, aURL, bCaseIndependent, aOpacity)) 1330 { 1331 setFill(aSvgPaint); 1332 if(aOpacity.isSet()) 1333 { 1334 setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0))); 1335 } 1336 } 1337 else if(!aURL.isEmpty()) 1338 { 1339 const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL); 1340 1341 if(pNode) 1342 { 1343 if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType()) 1344 { 1345 mpSvgGradientNodeFill = static_cast< const SvgGradientNode* >(pNode); 1346 } 1347 else if(SVGTokenPattern == pNode->getType()) 1348 { 1349 mpSvgPatternNodeFill = static_cast< const SvgPatternNode* >(pNode); 1350 } 1351 } 1352 } 1353 break; 1354 } 1355 case SVGTokenFillOpacity: 1356 { 1357 SvgNumber aNum; 1358 1359 if(readSingleNumber(aContent, aNum)) 1360 { 1361 maFillOpacity = SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet()); 1362 } 1363 break; 1364 } 1365 case SVGTokenFillRule: 1366 { 1367 if(!aContent.isEmpty()) 1368 { 1369 if(aContent.match(commonStrings::aStrNonzero)) 1370 { 1371 maFillRule = FillRule_nonzero; 1372 } 1373 else if(aContent.match(commonStrings::aStrEvenOdd)) 1374 { 1375 maFillRule = FillRule_evenodd; 1376 } 1377 } 1378 break; 1379 } 1380 case SVGTokenStroke: 1381 { 1382 SvgPaint aSvgPaint; 1383 OUString aURL; 1384 SvgNumber aOpacity; 1385 1386 if(readSvgPaint(aContent, aSvgPaint, aURL, bCaseIndependent, aOpacity)) 1387 { 1388 maStroke = aSvgPaint; 1389 if(aOpacity.isSet()) 1390 { 1391 setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0))); 1392 } 1393 } 1394 else if(!aURL.isEmpty()) 1395 { 1396 const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL); 1397 1398 if(pNode) 1399 { 1400 if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType()) 1401 { 1402 mpSvgGradientNodeStroke = static_cast< const SvgGradientNode* >(pNode); 1403 } 1404 else if(SVGTokenPattern == pNode->getType()) 1405 { 1406 mpSvgPatternNodeStroke = static_cast< const SvgPatternNode* >(pNode); 1407 } 1408 } 1409 } 1410 break; 1411 } 1412 case SVGTokenStrokeDasharray: 1413 { 1414 if(!aContent.isEmpty()) 1415 { 1416 SvgNumberVector aVector; 1417 1418 if(aContent.startsWith("none")) 1419 { 1420 // #121221# The special value 'none' needs to be handled 1421 // in the sense that *when* it is set, the parent shall not 1422 // be used. Before this was only dependent on the array being 1423 // empty 1424 mbStrokeDasharraySet = true; 1425 } 1426 else if(readSvgNumberVector(aContent, aVector)) 1427 { 1428 maStrokeDasharray = aVector; 1429 } 1430 } 1431 break; 1432 } 1433 case SVGTokenStrokeDashoffset: 1434 { 1435 SvgNumber aNum; 1436 1437 if(readSingleNumber(aContent, aNum)) 1438 { 1439 if(aNum.isPositive()) 1440 { 1441 maStrokeDashOffset = aNum; 1442 } 1443 } 1444 break; 1445 } 1446 case SVGTokenStrokeLinecap: 1447 { 1448 if(!aContent.isEmpty()) 1449 { 1450 if(aContent.startsWith("butt")) 1451 { 1452 setStrokeLinecap(StrokeLinecap_butt); 1453 } 1454 else if(aContent.startsWith("round")) 1455 { 1456 setStrokeLinecap(StrokeLinecap_round); 1457 } 1458 else if(aContent.startsWith("square")) 1459 { 1460 setStrokeLinecap(StrokeLinecap_square); 1461 } 1462 } 1463 break; 1464 } 1465 case SVGTokenStrokeLinejoin: 1466 { 1467 if(!aContent.isEmpty()) 1468 { 1469 if(aContent.startsWith("miter")) 1470 { 1471 setStrokeLinejoin(StrokeLinejoin_miter); 1472 } 1473 else if(aContent.startsWith("round")) 1474 { 1475 setStrokeLinejoin(StrokeLinejoin_round); 1476 } 1477 else if(aContent.startsWith("bevel")) 1478 { 1479 setStrokeLinejoin(StrokeLinejoin_bevel); 1480 } 1481 } 1482 break; 1483 } 1484 case SVGTokenStrokeMiterlimit: 1485 { 1486 SvgNumber aNum; 1487 1488 if(readSingleNumber(aContent, aNum)) 1489 { 1490 if(basegfx::fTools::moreOrEqual(aNum.getNumber(), 1.0)) 1491 { //readSingleNumber sets Unit_px as default, if unit is missing. Correct it here. 1492 maStrokeMiterLimit = SvgNumber(aNum.getNumber(), Unit_none); 1493 } 1494 } 1495 break; 1496 } 1497 case SVGTokenStrokeOpacity: 1498 { 1499 1500 SvgNumber aNum; 1501 1502 if(readSingleNumber(aContent, aNum)) 1503 { 1504 maStrokeOpacity = SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet()); 1505 } 1506 break; 1507 } 1508 case SVGTokenStrokeWidth: 1509 { 1510 SvgNumber aNum; 1511 1512 if(readSingleNumber(aContent, aNum)) 1513 { 1514 if(aNum.isPositive()) 1515 { 1516 maStrokeWidth = aNum; 1517 } 1518 } 1519 break; 1520 } 1521 case SVGTokenStopColor: 1522 { 1523 SvgPaint aSvgPaint; 1524 OUString aURL; 1525 SvgNumber aOpacity; 1526 1527 if(readSvgPaint(aContent, aSvgPaint, aURL, bCaseIndependent, aOpacity)) 1528 { 1529 maStopColor = aSvgPaint; 1530 if(aOpacity.isSet()) 1531 { 1532 setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0))); 1533 } 1534 } 1535 break; 1536 } 1537 case SVGTokenStopOpacity: 1538 { 1539 SvgNumber aNum; 1540 1541 if(readSingleNumber(aContent, aNum)) 1542 { 1543 if(aNum.isPositive()) 1544 { 1545 maStopOpacity = aNum; 1546 } 1547 } 1548 break; 1549 } 1550 case SVGTokenFont: 1551 { 1552 break; 1553 } 1554 case SVGTokenFontFamily: 1555 { 1556 SvgStringVector aSvgStringVector; 1557 1558 if(readSvgStringVector(aContent, aSvgStringVector)) 1559 { 1560 maFontFamily = aSvgStringVector; 1561 } 1562 break; 1563 } 1564 case SVGTokenFontSize: 1565 { 1566 if(!aContent.isEmpty()) 1567 { 1568 if(aContent.startsWith("xx-small")) 1569 { 1570 setFontSize(FontSize_xx_small); 1571 } 1572 else if(aContent.startsWith("x-small")) 1573 { 1574 setFontSize(FontSize_x_small); 1575 } 1576 else if(aContent.startsWith("small")) 1577 { 1578 setFontSize(FontSize_small); 1579 } 1580 else if(aContent.startsWith("smaller")) 1581 { 1582 setFontSize(FontSize_smaller); 1583 } 1584 else if(aContent.startsWith("medium")) 1585 { 1586 setFontSize(FontSize_medium); 1587 } 1588 else if(aContent.startsWith("larger")) 1589 { 1590 setFontSize(FontSize_larger); 1591 } 1592 else if(aContent.startsWith("large")) 1593 { 1594 setFontSize(FontSize_large); 1595 } 1596 else if(aContent.startsWith("x-large")) 1597 { 1598 setFontSize(FontSize_x_large); 1599 } 1600 else if(aContent.startsWith("xx-large")) 1601 { 1602 setFontSize(FontSize_xx_large); 1603 } 1604 else if(aContent.startsWith("initial")) 1605 { 1606 setFontSize(FontSize_initial); 1607 } 1608 else 1609 { 1610 SvgNumber aNum; 1611 1612 if(readSingleNumber(aContent, aNum)) 1613 { 1614 maFontSizeNumber = aNum; 1615 } 1616 } 1617 } 1618 break; 1619 } 1620 case SVGTokenFontSizeAdjust: 1621 { 1622 break; 1623 } 1624 case SVGTokenFontStretch: 1625 { 1626 if(!aContent.isEmpty()) 1627 { 1628 if(aContent.startsWith("normal")) 1629 { 1630 setFontStretch(FontStretch_normal); 1631 } 1632 else if(aContent.startsWith("wider")) 1633 { 1634 setFontStretch(FontStretch_wider); 1635 } 1636 else if(aContent.startsWith("narrower")) 1637 { 1638 setFontStretch(FontStretch_narrower); 1639 } 1640 else if(aContent.startsWith("ultra-condensed")) 1641 { 1642 setFontStretch(FontStretch_ultra_condensed); 1643 } 1644 else if(aContent.startsWith("extra-condensed")) 1645 { 1646 setFontStretch(FontStretch_extra_condensed); 1647 } 1648 else if(aContent.startsWith("condensed")) 1649 { 1650 setFontStretch(FontStretch_condensed); 1651 } 1652 else if(aContent.startsWith("semi-condensed")) 1653 { 1654 setFontStretch(FontStretch_semi_condensed); 1655 } 1656 else if(aContent.startsWith("semi-expanded")) 1657 { 1658 setFontStretch(FontStretch_semi_expanded); 1659 } 1660 else if(aContent.startsWith("expanded")) 1661 { 1662 setFontStretch(FontStretch_expanded); 1663 } 1664 else if(aContent.startsWith("extra-expanded")) 1665 { 1666 setFontStretch(FontStretch_extra_expanded); 1667 } 1668 else if(aContent.startsWith("ultra-expanded")) 1669 { 1670 setFontStretch(FontStretch_ultra_expanded); 1671 } 1672 } 1673 break; 1674 } 1675 case SVGTokenFontStyle: 1676 { 1677 if(!aContent.isEmpty()) 1678 { 1679 if(aContent.startsWith("normal")) 1680 { 1681 setFontStyle(FontStyle_normal); 1682 } 1683 else if(aContent.startsWith("italic")) 1684 { 1685 setFontStyle(FontStyle_italic); 1686 } 1687 else if(aContent.startsWith("oblique")) 1688 { 1689 setFontStyle(FontStyle_oblique); 1690 } 1691 } 1692 break; 1693 } 1694 case SVGTokenFontVariant: 1695 { 1696 break; 1697 } 1698 case SVGTokenFontWeight: 1699 { 1700 if(!aContent.isEmpty()) 1701 { 1702 if(aContent.startsWith("100")) 1703 { 1704 setFontWeight(FontWeight_100); 1705 } 1706 else if(aContent.startsWith("200")) 1707 { 1708 setFontWeight(FontWeight_200); 1709 } 1710 else if(aContent.startsWith("300")) 1711 { 1712 setFontWeight(FontWeight_300); 1713 } 1714 else if(aContent.startsWith("400") || aContent.startsWith("normal")) 1715 { 1716 setFontWeight(FontWeight_400); 1717 } 1718 else if(aContent.startsWith("500")) 1719 { 1720 setFontWeight(FontWeight_500); 1721 } 1722 else if(aContent.startsWith("600")) 1723 { 1724 setFontWeight(FontWeight_600); 1725 } 1726 else if(aContent.startsWith("700") || aContent.startsWith("bold")) 1727 { 1728 setFontWeight(FontWeight_700); 1729 } 1730 else if(aContent.startsWith("800")) 1731 { 1732 setFontWeight(FontWeight_800); 1733 } 1734 else if(aContent.startsWith("900")) 1735 { 1736 setFontWeight(FontWeight_900); 1737 } 1738 else if(aContent.startsWith("bolder")) 1739 { 1740 setFontWeight(FontWeight_bolder); 1741 } 1742 else if(aContent.startsWith("lighter")) 1743 { 1744 setFontWeight(FontWeight_lighter); 1745 } 1746 } 1747 break; 1748 } 1749 case SVGTokenDirection: 1750 { 1751 break; 1752 } 1753 case SVGTokenLetterSpacing: 1754 { 1755 break; 1756 } 1757 case SVGTokenTextDecoration: 1758 { 1759 if(!aContent.isEmpty()) 1760 { 1761 if(aContent.startsWith("none")) 1762 { 1763 setTextDecoration(TextDecoration_none); 1764 } 1765 else if(aContent.startsWith("underline")) 1766 { 1767 setTextDecoration(TextDecoration_underline); 1768 } 1769 else if(aContent.startsWith("overline")) 1770 { 1771 setTextDecoration(TextDecoration_overline); 1772 } 1773 else if(aContent.startsWith("line-through")) 1774 { 1775 setTextDecoration(TextDecoration_line_through); 1776 } 1777 else if(aContent.startsWith("blink")) 1778 { 1779 setTextDecoration(TextDecoration_blink); 1780 } 1781 } 1782 break; 1783 } 1784 case SVGTokenUnicodeBidi: 1785 { 1786 break; 1787 } 1788 case SVGTokenWordSpacing: 1789 { 1790 break; 1791 } 1792 case SVGTokenTextAnchor: 1793 { 1794 if(!aContent.isEmpty()) 1795 { 1796 if(aContent.startsWith("start")) 1797 { 1798 setTextAnchor(TextAnchor_start); 1799 } 1800 else if(aContent.startsWith("middle")) 1801 { 1802 setTextAnchor(TextAnchor_middle); 1803 } 1804 else if(aContent.startsWith("end")) 1805 { 1806 setTextAnchor(TextAnchor_end); 1807 } 1808 } 1809 break; 1810 } 1811 case SVGTokenTextAlign: 1812 { 1813 if(!aContent.isEmpty()) 1814 { 1815 if(aContent.startsWith("left")) 1816 { 1817 setTextAlign(TextAlign_left); 1818 } 1819 else if(aContent.startsWith("right")) 1820 { 1821 setTextAlign(TextAlign_right); 1822 } 1823 else if(aContent.startsWith("center")) 1824 { 1825 setTextAlign(TextAlign_center); 1826 } 1827 else if(aContent.startsWith("justify")) 1828 { 1829 setTextAlign(TextAlign_justify); 1830 } 1831 } 1832 break; 1833 } 1834 case SVGTokenColor: 1835 { 1836 SvgPaint aSvgPaint; 1837 OUString aURL; 1838 SvgNumber aOpacity; 1839 1840 if(readSvgPaint(aContent, aSvgPaint, aURL, bCaseIndependent, aOpacity)) 1841 { 1842 maColor = aSvgPaint; 1843 if(aOpacity.isSet()) 1844 { 1845 setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0))); 1846 } 1847 } 1848 break; 1849 } 1850 case SVGTokenOpacity: 1851 { 1852 SvgNumber aNum; 1853 1854 if(readSingleNumber(aContent, aNum)) 1855 { 1856 setOpacity(SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet())); 1857 } 1858 break; 1859 } 1860 case SVGTokenVisibility: 1861 { 1862 if(!aContent.isEmpty()) 1863 { 1864 if(aContent.startsWith("visible")) 1865 { 1866 setVisibility(Visibility_visible); 1867 } 1868 else if(aContent.startsWith("hidden")) 1869 { 1870 setVisibility(Visibility_hidden); 1871 } 1872 else if(aContent.startsWith("collapse")) 1873 { 1874 setVisibility(Visibility_collapse); 1875 } 1876 else if(aContent.startsWith("inherit")) 1877 { 1878 setVisibility(Visibility_inherit); 1879 } 1880 } 1881 break; 1882 } 1883 case SVGTokenTitle: 1884 { 1885 maTitle = aContent; 1886 break; 1887 } 1888 case SVGTokenDesc: 1889 { 1890 maDesc = aContent; 1891 break; 1892 } 1893 case SVGTokenClipPathProperty: 1894 { 1895 readLocalUrl(aContent, maClipPathXLink); 1896 break; 1897 } 1898 case SVGTokenMask: 1899 { 1900 readLocalUrl(aContent, maMaskXLink); 1901 break; 1902 } 1903 case SVGTokenClipRule: 1904 { 1905 if(!aContent.isEmpty()) 1906 { 1907 if(aContent.match(commonStrings::aStrNonzero)) 1908 { 1909 maClipRule = FillRule_nonzero; 1910 } 1911 else if(aContent.match(commonStrings::aStrEvenOdd)) 1912 { 1913 maClipRule = FillRule_evenodd; 1914 } 1915 } 1916 break; 1917 } 1918 case SVGTokenMarker: 1919 { 1920 if(bCaseIndependent) 1921 { 1922 readLocalUrl(aContent, maMarkerEndXLink); 1923 maMarkerStartXLink = maMarkerMidXLink = maMarkerEndXLink; 1924 } 1925 break; 1926 } 1927 case SVGTokenMarkerStart: 1928 { 1929 readLocalUrl(aContent, maMarkerStartXLink); 1930 break; 1931 } 1932 case SVGTokenMarkerMid: 1933 { 1934 readLocalUrl(aContent, maMarkerMidXLink); 1935 break; 1936 } 1937 case SVGTokenMarkerEnd: 1938 { 1939 readLocalUrl(aContent, maMarkerEndXLink); 1940 break; 1941 } 1942 case SVGTokenDisplay: 1943 { 1944 // There may be display:none statements inside of style defines, e.g. the following line: 1945 // style="display:none" 1946 // taken from a svg example; this needs to be parsed and set at the owning node. Do not call 1947 // mrOwner.parseAttribute(...) here, this would lead to a recursion 1948 if(!aContent.isEmpty()) 1949 { 1950 mrOwner.setDisplay(getDisplayFromContent(aContent)); 1951 } 1952 break; 1953 } 1954 case SVGTokenBaselineShift: 1955 { 1956 if(!aContent.isEmpty()) 1957 { 1958 SvgNumber aNum; 1959 1960 if(aContent.startsWith("sub")) 1961 { 1962 setBaselineShift(BaselineShift_Sub); 1963 } 1964 else if(aContent.startsWith("super")) 1965 { 1966 setBaselineShift(BaselineShift_Super); 1967 } 1968 else if(readSingleNumber(aContent, aNum)) 1969 { 1970 maBaselineShiftNumber = aNum; 1971 1972 if(Unit_percent == aNum.getUnit()) 1973 { 1974 setBaselineShift(BaselineShift_Percentage); 1975 } 1976 else 1977 { 1978 setBaselineShift(BaselineShift_Length); 1979 } 1980 } 1981 else 1982 { 1983 // no BaselineShift or inherit (which is automatically) 1984 setBaselineShift(BaselineShift_Baseline); 1985 } 1986 } 1987 break; 1988 } 1989 default: 1990 { 1991 break; 1992 } 1993 } 1994 } 1995 1996 // #i125258# ask if fill is a direct hard attribute (no hierarchy) 1997 bool SvgStyleAttributes::isFillSet() const 1998 { 1999 if(mbIsClipPathContent) 2000 { 2001 return false; 2002 } 2003 else if(maFill.isSet()) 2004 { 2005 return true; 2006 } 2007 2008 return false; 2009 } 2010 2011 const basegfx::BColor* SvgStyleAttributes::getCurrentColor() const 2012 { 2013 static basegfx::BColor aBlack(0.0, 0.0, 0.0); 2014 const basegfx::BColor *aColor = getColor(); 2015 if( aColor ) 2016 return aColor; 2017 else 2018 return &aBlack; 2019 } 2020 2021 const basegfx::BColor* SvgStyleAttributes::getFill() const 2022 { 2023 if(maFill.isSet()) 2024 { 2025 if(maFill.isCurrent()) 2026 { 2027 return getCurrentColor(); 2028 } 2029 else if(maFill.isOn()) 2030 { 2031 return &maFill.getBColor(); 2032 } 2033 } 2034 else if (!mpSvgGradientNodeFill && !mpSvgPatternNodeFill) 2035 { 2036 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2037 2038 if (pSvgStyleAttributes && maResolvingParent[0] < nStyleDepthLimit) 2039 { 2040 ++maResolvingParent[0]; 2041 const basegfx::BColor* pFill = pSvgStyleAttributes->getFill(); 2042 --maResolvingParent[0]; 2043 2044 if(mbIsClipPathContent) 2045 { 2046 if (pFill) 2047 { 2048 return pFill; 2049 } 2050 else 2051 { 2052 static basegfx::BColor aBlack(0.0, 0.0, 0.0); 2053 return &aBlack; 2054 } 2055 } 2056 else 2057 { 2058 return pFill; 2059 } 2060 } 2061 } 2062 2063 return nullptr; 2064 } 2065 2066 const basegfx::BColor* SvgStyleAttributes::getStroke() const 2067 { 2068 if(maStroke.isSet()) 2069 { 2070 if(maStroke.isCurrent()) 2071 { 2072 return getCurrentColor(); 2073 } 2074 else if(maStroke.isOn()) 2075 { 2076 return &maStroke.getBColor(); 2077 } 2078 } 2079 else if (!mpSvgGradientNodeStroke && !mpSvgPatternNodeStroke) 2080 { 2081 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2082 2083 if (pSvgStyleAttributes && maResolvingParent[1] < nStyleDepthLimit) 2084 { 2085 ++maResolvingParent[1]; 2086 auto ret = pSvgStyleAttributes->getStroke(); 2087 --maResolvingParent[1]; 2088 return ret; 2089 } 2090 } 2091 2092 return nullptr; 2093 } 2094 2095 const basegfx::BColor& SvgStyleAttributes::getStopColor() const 2096 { 2097 if(maStopColor.isCurrent()) 2098 { 2099 return *getCurrentColor(); 2100 } 2101 else 2102 { 2103 return maStopColor.getBColor(); 2104 } 2105 } 2106 2107 const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeFill() const 2108 { 2109 if(mpSvgGradientNodeFill) 2110 { 2111 return mpSvgGradientNodeFill; 2112 } 2113 else if (!maFill.isSet() && !mpSvgPatternNodeFill) 2114 { 2115 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2116 2117 if (pSvgStyleAttributes && maResolvingParent[2] < nStyleDepthLimit) 2118 { 2119 ++maResolvingParent[2]; 2120 auto ret = pSvgStyleAttributes->getSvgGradientNodeFill(); 2121 --maResolvingParent[2]; 2122 return ret; 2123 } 2124 } 2125 2126 return nullptr; 2127 } 2128 2129 const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeStroke() const 2130 { 2131 if(mpSvgGradientNodeStroke) 2132 { 2133 return mpSvgGradientNodeStroke; 2134 } 2135 else if (!maStroke.isSet() && !mpSvgPatternNodeStroke) 2136 { 2137 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2138 2139 if (pSvgStyleAttributes && maResolvingParent[3] < nStyleDepthLimit) 2140 { 2141 ++maResolvingParent[3]; 2142 auto ret = pSvgStyleAttributes->getSvgGradientNodeStroke(); 2143 --maResolvingParent[3]; 2144 return ret; 2145 } 2146 } 2147 2148 return nullptr; 2149 } 2150 2151 const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeFill() const 2152 { 2153 if(mpSvgPatternNodeFill) 2154 { 2155 return mpSvgPatternNodeFill; 2156 } 2157 else if (!maFill.isSet() && !mpSvgGradientNodeFill) 2158 { 2159 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2160 2161 if (pSvgStyleAttributes && maResolvingParent[4] < nStyleDepthLimit) 2162 { 2163 ++maResolvingParent[4]; 2164 auto ret = pSvgStyleAttributes->getSvgPatternNodeFill(); 2165 --maResolvingParent[4]; 2166 return ret; 2167 } 2168 } 2169 2170 return nullptr; 2171 } 2172 2173 const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeStroke() const 2174 { 2175 if(mpSvgPatternNodeStroke) 2176 { 2177 return mpSvgPatternNodeStroke; 2178 } 2179 else if (!maStroke.isSet() && !mpSvgGradientNodeStroke) 2180 { 2181 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2182 2183 if (pSvgStyleAttributes && maResolvingParent[5] < nStyleDepthLimit) 2184 { 2185 ++maResolvingParent[5]; 2186 auto ret = pSvgStyleAttributes->getSvgPatternNodeStroke(); 2187 --maResolvingParent[5]; 2188 return ret; 2189 } 2190 } 2191 2192 return nullptr; 2193 } 2194 2195 SvgNumber SvgStyleAttributes::getStrokeWidth() const 2196 { 2197 if(maStrokeWidth.isSet()) 2198 { 2199 return maStrokeWidth; 2200 } 2201 2202 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2203 2204 if (pSvgStyleAttributes && maResolvingParent[6] < nStyleDepthLimit) 2205 { 2206 ++maResolvingParent[6]; 2207 auto ret = pSvgStyleAttributes->getStrokeWidth(); 2208 --maResolvingParent[6]; 2209 return ret; 2210 } 2211 2212 if(mbIsClipPathContent) 2213 { 2214 return SvgNumber(0.0); 2215 } 2216 2217 // default is 1 2218 return SvgNumber(1.0); 2219 } 2220 2221 SvgNumber SvgStyleAttributes::getStopOpacity() const 2222 { 2223 if(maStopOpacity.isSet()) 2224 { 2225 return maStopOpacity; 2226 } 2227 2228 // default is 1 2229 return SvgNumber(1.0); 2230 } 2231 2232 SvgNumber SvgStyleAttributes::getFillOpacity() const 2233 { 2234 if(maFillOpacity.isSet()) 2235 { 2236 return maFillOpacity; 2237 } 2238 2239 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2240 2241 if (pSvgStyleAttributes && maResolvingParent[7] < nStyleDepthLimit) 2242 { 2243 ++maResolvingParent[7]; 2244 auto ret = pSvgStyleAttributes->getFillOpacity(); 2245 --maResolvingParent[7]; 2246 return ret; 2247 } 2248 2249 // default is 1 2250 return SvgNumber(1.0); 2251 } 2252 2253 SvgNumber SvgStyleAttributes::getOpacity() const 2254 { 2255 if(maOpacity.isSet()) 2256 { 2257 return maOpacity; 2258 } 2259 2260 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2261 2262 if (pSvgStyleAttributes && maResolvingParent[8] < nStyleDepthLimit) 2263 { 2264 ++maResolvingParent[8]; 2265 auto ret = pSvgStyleAttributes->getOpacity(); 2266 --maResolvingParent[8]; 2267 return ret; 2268 } 2269 2270 // default is 1 2271 return SvgNumber(1.0); 2272 } 2273 2274 Visibility SvgStyleAttributes::getVisibility() const 2275 { 2276 if(Visibility_notset == maVisibility || Visibility_inherit == maVisibility) 2277 { 2278 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2279 2280 if (pSvgStyleAttributes && maResolvingParent[9] < nStyleDepthLimit) 2281 { 2282 ++maResolvingParent[9]; 2283 auto ret = pSvgStyleAttributes->getVisibility(); 2284 --maResolvingParent[9]; 2285 return ret; 2286 } 2287 //default is Visible 2288 return Visibility_visible; 2289 } 2290 2291 // Visibility correction/exception for self-exported SVGs: 2292 // When Impress exports single or multi-page SVGs, it puts the 2293 // single slides into <g visibility="hidden">. Not sure why 2294 // this happens, but this leads (correctly) to empty imported 2295 // Graphics. 2296 // Thus, if Visibility_hidden is active and owner is a SVGTokenG 2297 // and it's parent is also a SVGTokenG and it has a Class 'SlideGroup' 2298 // set, check if we are an Impress export. 2299 // We are an Impress export if an SVG-Node titled 'ooo:meta_slides' 2300 // exists. 2301 // All together gives: 2302 if(Visibility_hidden == maVisibility 2303 && SVGTokenG == mrOwner.getType() 2304 && nullptr != mrOwner.getDocument().findSvgNodeById("ooo:meta_slides")) 2305 { 2306 const SvgNode* pParent(mrOwner.getParent()); 2307 2308 if(nullptr != pParent && SVGTokenG == pParent->getType() && pParent->getClass()) 2309 { 2310 const OUString aClass(*pParent->getClass()); 2311 2312 if("SlideGroup" == aClass) 2313 { 2314 // if we detect this exception, 2315 // override Visibility_hidden -> Visibility_visible 2316 return Visibility_visible; 2317 } 2318 } 2319 } 2320 2321 return maVisibility; 2322 } 2323 2324 FillRule SvgStyleAttributes::getFillRule() const 2325 { 2326 if(FillRule_notset != maFillRule) 2327 { 2328 return maFillRule; 2329 } 2330 2331 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2332 2333 if (pSvgStyleAttributes && maResolvingParent[10] < nStyleDepthLimit) 2334 { 2335 ++maResolvingParent[10]; 2336 auto ret = pSvgStyleAttributes->getFillRule(); 2337 --maResolvingParent[10]; 2338 return ret; 2339 } 2340 2341 // default is NonZero 2342 return FillRule_nonzero; 2343 } 2344 2345 const SvgNumberVector& SvgStyleAttributes::getStrokeDasharray() const 2346 { 2347 if(!maStrokeDasharray.empty()) 2348 { 2349 return maStrokeDasharray; 2350 } 2351 else if(mbStrokeDasharraySet) 2352 { 2353 // #121221# is set to empty *by purpose*, do not visit parent styles 2354 return maStrokeDasharray; 2355 } 2356 2357 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2358 2359 if (pSvgStyleAttributes && maResolvingParent[11] < nStyleDepthLimit) 2360 { 2361 ++maResolvingParent[11]; 2362 const SvgNumberVector& ret = pSvgStyleAttributes->getStrokeDasharray(); 2363 --maResolvingParent[11]; 2364 return ret; 2365 } 2366 2367 // default empty 2368 return maStrokeDasharray; 2369 } 2370 2371 SvgNumber SvgStyleAttributes::getStrokeDashOffset() const 2372 { 2373 if(maStrokeDashOffset.isSet()) 2374 { 2375 return maStrokeDashOffset; 2376 } 2377 2378 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2379 2380 if (pSvgStyleAttributes && maResolvingParent[12] < nStyleDepthLimit) 2381 { 2382 ++maResolvingParent[12]; 2383 auto ret = pSvgStyleAttributes->getStrokeDashOffset(); 2384 --maResolvingParent[12]; 2385 return ret; 2386 } 2387 2388 // default is 0 2389 return SvgNumber(0.0); 2390 } 2391 2392 StrokeLinecap SvgStyleAttributes::getStrokeLinecap() const 2393 { 2394 if(maStrokeLinecap != StrokeLinecap_notset) 2395 { 2396 return maStrokeLinecap; 2397 } 2398 2399 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2400 2401 if (pSvgStyleAttributes && maResolvingParent[13] < nStyleDepthLimit) 2402 { 2403 ++maResolvingParent[13]; 2404 auto ret = pSvgStyleAttributes->getStrokeLinecap(); 2405 --maResolvingParent[13]; 2406 return ret; 2407 } 2408 2409 // default is StrokeLinecap_butt 2410 return StrokeLinecap_butt; 2411 } 2412 2413 StrokeLinejoin SvgStyleAttributes::getStrokeLinejoin() const 2414 { 2415 if(maStrokeLinejoin != StrokeLinejoin_notset) 2416 { 2417 return maStrokeLinejoin; 2418 } 2419 2420 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2421 2422 if (pSvgStyleAttributes && maResolvingParent[14] < nStyleDepthLimit) 2423 { 2424 ++maResolvingParent[14]; 2425 auto ret = pSvgStyleAttributes->getStrokeLinejoin(); 2426 --maResolvingParent[14]; 2427 return ret; 2428 } 2429 2430 // default is StrokeLinejoin_butt 2431 return StrokeLinejoin_miter; 2432 } 2433 2434 SvgNumber SvgStyleAttributes::getStrokeMiterLimit() const 2435 { 2436 if(maStrokeMiterLimit.isSet()) 2437 { 2438 return maStrokeMiterLimit; 2439 } 2440 2441 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2442 2443 if (pSvgStyleAttributes && maResolvingParent[15] < nStyleDepthLimit) 2444 { 2445 ++maResolvingParent[15]; 2446 auto ret = pSvgStyleAttributes->getStrokeMiterLimit(); 2447 --maResolvingParent[15]; 2448 return ret; 2449 } 2450 2451 // default is 4 2452 return SvgNumber(4.0, Unit_none); 2453 } 2454 2455 SvgNumber SvgStyleAttributes::getStrokeOpacity() const 2456 { 2457 if(maStrokeOpacity.isSet()) 2458 { 2459 return maStrokeOpacity; 2460 } 2461 2462 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2463 2464 if (pSvgStyleAttributes && maResolvingParent[16] < nStyleDepthLimit) 2465 { 2466 ++maResolvingParent[16]; 2467 auto ret = pSvgStyleAttributes->getStrokeOpacity(); 2468 --maResolvingParent[16]; 2469 return ret; 2470 } 2471 2472 // default is 1 2473 return SvgNumber(1.0); 2474 } 2475 2476 const SvgStringVector& SvgStyleAttributes::getFontFamily() const 2477 { 2478 if(!maFontFamily.empty() && !maFontFamily[0].startsWith("inherit")) 2479 { 2480 return maFontFamily; 2481 } 2482 2483 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2484 2485 if (pSvgStyleAttributes && maResolvingParent[17] < nStyleDepthLimit) 2486 { 2487 ++maResolvingParent[17]; 2488 const SvgStringVector& ret = pSvgStyleAttributes->getFontFamily(); 2489 --maResolvingParent[17]; 2490 return ret; 2491 } 2492 2493 // default is empty 2494 return maFontFamily; 2495 } 2496 2497 SvgNumber SvgStyleAttributes::getFontSizeNumber() const 2498 { 2499 // default size is 'medium' or 16px, which is equal to the default PPI used in svgio ( 96.0 ) 2500 // converted to pixels 2501 const double aDefaultSize = F_SVG_PIXEL_PER_INCH / 6.0; 2502 2503 if(maFontSizeNumber.isSet()) 2504 { 2505 if(!maFontSizeNumber.isPositive()) 2506 return aDefaultSize; 2507 2508 // #122524# Handle Unit_percent relative to parent FontSize (see SVG1.1 2509 // spec 10.10 Font selection properties \91font-size\92, lastline (click 'normative 2510 // definition of the property') 2511 if(Unit_percent == maFontSizeNumber.getUnit()) 2512 { 2513 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2514 2515 if(pSvgStyleAttributes) 2516 { 2517 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber(); 2518 2519 return SvgNumber( 2520 aParentNumber.getNumber() * maFontSizeNumber.getNumber() * 0.01, 2521 aParentNumber.getUnit(), 2522 true); 2523 } 2524 // if there's no parent style, set the font size based on the default size 2525 // 100% = 16px 2526 return SvgNumber( 2527 maFontSizeNumber.getNumber() * aDefaultSize / 100.0, Unit_px, true); 2528 } 2529 else if((Unit_em == maFontSizeNumber.getUnit()) || (Unit_ex == maFontSizeNumber.getUnit())) 2530 { 2531 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2532 2533 if(pSvgStyleAttributes) 2534 { 2535 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber(); 2536 2537 return SvgNumber( 2538 aParentNumber.getNumber() * maFontSizeNumber.getNumber(), 2539 aParentNumber.getUnit(), 2540 true); 2541 } 2542 } 2543 2544 return maFontSizeNumber; 2545 } 2546 2547 //In CSS2, the suggested scaling factor between adjacent indexes is 1.2 2548 switch(maFontSize) 2549 { 2550 case FontSize_notset: 2551 break; 2552 case FontSize_xx_small: 2553 { 2554 return SvgNumber(aDefaultSize / 1.728); 2555 } 2556 case FontSize_x_small: 2557 { 2558 return SvgNumber(aDefaultSize / 1.44); 2559 } 2560 case FontSize_small: 2561 { 2562 return SvgNumber(aDefaultSize / 1.2); 2563 } 2564 case FontSize_smaller: 2565 { 2566 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2567 if(pSvgStyleAttributes) 2568 { 2569 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber(); 2570 return SvgNumber(aParentNumber.getNumber() / 1.2, aParentNumber.getUnit()); 2571 } 2572 [[fallthrough]]; 2573 } 2574 case FontSize_medium: 2575 case FontSize_initial: 2576 { 2577 return SvgNumber(aDefaultSize); 2578 } 2579 case FontSize_large: 2580 { 2581 return SvgNumber(aDefaultSize * 1.2); 2582 } 2583 case FontSize_larger: 2584 { 2585 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2586 if(pSvgStyleAttributes) 2587 { 2588 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber(); 2589 return SvgNumber(aParentNumber.getNumber() * 1.2, aParentNumber.getUnit()); 2590 } 2591 [[fallthrough]]; 2592 } 2593 case FontSize_x_large: 2594 { 2595 return SvgNumber(aDefaultSize * 1.44); 2596 } 2597 case FontSize_xx_large: 2598 { 2599 return SvgNumber(aDefaultSize * 1.728); 2600 } 2601 } 2602 2603 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2604 2605 if(pSvgStyleAttributes) 2606 { 2607 return pSvgStyleAttributes->getFontSizeNumber(); 2608 } 2609 2610 return SvgNumber(aDefaultSize); 2611 } 2612 2613 FontStretch SvgStyleAttributes::getFontStretch() const 2614 { 2615 if(maFontStretch != FontStretch_notset) 2616 { 2617 if(FontStretch_wider != maFontStretch && FontStretch_narrower != maFontStretch) 2618 { 2619 return maFontStretch; 2620 } 2621 } 2622 2623 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2624 2625 if (pSvgStyleAttributes && maResolvingParent[18] < nStyleDepthLimit) 2626 { 2627 ++maResolvingParent[18]; 2628 FontStretch aInherited = pSvgStyleAttributes->getFontStretch(); 2629 --maResolvingParent[18]; 2630 2631 if(FontStretch_wider == maFontStretch) 2632 { 2633 aInherited = getWider(aInherited); 2634 } 2635 else if(FontStretch_narrower == maFontStretch) 2636 { 2637 aInherited = getNarrower(aInherited); 2638 } 2639 2640 return aInherited; 2641 } 2642 2643 // default is FontStretch_normal 2644 return FontStretch_normal; 2645 } 2646 2647 FontStyle SvgStyleAttributes::getFontStyle() const 2648 { 2649 if(maFontStyle != FontStyle_notset) 2650 { 2651 return maFontStyle; 2652 } 2653 2654 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2655 2656 if (pSvgStyleAttributes && maResolvingParent[19] < nStyleDepthLimit) 2657 { 2658 ++maResolvingParent[19]; 2659 auto ret = pSvgStyleAttributes->getFontStyle(); 2660 --maResolvingParent[19]; 2661 return ret; 2662 } 2663 2664 // default is FontStyle_normal 2665 return FontStyle_normal; 2666 } 2667 2668 FontWeight SvgStyleAttributes::getFontWeight() const 2669 { 2670 if(maFontWeight != FontWeight_notset) 2671 { 2672 if(FontWeight_bolder != maFontWeight && FontWeight_lighter != maFontWeight) 2673 { 2674 return maFontWeight; 2675 } 2676 } 2677 2678 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2679 2680 if (pSvgStyleAttributes && maResolvingParent[20] < nStyleDepthLimit) 2681 { 2682 ++maResolvingParent[20]; 2683 FontWeight aInherited = pSvgStyleAttributes->getFontWeight(); 2684 --maResolvingParent[20]; 2685 2686 if(FontWeight_bolder == maFontWeight) 2687 { 2688 aInherited = getBolder(aInherited); 2689 } 2690 else if(FontWeight_lighter == maFontWeight) 2691 { 2692 aInherited = getLighter(aInherited); 2693 } 2694 2695 return aInherited; 2696 } 2697 2698 // default is FontWeight_400 (FontWeight_normal) 2699 return FontWeight_400; 2700 } 2701 2702 TextAlign SvgStyleAttributes::getTextAlign() const 2703 { 2704 if(maTextAlign != TextAlign_notset) 2705 { 2706 return maTextAlign; 2707 } 2708 2709 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2710 2711 if (pSvgStyleAttributes && maResolvingParent[21] < nStyleDepthLimit) 2712 { 2713 ++maResolvingParent[21]; 2714 auto ret = pSvgStyleAttributes->getTextAlign(); 2715 --maResolvingParent[21]; 2716 return ret; 2717 } 2718 2719 // default is TextAlign_left 2720 return TextAlign_left; 2721 } 2722 2723 const SvgStyleAttributes* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const 2724 { 2725 if(maTextDecoration != TextDecoration_notset) 2726 { 2727 return this; 2728 } 2729 2730 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2731 2732 if (pSvgStyleAttributes && maResolvingParent[22] < nStyleDepthLimit) 2733 { 2734 ++maResolvingParent[22]; 2735 auto ret = pSvgStyleAttributes->getTextDecorationDefiningSvgStyleAttributes(); 2736 --maResolvingParent[22]; 2737 return ret; 2738 } 2739 2740 // default is 0 2741 return nullptr; 2742 } 2743 2744 TextDecoration SvgStyleAttributes::getTextDecoration() const 2745 { 2746 const SvgStyleAttributes* pDefining = getTextDecorationDefiningSvgStyleAttributes(); 2747 2748 if(pDefining) 2749 { 2750 return pDefining->maTextDecoration; 2751 } 2752 else 2753 { 2754 // default is TextDecoration_none 2755 return TextDecoration_none; 2756 } 2757 } 2758 2759 TextAnchor SvgStyleAttributes::getTextAnchor() const 2760 { 2761 if(maTextAnchor != TextAnchor_notset) 2762 { 2763 return maTextAnchor; 2764 } 2765 2766 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2767 2768 if (pSvgStyleAttributes && maResolvingParent[23] < nStyleDepthLimit) 2769 { 2770 ++maResolvingParent[23]; 2771 auto ret = pSvgStyleAttributes->getTextAnchor(); 2772 --maResolvingParent[23]; 2773 return ret; 2774 } 2775 2776 // default is TextAnchor_start 2777 return TextAnchor_start; 2778 } 2779 2780 const basegfx::BColor* SvgStyleAttributes::getColor() const 2781 { 2782 if(maColor.isSet()) 2783 { 2784 if(maColor.isCurrent()) 2785 { 2786 OSL_ENSURE(false, "Svg error: current color uses current color (!)"); 2787 return nullptr; 2788 } 2789 else if(maColor.isOn()) 2790 { 2791 return &maColor.getBColor(); 2792 } 2793 } 2794 else 2795 { 2796 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2797 2798 if (pSvgStyleAttributes && maResolvingParent[24] < nStyleDepthLimit) 2799 { 2800 ++maResolvingParent[24]; 2801 auto ret = pSvgStyleAttributes->getColor(); 2802 --maResolvingParent[24]; 2803 return ret; 2804 } 2805 } 2806 2807 return nullptr; 2808 } 2809 2810 OUString const & SvgStyleAttributes::getClipPathXLink() const 2811 { 2812 return maClipPathXLink; 2813 } 2814 2815 const SvgClipPathNode* SvgStyleAttributes::accessClipPathXLink() const 2816 { 2817 if(!mpClipPathXLink) 2818 { 2819 const OUString aClipPath(getClipPathXLink()); 2820 2821 if(!aClipPath.isEmpty()) 2822 { 2823 const_cast< SvgStyleAttributes* >(this)->mpClipPathXLink = dynamic_cast< const SvgClipPathNode* >(mrOwner.getDocument().findSvgNodeById(aClipPath)); 2824 } 2825 } 2826 2827 return mpClipPathXLink; 2828 } 2829 2830 OUString SvgStyleAttributes::getMaskXLink() const 2831 { 2832 if(!maMaskXLink.isEmpty()) 2833 { 2834 return maMaskXLink; 2835 } 2836 2837 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2838 2839 if (pSvgStyleAttributes && !pSvgStyleAttributes->maMaskXLink.isEmpty() && maResolvingParent[25] < nStyleDepthLimit) 2840 { 2841 ++maResolvingParent[25]; 2842 auto ret = pSvgStyleAttributes->getMaskXLink(); 2843 --maResolvingParent[25]; 2844 return ret; 2845 } 2846 2847 return OUString(); 2848 } 2849 2850 const SvgMaskNode* SvgStyleAttributes::accessMaskXLink() const 2851 { 2852 if(!mpMaskXLink) 2853 { 2854 const OUString aMask(getMaskXLink()); 2855 2856 if(!aMask.isEmpty()) 2857 { 2858 const_cast< SvgStyleAttributes* >(this)->mpMaskXLink = dynamic_cast< const SvgMaskNode* >(mrOwner.getDocument().findSvgNodeById(aMask)); 2859 } 2860 } 2861 2862 return mpMaskXLink; 2863 } 2864 2865 OUString SvgStyleAttributes::getMarkerStartXLink() const 2866 { 2867 if(!maMarkerStartXLink.isEmpty()) 2868 { 2869 return maMarkerStartXLink; 2870 } 2871 2872 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2873 2874 if (pSvgStyleAttributes && maResolvingParent[26] < nStyleDepthLimit) 2875 { 2876 ++maResolvingParent[26]; 2877 auto ret = pSvgStyleAttributes->getMarkerStartXLink(); 2878 --maResolvingParent[26]; 2879 return ret; 2880 } 2881 2882 return OUString(); 2883 } 2884 2885 const SvgMarkerNode* SvgStyleAttributes::accessMarkerStartXLink() const 2886 { 2887 if(!mpMarkerStartXLink) 2888 { 2889 const OUString aMarker(getMarkerStartXLink()); 2890 2891 if(!aMarker.isEmpty()) 2892 { 2893 const_cast< SvgStyleAttributes* >(this)->mpMarkerStartXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerStartXLink())); 2894 } 2895 } 2896 2897 return mpMarkerStartXLink; 2898 } 2899 2900 OUString SvgStyleAttributes::getMarkerMidXLink() const 2901 { 2902 if(!maMarkerMidXLink.isEmpty()) 2903 { 2904 return maMarkerMidXLink; 2905 } 2906 2907 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2908 2909 if (pSvgStyleAttributes && maResolvingParent[27] < nStyleDepthLimit) 2910 { 2911 ++maResolvingParent[27]; 2912 auto ret = pSvgStyleAttributes->getMarkerMidXLink(); 2913 --maResolvingParent[27]; 2914 return ret; 2915 } 2916 2917 return OUString(); 2918 } 2919 2920 const SvgMarkerNode* SvgStyleAttributes::accessMarkerMidXLink() const 2921 { 2922 if(!mpMarkerMidXLink) 2923 { 2924 const OUString aMarker(getMarkerMidXLink()); 2925 2926 if(!aMarker.isEmpty()) 2927 { 2928 const_cast< SvgStyleAttributes* >(this)->mpMarkerMidXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerMidXLink())); 2929 } 2930 } 2931 2932 return mpMarkerMidXLink; 2933 } 2934 2935 OUString SvgStyleAttributes::getMarkerEndXLink() const 2936 { 2937 if(!maMarkerEndXLink.isEmpty()) 2938 { 2939 return maMarkerEndXLink; 2940 } 2941 2942 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2943 2944 if (pSvgStyleAttributes && maResolvingParent[28] < nStyleDepthLimit) 2945 { 2946 ++maResolvingParent[28]; 2947 auto ret = pSvgStyleAttributes->getMarkerEndXLink(); 2948 --maResolvingParent[28]; 2949 return ret; 2950 } 2951 2952 return OUString(); 2953 } 2954 2955 const SvgMarkerNode* SvgStyleAttributes::accessMarkerEndXLink() const 2956 { 2957 if(!mpMarkerEndXLink) 2958 { 2959 const OUString aMarker(getMarkerEndXLink()); 2960 2961 if(!aMarker.isEmpty()) 2962 { 2963 const_cast< SvgStyleAttributes* >(this)->mpMarkerEndXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerEndXLink())); 2964 } 2965 } 2966 2967 return mpMarkerEndXLink; 2968 } 2969 2970 SvgNumber SvgStyleAttributes::getBaselineShiftNumber() const 2971 { 2972 // #122524# Handle Unit_percent relative to parent BaselineShift 2973 if(Unit_percent == maBaselineShiftNumber.getUnit()) 2974 { 2975 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2976 2977 if (pSvgStyleAttributes && maResolvingParent[29] < nStyleDepthLimit) 2978 { 2979 ++maResolvingParent[29]; 2980 const SvgNumber aParentNumber = pSvgStyleAttributes->getBaselineShiftNumber(); 2981 --maResolvingParent[29]; 2982 2983 return SvgNumber( 2984 aParentNumber.getNumber() * maBaselineShiftNumber.getNumber() * 0.01, 2985 aParentNumber.getUnit(), 2986 true); 2987 } 2988 } 2989 2990 return maBaselineShiftNumber; 2991 } 2992 } // end of namespace svgreader 2993 } // end of namespace svgio 2994 2995 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2996 2997
