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/svgstyleattributes.hxx> 26 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 27 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> 28 #include <svgio/svgreader/svgnode.hxx> 29 #include <svgio/svgreader/svgdocument.hxx> 30 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> 31 #include <svgio/svgreader/svggradientnode.hxx> 32 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 33 #include <basegfx/vector/b2enums.hxx> 34 #include <drawinglayer/processor2d/linegeometryextractor2d.hxx> 35 #include <drawinglayer/processor2d/textaspolygonextractor2d.hxx> 36 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 37 #include <svgio/svgreader/svgclippathnode.hxx> 38 #include <svgio/svgreader/svgmasknode.hxx> 39 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 40 #include <basegfx/polygon/b2dpolypolygontools.hxx> 41 #include <svgio/svgreader/svgmarkernode.hxx> 42 #include <basegfx/curve/b2dcubicbezier.hxx> 43 #include <svgio/svgreader/svgpatternnode.hxx> 44 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx> 45 #include <basegfx/polygon/b2dpolygontools.hxx> 46 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 47 48 ////////////////////////////////////////////////////////////////////////////// 49 50 namespace svgio 51 { 52 namespace svgreader 53 { 54 basegfx::B2DLineJoin StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin) 55 { 56 if(StrokeLinejoin_round == aStrokeLinejoin) 57 { 58 return basegfx::B2DLINEJOIN_ROUND; 59 } 60 else if(StrokeLinejoin_bevel == aStrokeLinejoin) 61 { 62 return basegfx::B2DLINEJOIN_BEVEL; 63 } 64 65 return basegfx::B2DLINEJOIN_MITER; 66 } 67 68 com::sun::star::drawing::LineCap StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap) 69 { 70 switch(aStrokeLinecap) 71 { 72 default: /* StrokeLinecap_notset, StrokeLinecap_butt */ 73 { 74 return com::sun::star::drawing::LineCap_BUTT; 75 break; 76 } 77 case StrokeLinecap_round: 78 { 79 return com::sun::star::drawing::LineCap_ROUND; 80 break; 81 } 82 case StrokeLinecap_square: 83 { 84 return com::sun::star::drawing::LineCap_SQUARE; 85 break; 86 } 87 } 88 } 89 90 FontStretch getWider(FontStretch aSource) 91 { 92 switch(aSource) 93 { 94 case FontStretch_ultra_condensed: aSource = FontStretch_extra_condensed; break; 95 case FontStretch_extra_condensed: aSource = FontStretch_condensed; break; 96 case FontStretch_condensed: aSource = FontStretch_semi_condensed; break; 97 case FontStretch_semi_condensed: aSource = FontStretch_normal; break; 98 case FontStretch_normal: aSource = FontStretch_semi_expanded; break; 99 case FontStretch_semi_expanded: aSource = FontStretch_expanded; break; 100 case FontStretch_expanded: aSource = FontStretch_extra_expanded; break; 101 case FontStretch_extra_expanded: aSource = FontStretch_ultra_expanded; break; 102 default: break; 103 } 104 105 return aSource; 106 } 107 108 FontStretch getNarrower(FontStretch aSource) 109 { 110 switch(aSource) 111 { 112 case FontStretch_extra_condensed: aSource = FontStretch_ultra_condensed; break; 113 case FontStretch_condensed: aSource = FontStretch_extra_condensed; break; 114 case FontStretch_semi_condensed: aSource = FontStretch_condensed; break; 115 case FontStretch_normal: aSource = FontStretch_semi_condensed; break; 116 case FontStretch_semi_expanded: aSource = FontStretch_normal; break; 117 case FontStretch_expanded: aSource = FontStretch_semi_expanded; break; 118 case FontStretch_extra_expanded: aSource = FontStretch_expanded; break; 119 case FontStretch_ultra_expanded: aSource = FontStretch_extra_expanded; break; 120 default: break; 121 } 122 123 return aSource; 124 } 125 126 FontWeight getBolder(FontWeight aSource) 127 { 128 switch(aSource) 129 { 130 case FontWeight_100: aSource = FontWeight_200; break; 131 case FontWeight_200: aSource = FontWeight_300; break; 132 case FontWeight_300: aSource = FontWeight_400; break; 133 case FontWeight_400: aSource = FontWeight_500; break; 134 case FontWeight_500: aSource = FontWeight_600; break; 135 case FontWeight_600: aSource = FontWeight_700; break; 136 case FontWeight_700: aSource = FontWeight_800; break; 137 case FontWeight_800: aSource = FontWeight_900; break; 138 default: break; 139 } 140 141 return aSource; 142 } 143 144 FontWeight getLighter(FontWeight aSource) 145 { 146 switch(aSource) 147 { 148 case FontWeight_200: aSource = FontWeight_100; break; 149 case FontWeight_300: aSource = FontWeight_200; break; 150 case FontWeight_400: aSource = FontWeight_300; break; 151 case FontWeight_500: aSource = FontWeight_400; break; 152 case FontWeight_600: aSource = FontWeight_500; break; 153 case FontWeight_700: aSource = FontWeight_600; break; 154 case FontWeight_800: aSource = FontWeight_700; break; 155 case FontWeight_900: aSource = FontWeight_800; break; 156 default: break; 157 } 158 159 return aSource; 160 } 161 162 ::FontWeight getVclFontWeight(FontWeight aSource) 163 { 164 ::FontWeight nRetval(WEIGHT_NORMAL); 165 166 switch(aSource) 167 { 168 case FontWeight_100: nRetval = WEIGHT_ULTRALIGHT; break; 169 case FontWeight_200: nRetval = WEIGHT_LIGHT; break; 170 case FontWeight_300: nRetval = WEIGHT_SEMILIGHT; break; 171 case FontWeight_400: nRetval = WEIGHT_NORMAL; break; 172 case FontWeight_500: nRetval = WEIGHT_MEDIUM; break; 173 case FontWeight_600: nRetval = WEIGHT_SEMIBOLD; break; 174 case FontWeight_700: nRetval = WEIGHT_BOLD; break; 175 case FontWeight_800: nRetval = WEIGHT_ULTRABOLD; break; 176 case FontWeight_900: nRetval = WEIGHT_BLACK; break; 177 default: break; 178 } 179 180 return nRetval; 181 } 182 183 void SvgStyleAttributes::readStyle(const rtl::OUString& rCandidate) 184 { 185 const sal_Int32 nLen(rCandidate.getLength()); 186 sal_Int32 nPos(0); 187 188 while(nPos < nLen) 189 { 190 const sal_Int32 nInitPos(nPos); 191 skip_char(rCandidate, sal_Unicode(' '), nPos, nLen); 192 rtl::OUStringBuffer aTokenName; 193 copyString(rCandidate, nPos, aTokenName, nLen); 194 195 if(aTokenName.getLength()) 196 { 197 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(':'), nPos, nLen); 198 rtl::OUStringBuffer aTokenValue; 199 copyToLimiter(rCandidate, sal_Unicode(';'), nPos, aTokenValue, nLen); 200 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(';'), nPos, nLen); 201 const rtl::OUString aOUTokenName(aTokenName.makeStringAndClear()); 202 const rtl::OUString aOUTokenValue(aTokenValue.makeStringAndClear()); 203 204 parseStyleAttribute(aOUTokenName, StrToSVGToken(aOUTokenName), aOUTokenValue); 205 } 206 207 if(nInitPos == nPos) 208 { 209 OSL_ENSURE(false, "Could not interpret on current position (!)"); 210 nPos++; 211 } 212 } 213 } 214 215 const SvgStyleAttributes* SvgStyleAttributes::getParentStyle() const 216 { 217 if(getCssStyleParent()) 218 { 219 return getCssStyleParent(); 220 } 221 222 if(mrOwner.getParent()) 223 { 224 return mrOwner.getParent()->getSvgStyleAttributes(); 225 } 226 227 return 0; 228 } 229 230 void SvgStyleAttributes::add_text( 231 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 232 drawinglayer::primitive2d::Primitive2DSequence& rSource) const 233 { 234 if(rSource.hasElements()) 235 { 236 // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D 237 // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill()) 238 // set. When another fill is used and also evtl. stroke is set it gets necessary to 239 // dismantle to geometry and add needed primitives 240 const basegfx::BColor* pFill = getFill(); 241 const SvgGradientNode* pFillGradient = getSvgGradientNodeFill(); 242 const SvgPatternNode* pFillPattern = getSvgPatternNodeFill(); 243 const basegfx::BColor* pStroke = getStroke(); 244 const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke(); 245 const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke(); 246 basegfx::B2DPolyPolygon aMergedArea; 247 248 if(pFillGradient || pFillPattern || pStroke || pStrokeGradient || pStrokePattern) 249 { 250 // text geometry is needed, create 251 // use neutral ViewInformation and create LineGeometryExtractor2D 252 const drawinglayer::geometry::ViewInformation2D aViewInformation2D; 253 drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D); 254 255 // proccess 256 aExtractor.process(rSource); 257 258 // get results 259 const drawinglayer::processor2d::TextAsPolygonDataNodeVector& rResult = aExtractor.getTarget(); 260 const sal_uInt32 nResultCount(rResult.size()); 261 basegfx::B2DPolyPolygonVector aTextFillVector; 262 aTextFillVector.reserve(nResultCount); 263 264 for(sal_uInt32 a(0); a < nResultCount; a++) 265 { 266 const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a]; 267 268 if(rCandidate.getIsFilled()) 269 { 270 aTextFillVector.push_back(rCandidate.getB2DPolyPolygon()); 271 } 272 } 273 274 if(!aTextFillVector.empty()) 275 { 276 aMergedArea = basegfx::tools::mergeToSinglePolyPolygon(aTextFillVector); 277 } 278 } 279 280 const bool bStrokeUsed(pStroke || pStrokeGradient || pStrokePattern); 281 282 // add fill. Use geometry even for simple color fill when stroke 283 // is used, else text rendering and the geometry-based stroke will 284 // normally not really match optically due to divrese system text 285 // renderers 286 if(aMergedArea.count() && (pFillGradient || pFillPattern || bStrokeUsed)) 287 { 288 // create text fill content based on geometry 289 add_fill(aMergedArea, rTarget, aMergedArea.getB2DRange()); 290 } 291 else if(pFill) 292 { 293 // add the already prepared primitives for single color fill 294 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, rSource); 295 } 296 297 // add stroke 298 if(aMergedArea.count() && bStrokeUsed) 299 { 300 // create text stroke content 301 add_stroke(aMergedArea, rTarget, aMergedArea.getB2DRange()); 302 } 303 } 304 } 305 306 void SvgStyleAttributes::add_fillGradient( 307 const basegfx::B2DPolyPolygon& rPath, 308 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 309 const SvgGradientNode& rFillGradient, 310 const basegfx::B2DRange& rGeoRange) const 311 { 312 // create fill content 313 drawinglayer::primitive2d::SvgGradientEntryVector aSvgGradientEntryVector; 314 315 // get the color stops 316 rFillGradient.collectGradientEntries(aSvgGradientEntryVector); 317 318 if(!aSvgGradientEntryVector.empty()) 319 { 320 basegfx::B2DHomMatrix aGeoToUnit; 321 322 if(rFillGradient.getGradientTransform()) 323 { 324 aGeoToUnit = *rFillGradient.getGradientTransform(); 325 } 326 327 if(userSpaceOnUse == rFillGradient.getGradientUnits()) 328 { 329 aGeoToUnit.translate(-rGeoRange.getMinX(), -rGeoRange.getMinY()); 330 aGeoToUnit.scale(1.0 / rGeoRange.getWidth(), 1.0 / rGeoRange.getHeight()); 331 } 332 333 if(SVGTokenLinearGradient == rFillGradient.getType()) 334 { 335 basegfx::B2DPoint aStart(0.0, 0.0); 336 basegfx::B2DPoint aEnd(1.0, 0.0); 337 338 if(userSpaceOnUse == rFillGradient.getGradientUnits()) 339 { 340 // all possible units 341 aStart.setX(rFillGradient.getX1().solve(mrOwner, xcoordinate)); 342 aStart.setY(rFillGradient.getY1().solve(mrOwner, ycoordinate)); 343 aEnd.setX(rFillGradient.getX2().solve(mrOwner, xcoordinate)); 344 aEnd.setY(rFillGradient.getY2().solve(mrOwner, ycoordinate)); 345 } 346 else 347 { 348 // fractions or percent relative to object bounds 349 const SvgNumber X1(rFillGradient.getX1()); 350 const SvgNumber Y1(rFillGradient.getY1()); 351 const SvgNumber X2(rFillGradient.getX2()); 352 const SvgNumber Y2(rFillGradient.getY2()); 353 354 aStart.setX(Unit_percent == X1.getUnit() ? X1.getNumber() * 0.01 : X1.getNumber()); 355 aStart.setY(Unit_percent == Y1.getUnit() ? Y1.getNumber() * 0.01 : Y1.getNumber()); 356 aEnd.setX(Unit_percent == X2.getUnit() ? X2.getNumber() * 0.01 : X2.getNumber()); 357 aEnd.setY(Unit_percent == Y2.getUnit() ? Y2.getNumber() * 0.01 : Y2.getNumber()); 358 } 359 360 if(!aGeoToUnit.isIdentity()) 361 { 362 aStart *= aGeoToUnit; 363 aEnd *= aGeoToUnit; 364 } 365 366 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence( 367 rTarget, 368 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D( 369 rPath, 370 aSvgGradientEntryVector, 371 aStart, 372 aEnd, 373 userSpaceOnUse != rFillGradient.getGradientUnits(), 374 rFillGradient.getSpreadMethod())); 375 } 376 else 377 { 378 basegfx::B2DPoint aStart(0.5, 0.5); 379 basegfx::B2DPoint aFocal; 380 double fRadius(0.5); 381 const SvgNumber* pFx = rFillGradient.getFx(); 382 const SvgNumber* pFy = rFillGradient.getFy(); 383 const bool bFocal(pFx || pFy); 384 385 if(userSpaceOnUse == rFillGradient.getGradientUnits()) 386 { 387 // all possible units 388 aStart.setX(rFillGradient.getCx().solve(mrOwner, xcoordinate)); 389 aStart.setY(rFillGradient.getCy().solve(mrOwner, ycoordinate)); 390 fRadius = rFillGradient.getR().solve(mrOwner, length); 391 392 if(bFocal) 393 { 394 aFocal.setX(pFx ? pFx->solve(mrOwner, xcoordinate) : aStart.getX()); 395 aFocal.setY(pFy ? pFy->solve(mrOwner, ycoordinate) : aStart.getY()); 396 } 397 } 398 else 399 { 400 // fractions or percent relative to object bounds 401 const SvgNumber Cx(rFillGradient.getCx()); 402 const SvgNumber Cy(rFillGradient.getCy()); 403 const SvgNumber R(rFillGradient.getR()); 404 405 aStart.setX(Unit_percent == Cx.getUnit() ? Cx.getNumber() * 0.01 : Cx.getNumber()); 406 aStart.setY(Unit_percent == Cy.getUnit() ? Cy.getNumber() * 0.01 : Cy.getNumber()); 407 fRadius = (Unit_percent == R.getUnit()) ? R.getNumber() * 0.01 : R.getNumber(); 408 409 if(bFocal) 410 { 411 aFocal.setX(pFx ? (Unit_percent == pFx->getUnit() ? pFx->getNumber() * 0.01 : pFx->getNumber()) : aStart.getX()); 412 aFocal.setY(pFy ? (Unit_percent == pFy->getUnit() ? pFy->getNumber() * 0.01 : pFy->getNumber()) : aStart.getY()); 413 } 414 } 415 416 if(!aGeoToUnit.isIdentity()) 417 { 418 aStart *= aGeoToUnit; 419 fRadius = (aGeoToUnit * basegfx::B2DVector(fRadius, 0.0)).getLength(); 420 421 if(bFocal) 422 { 423 aFocal *= aGeoToUnit; 424 } 425 } 426 427 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence( 428 rTarget, 429 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D( 430 rPath, 431 aSvgGradientEntryVector, 432 aStart, 433 fRadius, 434 userSpaceOnUse != rFillGradient.getGradientUnits(), 435 rFillGradient.getSpreadMethod(), 436 bFocal ? &aFocal : 0)); 437 } 438 } 439 } 440 441 void SvgStyleAttributes::add_fillPatternTransform( 442 const basegfx::B2DPolyPolygon& rPath, 443 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 444 const SvgPatternNode& rFillPattern, 445 const basegfx::B2DRange& rGeoRange) const 446 { 447 // prepare fill polyPolygon with given pattern, check for patternTransform 448 if(rFillPattern.getPatternTransform() && !rFillPattern.getPatternTransform()->isIdentity()) 449 { 450 // PatternTransform is active; Handle by filling the inverse transformed 451 // path and back-transforming the result 452 basegfx::B2DPolyPolygon aPath(rPath); 453 basegfx::B2DHomMatrix aInv(*rFillPattern.getPatternTransform()); 454 drawinglayer::primitive2d::Primitive2DSequence aNewTarget; 455 456 aInv.invert(); 457 aPath.transform(aInv); 458 add_fillPattern(aPath, aNewTarget, rFillPattern, aPath.getB2DRange()); 459 460 if(aNewTarget.hasElements()) 461 { 462 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence( 463 rTarget, 464 new drawinglayer::primitive2d::TransformPrimitive2D( 465 *rFillPattern.getPatternTransform(), 466 aNewTarget)); 467 } 468 } 469 else 470 { 471 // no patternTransform, create fillPattern directly 472 add_fillPattern(rPath, rTarget, rFillPattern, rGeoRange); 473 } 474 } 475 476 void SvgStyleAttributes::add_fillPattern( 477 const basegfx::B2DPolyPolygon& rPath, 478 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 479 const SvgPatternNode& rFillPattern, 480 const basegfx::B2DRange& rGeoRange) const 481 { 482 // fill polyPolygon with given pattern 483 const drawinglayer::primitive2d::Primitive2DSequence& rPrimitives = rFillPattern.getPatternPrimitives(); 484 485 if(rPrimitives.hasElements()) 486 { 487 double fTargetWidth(rGeoRange.getWidth()); 488 double fTargetHeight(rGeoRange.getHeight()); 489 490 if(fTargetWidth > 0.0 && fTargetHeight > 0.0) 491 { 492 // get relative values from pattern 493 double fX(0.0); 494 double fY(0.0); 495 double fW(0.0); 496 double fH(0.0); 497 498 rFillPattern.getValuesRelative(fX, fY, fW, fH, rGeoRange, mrOwner); 499 500 if(fW > 0.0 && fH > 0.0) 501 { 502 // build the reference range relative to the rGeoRange 503 const basegfx::B2DRange aReferenceRange(fX, fY, fX + fW, fY + fH); 504 505 // find out how the content is mapped to the reference range 506 basegfx::B2DHomMatrix aMapPrimitivesToUnitRange; 507 const basegfx::B2DRange* pViewBox = rFillPattern.getViewBox(); 508 509 if(pViewBox) 510 { 511 // use viewBox/preserveAspectRatio 512 const SvgAspectRatio& rRatio = rFillPattern.getSvgAspectRatio(); 513 const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); 514 515 if(rRatio.isSet()) 516 { 517 // let mapping be created from SvgAspectRatio 518 aMapPrimitivesToUnitRange = rRatio.createMapping(aUnitRange, *pViewBox); 519 } 520 else 521 { 522 // choose default mapping 523 aMapPrimitivesToUnitRange = rRatio.createLinearMapping(aUnitRange, *pViewBox); 524 } 525 } 526 else 527 { 528 // use patternContentUnits 529 const SvgUnits aPatternContentUnits(rFillPattern.getPatternContentUnits() ? *rFillPattern.getPatternContentUnits() : userSpaceOnUse); 530 531 if(userSpaceOnUse == aPatternContentUnits) 532 { 533 // create relative mapping to unit coordinates 534 aMapPrimitivesToUnitRange.scale(1.0 / (fW * fTargetWidth), 1.0 / (fH * fTargetHeight)); 535 } 536 else 537 { 538 aMapPrimitivesToUnitRange.scale(1.0 / fW, 1.0 / fH); 539 } 540 } 541 542 // apply aMapPrimitivesToUnitRange to content when used 543 drawinglayer::primitive2d::Primitive2DSequence aPrimitives(rPrimitives); 544 545 if(!aMapPrimitivesToUnitRange.isIdentity()) 546 { 547 const drawinglayer::primitive2d::Primitive2DReference xRef( 548 new drawinglayer::primitive2d::TransformPrimitive2D( 549 aMapPrimitivesToUnitRange, 550 aPrimitives)); 551 552 aPrimitives = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1); 553 } 554 555 // embed in PatternFillPrimitive2D 556 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence( 557 rTarget, 558 new drawinglayer::primitive2d::PatternFillPrimitive2D( 559 rPath, 560 aPrimitives, 561 aReferenceRange)); 562 } 563 } 564 } 565 } 566 567 void SvgStyleAttributes::add_fill( 568 const basegfx::B2DPolyPolygon& rPath, 569 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 570 const basegfx::B2DRange& rGeoRange) const 571 { 572 const basegfx::BColor* pFill = getFill(); 573 const SvgGradientNode* pFillGradient = getSvgGradientNodeFill(); 574 const SvgPatternNode* pFillPattern = getSvgPatternNodeFill(); 575 576 if(pFill || pFillGradient || pFillPattern) 577 { 578 const double fFillOpacity(getFillOpacity().solve(mrOwner, length)); 579 580 if(basegfx::fTools::more(fFillOpacity, 0.0)) 581 { 582 drawinglayer::primitive2d::Primitive2DSequence aNewFill; 583 584 if(pFillGradient) 585 { 586 // create fill content with SVG gradient primitive 587 add_fillGradient(rPath, aNewFill, *pFillGradient, rGeoRange); 588 } 589 else if(pFillPattern) 590 { 591 // create fill content with SVG pattern primitive 592 add_fillPatternTransform(rPath, aNewFill, *pFillPattern, rGeoRange); 593 } 594 else // if(pFill) 595 { 596 // create fill content 597 aNewFill.realloc(1); 598 aNewFill[0] = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( 599 rPath, 600 *pFill); 601 } 602 603 if(aNewFill.hasElements()) 604 { 605 if(basegfx::fTools::less(fFillOpacity, 1.0)) 606 { 607 // embed in UnifiedTransparencePrimitive2D 608 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence( 609 rTarget, 610 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 611 aNewFill, 612 1.0 - fFillOpacity)); 613 } 614 else 615 { 616 // append 617 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewFill); 618 } 619 } 620 } 621 } 622 } 623 624 void SvgStyleAttributes::add_stroke( 625 const basegfx::B2DPolyPolygon& rPath, 626 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 627 const basegfx::B2DRange& rGeoRange) const 628 { 629 const basegfx::BColor* pStroke = getStroke(); 630 const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke(); 631 const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke(); 632 633 if(pStroke || pStrokeGradient || pStrokePattern) 634 { 635 drawinglayer::primitive2d::Primitive2DSequence aNewStroke; 636 const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner, length)); 637 638 if(basegfx::fTools::more(fStrokeOpacity, 0.0)) 639 { 640 // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all 641 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner, length) : 1.0); 642 643 if(basegfx::fTools::more(fStrokeWidth, 0.0)) 644 { 645 // get LineJoin, LineCap and stroke array 646 const basegfx::B2DLineJoin aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin())); 647 const com::sun::star::drawing::LineCap aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap())); 648 ::std::vector< double > aDashArray; 649 650 if(!getStrokeDasharray().empty()) 651 { 652 aDashArray = solveSvgNumberVector(getStrokeDasharray(), mrOwner, length); 653 } 654 655 // todo: Handle getStrokeDashOffset() 656 657 // prepare line attribute 658 drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive; 659 const drawinglayer::attribute::LineAttribute aLineAttribute( 660 pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0), 661 fStrokeWidth, 662 aB2DLineJoin, 663 aLineCap); 664 665 if(aDashArray.empty()) 666 { 667 aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( 668 rPath, 669 aLineAttribute); 670 } 671 else 672 { 673 const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(aDashArray); 674 675 aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( 676 rPath, 677 aLineAttribute, 678 aDashArray); 679 } 680 681 if(pStrokeGradient || pStrokePattern) 682 { 683 // put primitive into Primitive2DReference and Primitive2DSequence 684 const drawinglayer::primitive2d::Primitive2DSequence aSeq(&aNewLinePrimitive, 1); 685 686 // use neutral ViewInformation and create LineGeometryExtractor2D 687 const drawinglayer::geometry::ViewInformation2D aViewInformation2D; 688 drawinglayer::processor2d::LineGeometryExtractor2D aExtractor(aViewInformation2D); 689 690 // proccess 691 aExtractor.process(aSeq); 692 693 // check for fill rsults 694 const basegfx::B2DPolyPolygonVector& rLineFillVector(aExtractor.getExtractedLineFills()); 695 696 if(!rLineFillVector.empty()) 697 { 698 const basegfx::B2DPolyPolygon aMergedArea( 699 basegfx::tools::mergeToSinglePolyPolygon( 700 rLineFillVector)); 701 702 if(aMergedArea.count()) 703 { 704 if(pStrokeGradient) 705 { 706 // create fill content with SVG gradient primitive. Use original GeoRange, 707 // e.g. from circle without LineWidth 708 add_fillGradient(aMergedArea, aNewStroke, *pStrokeGradient, rGeoRange); 709 } 710 else // if(pStrokePattern) 711 { 712 // create fill content with SVG pattern primitive. Use GeoRange 713 // from the expanded data, e.g. circle with extended geo by half linewidth 714 add_fillPatternTransform(aMergedArea, aNewStroke, *pStrokePattern, aMergedArea.getB2DRange()); 715 } 716 } 717 } 718 } 719 else // if(pStroke) 720 { 721 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aNewStroke, aNewLinePrimitive); 722 } 723 724 if(aNewStroke.hasElements()) 725 { 726 if(basegfx::fTools::less(fStrokeOpacity, 1.0)) 727 { 728 // embed in UnifiedTransparencePrimitive2D 729 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence( 730 rTarget, 731 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 732 aNewStroke, 733 1.0 - fStrokeOpacity)); 734 } 735 else 736 { 737 // append 738 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewStroke); 739 } 740 } 741 } 742 } 743 } 744 } 745 746 double get_markerRotation( 747 const SvgMarkerNode& rMarker, 748 const basegfx::B2DPolygon& rPolygon, 749 const sal_uInt32 nIndex) 750 { 751 double fAngle(0.0); 752 const sal_uInt32 nPointCount(rPolygon.count()); 753 754 if(nPointCount) 755 { 756 if(rMarker.getOrientAuto()) 757 { 758 const bool bPrev(rPolygon.isClosed() || nIndex > 0); 759 basegfx::B2DCubicBezier aSegment; 760 basegfx::B2DVector aPrev; 761 basegfx::B2DVector aNext; 762 763 if(bPrev) 764 { 765 rPolygon.getBezierSegment((nIndex - 1) % nPointCount, aSegment); 766 aPrev = aSegment.getTangent(1.0); 767 } 768 769 const bool bNext(rPolygon.isClosed() || nIndex + 1 < nPointCount); 770 771 if(bNext) 772 { 773 rPolygon.getBezierSegment(nIndex % nPointCount, aSegment); 774 aNext = aSegment.getTangent(0.0); 775 } 776 777 if(bPrev && bNext) 778 { 779 fAngle = atan2(aPrev.getY() + aNext.getY(), aPrev.getX() + aNext.getX()); 780 } 781 else if(bPrev) 782 { 783 fAngle = atan2(aPrev.getY(), aPrev.getX()); 784 } 785 else if(bNext) 786 { 787 fAngle = atan2(aNext.getY(), aNext.getX()); 788 } 789 } 790 else 791 { 792 fAngle = rMarker.getAngle(); 793 } 794 } 795 796 return fAngle; 797 } 798 799 bool SvgStyleAttributes::prepare_singleMarker( 800 drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives, 801 basegfx::B2DHomMatrix& rMarkerTransform, 802 basegfx::B2DRange& rClipRange, 803 const SvgMarkerNode& rMarker) const 804 { 805 // reset return values 806 rMarkerTransform.identity(); 807 rClipRange.reset(); 808 809 // get marker primitive representation 810 rMarkerPrimitives = rMarker.getMarkerPrimitives(); 811 812 if(rMarkerPrimitives.hasElements()) 813 { 814 basegfx::B2DRange aPrimitiveRange(0.0, 0.0, 1.0, 1.0); 815 const basegfx::B2DRange* pViewBox = rMarker.getViewBox(); 816 817 if(pViewBox) 818 { 819 aPrimitiveRange = *pViewBox; 820 } 821 822 if(aPrimitiveRange.getWidth() > 0.0 && aPrimitiveRange.getHeight() > 0.0) 823 { 824 double fTargetWidth(rMarker.getMarkerWidth().isSet() ? rMarker.getMarkerWidth().solve(mrOwner, xcoordinate) : 3.0); 825 double fTargetHeight(rMarker.getMarkerHeight().isSet() ? rMarker.getMarkerHeight().solve(mrOwner, xcoordinate) : 3.0); 826 const bool bStrokeWidth(SvgMarkerNode::strokeWidth == rMarker.getMarkerUnits()); 827 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner, length) : 1.0); 828 829 if(bStrokeWidth) 830 { 831 // relative to strokeWidth 832 fTargetWidth *= fStrokeWidth; 833 fTargetHeight *= fStrokeWidth; 834 } 835 836 if(fTargetWidth > 0.0 && fTargetHeight > 0.0) 837 { 838 // create mapping 839 const basegfx::B2DRange aTargetRange(0.0, 0.0, fTargetWidth, fTargetHeight); 840 const SvgAspectRatio& rRatio = rMarker.getSvgAspectRatio(); 841 842 if(rRatio.isSet()) 843 { 844 // let mapping be created from SvgAspectRatio 845 rMarkerTransform = rRatio.createMapping(aTargetRange, aPrimitiveRange); 846 847 if(rRatio.isMeetOrSlice()) 848 { 849 // need to clip 850 rClipRange = aPrimitiveRange; 851 } 852 } 853 else 854 { 855 if(!pViewBox) 856 { 857 if(bStrokeWidth) 858 { 859 // adapt to strokewidth if needed 860 rMarkerTransform.scale(fStrokeWidth, fStrokeWidth); 861 } 862 } 863 else 864 { 865 // choose default mapping 866 rMarkerTransform = rRatio.createLinearMapping(aTargetRange, aPrimitiveRange); 867 } 868 } 869 870 // get and apply reference point. Initially it's in marker local coordinate system 871 basegfx::B2DPoint aRefPoint( 872 rMarker.getRefX().isSet() ? rMarker.getRefX().solve(mrOwner, xcoordinate) : 0.0, 873 rMarker.getRefY().isSet() ? rMarker.getRefY().solve(mrOwner, ycoordinate) : 0.0); 874 875 // apply MarkerTransform to have it in mapped coordinates 876 aRefPoint *= rMarkerTransform; 877 878 // apply by moving RepPoint to (0.0) 879 rMarkerTransform.translate(-aRefPoint.getX(), -aRefPoint.getY()); 880 881 return true; 882 } 883 } 884 } 885 886 return false; 887 } 888 889 void SvgStyleAttributes::add_singleMarker( 890 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 891 const drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives, 892 const basegfx::B2DHomMatrix& rMarkerTransform, 893 const basegfx::B2DRange& rClipRange, 894 const SvgMarkerNode& rMarker, 895 const basegfx::B2DPolygon& rCandidate, 896 const sal_uInt32 nIndex) const 897 { 898 const sal_uInt32 nPointCount(rCandidate.count()); 899 900 if(nPointCount) 901 { 902 // get and apply rotation 903 basegfx::B2DHomMatrix aCombinedTransform(rMarkerTransform); 904 aCombinedTransform.rotate(get_markerRotation(rMarker, rCandidate, nIndex)); 905 906 // get and apply target position 907 const basegfx::B2DPoint aPoint(rCandidate.getB2DPoint(nIndex % nPointCount)); 908 aCombinedTransform.translate(aPoint.getX(), aPoint.getY()); 909 910 // prepare marker 911 drawinglayer::primitive2d::Primitive2DReference xMarker( 912 new drawinglayer::primitive2d::TransformPrimitive2D( 913 aCombinedTransform, 914 rMarkerPrimitives)); 915 916 if(!rClipRange.isEmpty()) 917 { 918 // marker needs to be clipped, it's bigger as the mapping 919 basegfx::B2DPolyPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(rClipRange)); 920 921 aClipPolygon.transform(aCombinedTransform); 922 xMarker = new drawinglayer::primitive2d::MaskPrimitive2D( 923 aClipPolygon, 924 drawinglayer::primitive2d::Primitive2DSequence(&xMarker, 1)); 925 } 926 927 // add marker 928 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMarker); 929 } 930 } 931 932 void SvgStyleAttributes::add_markers( 933 const basegfx::B2DPolyPolygon& rPath, 934 drawinglayer::primitive2d::Primitive2DSequence& rTarget) const 935 { 936 // try to access linked markers 937 const SvgMarkerNode* pStart = accessMarkerStartXLink(); 938 const SvgMarkerNode* pMid = accessMarkerMidXLink(); 939 const SvgMarkerNode* pEnd = accessMarkerEndXLink(); 940 941 if(pStart || pMid || pEnd) 942 { 943 const sal_uInt32 nCount(rPath.count()); 944 945 for (sal_uInt32 a(0); a < nCount; a++) 946 { 947 const basegfx::B2DPolygon aCandidate(rPath.getB2DPolygon(a)); 948 const sal_uInt32 nPointCount(aCandidate.count()); 949 950 if(nPointCount) 951 { 952 const sal_uInt32 nMarkerCount(aCandidate.isClosed() ? nPointCount + 1 : nPointCount); 953 drawinglayer::primitive2d::Primitive2DSequence aMarkerPrimitives; 954 basegfx::B2DHomMatrix aMarkerTransform; 955 basegfx::B2DRange aClipRange; 956 const SvgMarkerNode* pPrepared = 0; 957 958 if(pStart) 959 { 960 if(prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pStart)) 961 { 962 pPrepared = pStart; 963 add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, 0); 964 } 965 } 966 967 if(pMid && nMarkerCount > 2) 968 { 969 if(pMid == pPrepared || prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pMid)) 970 { 971 pPrepared = pMid; 972 973 for(sal_uInt32 b(1); b < nMarkerCount - 1; b++) 974 { 975 add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, b); 976 } 977 } 978 } 979 980 if(pEnd) 981 { 982 if(pEnd == pPrepared || prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pEnd)) 983 { 984 pPrepared = pEnd; 985 add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, nMarkerCount - 1); 986 } 987 } 988 } 989 } 990 } 991 } 992 993 void SvgStyleAttributes::add_path( 994 const basegfx::B2DPolyPolygon& rPath, 995 drawinglayer::primitive2d::Primitive2DSequence& rTarget) const 996 { 997 const bool bIsLine(1 == rPath.count() 998 && !rPath.areControlPointsUsed() 999 && 2 == rPath.getB2DPolygon(0).count()); 1000 1001 if(!rPath.count()) 1002 { 1003 return; 1004 } 1005 1006 const basegfx::B2DRange aGeoRange(rPath.getB2DRange()); 1007 1008 if(aGeoRange.isEmpty()) 1009 { 1010 return; 1011 } 1012 1013 if(!bIsLine && // not for lines 1014 (basegfx::fTools::equalZero(aGeoRange.getWidth()) 1015 || basegfx::fTools::equalZero(aGeoRange.getHeight()))) 1016 { 1017 return; 1018 } 1019 1020 const double fOpacity(getOpacity().getNumber()); 1021 1022 if(basegfx::fTools::equalZero(fOpacity)) 1023 { 1024 return; 1025 } 1026 1027 if(!bIsLine) 1028 { 1029 basegfx::B2DPolyPolygon aPath(rPath); 1030 const bool bNeedToCheckClipRule(SVGTokenPath == mrOwner.getType() || SVGTokenPolygon == mrOwner.getType()); 1031 const bool bClipPathIsNonzero(!bIsLine && bNeedToCheckClipRule && mbIsClipPathContent && FillRule_nonzero == maClipRule); 1032 const bool bFillRuleIsNonzero(!bIsLine && bNeedToCheckClipRule && !mbIsClipPathContent && FillRule_nonzero == getFillRule()); 1033 1034 if(bClipPathIsNonzero || bFillRuleIsNonzero) 1035 { 1036 // nonzero is wanted, solve geometrically (see description on basegfx) 1037 aPath = basegfx::tools::createNonzeroConform(aPath); 1038 } 1039 1040 add_fill(aPath, rTarget, aGeoRange); 1041 } 1042 1043 add_stroke(rPath, rTarget, aGeoRange); 1044 1045 // Svg supports markers for path, polygon, polyline and line 1046 if(SVGTokenPath == mrOwner.getType() || // path 1047 SVGTokenPolygon == mrOwner.getType() || // polygon, polyline 1048 SVGTokenLine == mrOwner.getType()) // line 1049 { 1050 // try to add markers 1051 add_markers(rPath, rTarget); 1052 } 1053 } 1054 1055 void SvgStyleAttributes::add_postProcess( 1056 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 1057 const drawinglayer::primitive2d::Primitive2DSequence& rSource, 1058 const basegfx::B2DHomMatrix* pTransform) const 1059 { 1060 if(rSource.hasElements()) 1061 { 1062 const double fOpacity(getOpacity().getNumber()); 1063 1064 if(basegfx::fTools::equalZero(fOpacity)) 1065 { 1066 return; 1067 } 1068 1069 drawinglayer::primitive2d::Primitive2DSequence aSource(rSource); 1070 1071 if(basegfx::fTools::less(fOpacity, 1.0)) 1072 { 1073 // embed in UnifiedTransparencePrimitive2D 1074 const drawinglayer::primitive2d::Primitive2DReference xRef( 1075 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 1076 aSource, 1077 1.0 - fOpacity)); 1078 1079 aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1); 1080 } 1081 1082 if(pTransform) 1083 { 1084 // create embedding group element with transformation. This applies the given 1085 // transformation to the graphical content, but *not* to mask and/or clip (as needed) 1086 const drawinglayer::primitive2d::Primitive2DReference xRef( 1087 new drawinglayer::primitive2d::TransformPrimitive2D( 1088 *pTransform, 1089 aSource)); 1090 1091 aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1); 1092 } 1093 1094 if(getClipPathXLink().getLength()) 1095 { 1096 // try to access linked ClipPath 1097 const SvgClipPathNode* mpClip = dynamic_cast< const SvgClipPathNode* >(mrOwner.getDocument().findSvgNodeById(getClipPathXLink())); 1098 1099 if(mpClip) 1100 { 1101 mpClip->apply(aSource); 1102 } 1103 } 1104 1105 if(aSource.hasElements()) // test again, applied clipPath may have lead to empty geometry 1106 { 1107 if(getMaskXLink().getLength()) 1108 { 1109 // try to access linked Mask 1110 const SvgMaskNode* mpMask = dynamic_cast< const SvgMaskNode* >(mrOwner.getDocument().findSvgNodeById(getMaskXLink())); 1111 1112 if(mpMask) 1113 { 1114 mpMask->apply(aSource); 1115 } 1116 } 1117 1118 if(aSource.hasElements()) // test again, applied mask may have lead to empty geometry 1119 { 1120 // append to current target 1121 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSource); 1122 } 1123 } 1124 } 1125 } 1126 1127 SvgStyleAttributes::SvgStyleAttributes(SvgNode& rOwner) 1128 : mrOwner(rOwner), 1129 mpCssStyleParent(0), 1130 maFill(), 1131 maStroke(), 1132 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true), 1133 maStrokeWidth(), 1134 maStopOpacity(), 1135 mpSvgGradientNodeFill(0), 1136 mpSvgGradientNodeStroke(0), 1137 mpSvgPatternNodeFill(0), 1138 mpSvgPatternNodeStroke(0), 1139 maFillOpacity(), 1140 maStrokeDasharray(), 1141 maStrokeDashOffset(), 1142 maStrokeLinecap(StrokeLinecap_notset), 1143 maStrokeLinejoin(StrokeLinejoin_notset), 1144 maStrokeMiterLimit(), 1145 maStrokeOpacity(), 1146 maFontFamily(), 1147 maFontSize(), 1148 maFontStretch(FontStretch_notset), 1149 maFontStyle(FontStyle_notset), 1150 maFontVariant(FontVariant_notset), 1151 maFontWeight(FontWeight_notset), 1152 maTextAlign(TextAlign_notset), 1153 maTextDecoration(TextDecoration_notset), 1154 maTextAnchor(TextAnchor_notset), 1155 maColor(), 1156 maOpacity(1.0), 1157 maTitle(), 1158 maDesc(), 1159 maClipPathXLink(), 1160 maMaskXLink(), 1161 maMarkerStartXLink(), 1162 mpMarkerStartXLink(0), 1163 maMarkerMidXLink(), 1164 mpMarkerMidXLink(0), 1165 maMarkerEndXLink(), 1166 mpMarkerEndXLink(0), 1167 maFillRule(FillRule_notset), 1168 maClipRule(FillRule_nonzero), 1169 mbIsClipPathContent(SVGTokenClipPathNode == mrOwner.getType()), 1170 mbStrokeDasharraySet(false) 1171 { 1172 if(!mbIsClipPathContent) 1173 { 1174 const SvgStyleAttributes* pParentStyle = getParentStyle(); 1175 1176 if(pParentStyle) 1177 { 1178 mbIsClipPathContent = pParentStyle->mbIsClipPathContent; 1179 } 1180 } 1181 } 1182 1183 SvgStyleAttributes::~SvgStyleAttributes() 1184 { 1185 } 1186 1187 void SvgStyleAttributes::parseStyleAttribute(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent) 1188 { 1189 switch(aSVGToken) 1190 { 1191 case SVGTokenFill: 1192 { 1193 SvgPaint aSvgPaint; 1194 rtl::OUString aURL; 1195 1196 if(readSvgPaint(aContent, aSvgPaint, aURL)) 1197 { 1198 setFill(aSvgPaint); 1199 } 1200 else if(aURL.getLength()) 1201 { 1202 const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL); 1203 1204 if(pNode) 1205 { 1206 if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType()) 1207 { 1208 setSvgGradientNodeFill(static_cast< const SvgGradientNode* >(pNode)); 1209 } 1210 else if(SVGTokenPattern == pNode->getType()) 1211 { 1212 setSvgPatternNodeFill(static_cast< const SvgPatternNode* >(pNode)); 1213 } 1214 } 1215 } 1216 break; 1217 } 1218 case SVGTokenFillOpacity: 1219 { 1220 SvgNumber aNum; 1221 1222 if(readSingleNumber(aContent, aNum)) 1223 { 1224 if(aNum.isPositive()) 1225 { 1226 setFillOpacity(aNum); 1227 } 1228 } 1229 break; 1230 } 1231 case SVGTokenFillRule: 1232 { 1233 if(aContent.getLength()) 1234 { 1235 if(aContent.match(commonStrings::aStrNonzero)) 1236 { 1237 maFillRule = FillRule_nonzero; 1238 } 1239 else if(aContent.match(commonStrings::aStrEvenOdd)) 1240 { 1241 maFillRule = FillRule_evenodd; 1242 } 1243 } 1244 break; 1245 } 1246 case SVGTokenStroke: 1247 { 1248 SvgPaint aSvgPaint; 1249 rtl::OUString aURL; 1250 1251 if(readSvgPaint(aContent, aSvgPaint, aURL)) 1252 { 1253 setStroke(aSvgPaint); 1254 } 1255 else if(aURL.getLength()) 1256 { 1257 const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL); 1258 1259 if(pNode) 1260 { 1261 if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType()) 1262 { 1263 setSvgGradientNodeStroke(static_cast< const SvgGradientNode* >(pNode)); 1264 } 1265 else if(SVGTokenPattern == pNode->getType()) 1266 { 1267 setSvgPatternNodeStroke(static_cast< const SvgPatternNode* >(pNode)); 1268 } 1269 } 1270 } 1271 break; 1272 } 1273 case SVGTokenStrokeDasharray: 1274 { 1275 if(aContent.getLength()) 1276 { 1277 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none")); 1278 SvgNumberVector aVector; 1279 1280 if(aContent.match(aStrNone)) 1281 { 1282 // #121221# The special value 'none' needs to be handled 1283 // in the sense that *when* it is set, the parent shall not 1284 // be used. Before this was only dependent on the array being 1285 // empty 1286 setStrokeDasharraySet(true); 1287 } 1288 else if(readSvgNumberVector(aContent, aVector)) 1289 { 1290 setStrokeDasharray(aVector); 1291 } 1292 } 1293 break; 1294 } 1295 case SVGTokenStrokeDashoffset: 1296 { 1297 SvgNumber aNum; 1298 1299 if(readSingleNumber(aContent, aNum)) 1300 { 1301 if(aNum.isPositive()) 1302 { 1303 setStrokeDashOffset(aNum); 1304 } 1305 } 1306 break; 1307 } 1308 case SVGTokenStrokeLinecap: 1309 { 1310 if(aContent.getLength()) 1311 { 1312 static rtl::OUString aStrButt(rtl::OUString::createFromAscii("butt")); 1313 static rtl::OUString aStrRound(rtl::OUString::createFromAscii("round")); 1314 static rtl::OUString aStrSquare(rtl::OUString::createFromAscii("square")); 1315 1316 if(aContent.match(aStrButt)) 1317 { 1318 setStrokeLinecap(StrokeLinecap_butt); 1319 } 1320 else if(aContent.match(aStrRound)) 1321 { 1322 setStrokeLinecap(StrokeLinecap_round); 1323 } 1324 else if(aContent.match(aStrSquare)) 1325 { 1326 setStrokeLinecap(StrokeLinecap_square); 1327 } 1328 } 1329 break; 1330 } 1331 case SVGTokenStrokeLinejoin: 1332 { 1333 if(aContent.getLength()) 1334 { 1335 static rtl::OUString aStrMiter(rtl::OUString::createFromAscii("miter")); 1336 static rtl::OUString aStrRound(rtl::OUString::createFromAscii("round")); 1337 static rtl::OUString aStrBevel(rtl::OUString::createFromAscii("bevel")); 1338 1339 if(aContent.match(aStrMiter)) 1340 { 1341 setStrokeLinejoin(StrokeLinejoin_miter); 1342 } 1343 else if(aContent.match(aStrRound)) 1344 { 1345 setStrokeLinejoin(StrokeLinejoin_round); 1346 } 1347 else if(aContent.match(aStrBevel)) 1348 { 1349 setStrokeLinejoin(StrokeLinejoin_bevel); 1350 } 1351 } 1352 break; 1353 } 1354 case SVGTokenStrokeMiterlimit: 1355 { 1356 SvgNumber aNum; 1357 1358 if(readSingleNumber(aContent, aNum)) 1359 { 1360 if(aNum.isPositive()) 1361 { 1362 setStrokeMiterLimit(aNum); 1363 } 1364 } 1365 break; 1366 } 1367 case SVGTokenStrokeOpacity: 1368 { 1369 SvgNumber aNum; 1370 1371 if(readSingleNumber(aContent, aNum)) 1372 { 1373 if(aNum.isPositive()) 1374 { 1375 setStrokeOpacity(aNum); 1376 } 1377 } 1378 break; 1379 } 1380 case SVGTokenStrokeWidth: 1381 { 1382 SvgNumber aNum; 1383 1384 if(readSingleNumber(aContent, aNum)) 1385 { 1386 if(aNum.isPositive()) 1387 { 1388 setStrokeWidth(aNum); 1389 } 1390 } 1391 break; 1392 } 1393 case SVGTokenStopColor: 1394 { 1395 SvgPaint aSvgPaint; 1396 rtl::OUString aURL; 1397 1398 if(readSvgPaint(aContent, aSvgPaint, aURL)) 1399 { 1400 setStopColor(aSvgPaint); 1401 } 1402 break; 1403 } 1404 case SVGTokenStopOpacity: 1405 { 1406 SvgNumber aNum; 1407 1408 if(readSingleNumber(aContent, aNum)) 1409 { 1410 if(aNum.isPositive()) 1411 { 1412 setStopOpacity(aNum); 1413 } 1414 } 1415 break; 1416 } 1417 case SVGTokenFont: 1418 { 1419 break; 1420 } 1421 case SVGTokenFontFamily: 1422 { 1423 SvgStringVector aSvgStringVector; 1424 1425 if(readSvgStringVector(aContent, aSvgStringVector)) 1426 { 1427 setFontFamily(aSvgStringVector); 1428 } 1429 break; 1430 } 1431 case SVGTokenFontSize: 1432 { 1433 SvgNumber aNum; 1434 1435 if(readSingleNumber(aContent, aNum)) 1436 { 1437 setFontSize(aNum); 1438 } 1439 break; 1440 } 1441 case SVGTokenFontSizeAdjust: 1442 { 1443 break; 1444 } 1445 case SVGTokenFontStretch: 1446 { 1447 if(aContent.getLength()) 1448 { 1449 static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal")); 1450 static rtl::OUString aStrWider(rtl::OUString::createFromAscii("wider")); 1451 static rtl::OUString aStrNarrower(rtl::OUString::createFromAscii("narrower")); 1452 static rtl::OUString aStrUltra_condensed(rtl::OUString::createFromAscii("ultra-condensed")); 1453 static rtl::OUString aStrExtra_condensed(rtl::OUString::createFromAscii("extra-condensed")); 1454 static rtl::OUString aStrCondensed(rtl::OUString::createFromAscii("condensed")); 1455 static rtl::OUString aStrSemi_condensed(rtl::OUString::createFromAscii("semi-condensed")); 1456 static rtl::OUString aStrSemi_expanded(rtl::OUString::createFromAscii("semi-expanded")); 1457 static rtl::OUString aStrExpanded(rtl::OUString::createFromAscii("expanded")); 1458 static rtl::OUString aStrExtra_expanded(rtl::OUString::createFromAscii("extra-expanded")); 1459 static rtl::OUString aStrUltra_expanded(rtl::OUString::createFromAscii("ultra-expanded")); 1460 1461 if(aContent.match(aStrNormal)) 1462 { 1463 setFontStretch(FontStretch_normal); 1464 } 1465 else if(aContent.match(aStrWider)) 1466 { 1467 setFontStretch(FontStretch_wider); 1468 } 1469 else if(aContent.match(aStrNarrower)) 1470 { 1471 setFontStretch(FontStretch_narrower); 1472 } 1473 else if(aContent.match(aStrUltra_condensed)) 1474 { 1475 setFontStretch(FontStretch_ultra_condensed); 1476 } 1477 else if(aContent.match(aStrExtra_condensed)) 1478 { 1479 setFontStretch(FontStretch_extra_condensed); 1480 } 1481 else if(aContent.match(aStrCondensed)) 1482 { 1483 setFontStretch(FontStretch_condensed); 1484 } 1485 else if(aContent.match(aStrSemi_condensed)) 1486 { 1487 setFontStretch(FontStretch_semi_condensed); 1488 } 1489 else if(aContent.match(aStrSemi_expanded)) 1490 { 1491 setFontStretch(FontStretch_semi_expanded); 1492 } 1493 else if(aContent.match(aStrExpanded)) 1494 { 1495 setFontStretch(FontStretch_expanded); 1496 } 1497 else if(aContent.match(aStrExtra_expanded)) 1498 { 1499 setFontStretch(FontStretch_extra_expanded); 1500 } 1501 else if(aContent.match(aStrUltra_expanded)) 1502 { 1503 setFontStretch(FontStretch_ultra_expanded); 1504 } 1505 } 1506 break; 1507 } 1508 case SVGTokenFontStyle: 1509 { 1510 if(aContent.getLength()) 1511 { 1512 static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal")); 1513 static rtl::OUString aStrItalic(rtl::OUString::createFromAscii("italic")); 1514 static rtl::OUString aStrOblique(rtl::OUString::createFromAscii("oblique")); 1515 1516 if(aContent.match(aStrNormal)) 1517 { 1518 setFontStyle(FontStyle_normal); 1519 } 1520 else if(aContent.match(aStrItalic)) 1521 { 1522 setFontStyle(FontStyle_italic); 1523 } 1524 else if(aContent.match(aStrOblique)) 1525 { 1526 setFontStyle(FontStyle_oblique); 1527 } 1528 } 1529 break; 1530 } 1531 case SVGTokenFontVariant: 1532 { 1533 if(aContent.getLength()) 1534 { 1535 static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal")); 1536 static rtl::OUString aStrSmallCaps(rtl::OUString::createFromAscii("small-caps")); 1537 1538 if(aContent.match(aStrNormal)) 1539 { 1540 setFontVariant(FontVariant_normal); 1541 } 1542 else if(aContent.match(aStrSmallCaps)) 1543 { 1544 setFontVariant(FontVariant_small_caps); 1545 } 1546 } 1547 break; 1548 } 1549 case SVGTokenFontWeight: 1550 { 1551 if(aContent.getLength()) 1552 { 1553 static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal")); 1554 static rtl::OUString aStrBold(rtl::OUString::createFromAscii("bold")); 1555 static rtl::OUString aStrBolder(rtl::OUString::createFromAscii("bolder")); 1556 static rtl::OUString aStrLighter(rtl::OUString::createFromAscii("lighter")); 1557 static rtl::OUString aStr100(rtl::OUString::createFromAscii("100")); 1558 static rtl::OUString aStr200(rtl::OUString::createFromAscii("200")); 1559 static rtl::OUString aStr300(rtl::OUString::createFromAscii("300")); 1560 static rtl::OUString aStr400(rtl::OUString::createFromAscii("400")); 1561 static rtl::OUString aStr500(rtl::OUString::createFromAscii("500")); 1562 static rtl::OUString aStr600(rtl::OUString::createFromAscii("600")); 1563 static rtl::OUString aStr700(rtl::OUString::createFromAscii("700")); 1564 static rtl::OUString aStr800(rtl::OUString::createFromAscii("800")); 1565 static rtl::OUString aStr900(rtl::OUString::createFromAscii("900")); 1566 1567 if(aContent.match(aStr100)) 1568 { 1569 setFontWeight(FontWeight_100); 1570 } 1571 else if(aContent.match(aStr200)) 1572 { 1573 setFontWeight(FontWeight_200); 1574 } 1575 else if(aContent.match(aStr300)) 1576 { 1577 setFontWeight(FontWeight_300); 1578 } 1579 else if(aContent.match(aStr400) || aContent.match(aStrNormal)) 1580 { 1581 setFontWeight(FontWeight_400); 1582 } 1583 else if(aContent.match(aStr500)) 1584 { 1585 setFontWeight(FontWeight_500); 1586 } 1587 else if(aContent.match(aStr600)) 1588 { 1589 setFontWeight(FontWeight_600); 1590 } 1591 else if(aContent.match(aStr700) || aContent.match(aStrBold)) 1592 { 1593 setFontWeight(FontWeight_700); 1594 } 1595 else if(aContent.match(aStr800)) 1596 { 1597 setFontWeight(FontWeight_800); 1598 } 1599 else if(aContent.match(aStr900)) 1600 { 1601 setFontWeight(FontWeight_900); 1602 } 1603 else if(aContent.match(aStrBolder)) 1604 { 1605 setFontWeight(FontWeight_bolder); 1606 } 1607 else if(aContent.match(aStrLighter)) 1608 { 1609 setFontWeight(FontWeight_lighter); 1610 } 1611 } 1612 break; 1613 } 1614 case SVGTokenDirection: 1615 { 1616 break; 1617 } 1618 case SVGTokenLetterSpacing: 1619 { 1620 break; 1621 } 1622 case SVGTokenTextDecoration: 1623 { 1624 if(aContent.getLength()) 1625 { 1626 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none")); 1627 static rtl::OUString aStrUnderline(rtl::OUString::createFromAscii("underline")); 1628 static rtl::OUString aStrOverline(rtl::OUString::createFromAscii("overline")); 1629 static rtl::OUString aStrLineThrough(rtl::OUString::createFromAscii("line-through")); 1630 static rtl::OUString aStrBlink(rtl::OUString::createFromAscii("blink")); 1631 1632 if(aContent.match(aStrNone)) 1633 { 1634 setTextDecoration(TextDecoration_none); 1635 } 1636 else if(aContent.match(aStrUnderline)) 1637 { 1638 setTextDecoration(TextDecoration_underline); 1639 } 1640 else if(aContent.match(aStrOverline)) 1641 { 1642 setTextDecoration(TextDecoration_overline); 1643 } 1644 else if(aContent.match(aStrLineThrough)) 1645 { 1646 setTextDecoration(TextDecoration_line_through); 1647 } 1648 else if(aContent.match(aStrBlink)) 1649 { 1650 setTextDecoration(TextDecoration_blink); 1651 } 1652 } 1653 break; 1654 } 1655 case SVGTokenUnicodeBidi: 1656 { 1657 break; 1658 } 1659 case SVGTokenWordSpacing: 1660 { 1661 break; 1662 } 1663 case SVGTokenTextAnchor: 1664 { 1665 if(aContent.getLength()) 1666 { 1667 static rtl::OUString aStrStart(rtl::OUString::createFromAscii("start")); 1668 static rtl::OUString aStrMiddle(rtl::OUString::createFromAscii("middle")); 1669 static rtl::OUString aStrEnd(rtl::OUString::createFromAscii("end")); 1670 1671 if(aContent.match(aStrStart)) 1672 { 1673 setTextAnchor(TextAnchor_start); 1674 } 1675 else if(aContent.match(aStrMiddle)) 1676 { 1677 setTextAnchor(TextAnchor_middle); 1678 } 1679 else if(aContent.match(aStrEnd)) 1680 { 1681 setTextAnchor(TextAnchor_end); 1682 } 1683 } 1684 break; 1685 } 1686 case SVGTokenTextAlign: 1687 { 1688 if(aContent.getLength()) 1689 { 1690 static rtl::OUString aStrLeft(rtl::OUString::createFromAscii("left")); 1691 static rtl::OUString aStrRight(rtl::OUString::createFromAscii("right")); 1692 static rtl::OUString aStrCenter(rtl::OUString::createFromAscii("center")); 1693 static rtl::OUString aStrJustify(rtl::OUString::createFromAscii("justify")); 1694 1695 if(aContent.match(aStrLeft)) 1696 { 1697 setTextAlign(TextAlign_left); 1698 } 1699 else if(aContent.match(aStrRight)) 1700 { 1701 setTextAlign(TextAlign_right); 1702 } 1703 else if(aContent.match(aStrCenter)) 1704 { 1705 setTextAlign(TextAlign_center); 1706 } 1707 else if(aContent.match(aStrJustify)) 1708 { 1709 setTextAlign(TextAlign_justify); 1710 } 1711 } 1712 break; 1713 } 1714 case SVGTokenColor: 1715 { 1716 SvgPaint aSvgPaint; 1717 rtl::OUString aURL; 1718 1719 if(readSvgPaint(aContent, aSvgPaint, aURL)) 1720 { 1721 setColor(aSvgPaint); 1722 } 1723 break; 1724 } 1725 case SVGTokenOpacity: 1726 { 1727 SvgNumber aNum; 1728 1729 if(readSingleNumber(aContent, aNum)) 1730 { 1731 setOpacity(SvgNumber(basegfx::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet())); 1732 } 1733 break; 1734 } 1735 case SVGTokenTitle: 1736 { 1737 setTitle(aContent); 1738 break; 1739 } 1740 case SVGTokenDesc: 1741 { 1742 setDesc(aContent); 1743 break; 1744 } 1745 case SVGTokenClipPathProperty: 1746 { 1747 readLocalUrl(aContent, maClipPathXLink); 1748 break; 1749 } 1750 case SVGTokenMask: 1751 { 1752 readLocalUrl(aContent, maMaskXLink); 1753 break; 1754 } 1755 case SVGTokenClipRule: 1756 { 1757 if(aContent.getLength()) 1758 { 1759 if(aContent.match(commonStrings::aStrNonzero)) 1760 { 1761 maClipRule = FillRule_nonzero; 1762 } 1763 else if(aContent.match(commonStrings::aStrEvenOdd)) 1764 { 1765 maClipRule = FillRule_evenodd; 1766 } 1767 } 1768 break; 1769 } 1770 case SVGTokenMarker: 1771 { 1772 readLocalUrl(aContent, maMarkerEndXLink); 1773 maMarkerStartXLink = maMarkerMidXLink = maMarkerEndXLink; 1774 break; 1775 } 1776 case SVGTokenMarkerStart: 1777 { 1778 readLocalUrl(aContent, maMarkerStartXLink); 1779 break; 1780 } 1781 case SVGTokenMarkerMid: 1782 { 1783 readLocalUrl(aContent, maMarkerMidXLink); 1784 break; 1785 } 1786 case SVGTokenMarkerEnd: 1787 { 1788 readLocalUrl(aContent, maMarkerEndXLink); 1789 break; 1790 } 1791 default: 1792 { 1793 break; 1794 } 1795 } 1796 } 1797 1798 const basegfx::BColor* SvgStyleAttributes::getFill() const 1799 { 1800 if(mbIsClipPathContent) 1801 { 1802 static basegfx::BColor aBlack(0.0, 0.0, 0.0); 1803 1804 return &aBlack; 1805 } 1806 else if(maFill.isSet()) 1807 { 1808 if(maFill.isCurrent()) 1809 { 1810 return getColor(); 1811 } 1812 else if(maFill.isOn()) 1813 { 1814 return &maFill.getBColor(); 1815 } 1816 } 1817 else 1818 { 1819 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1820 1821 if(pSvgStyleAttributes) 1822 { 1823 return pSvgStyleAttributes->getFill(); 1824 } 1825 } 1826 1827 return 0; 1828 } 1829 1830 const basegfx::BColor* SvgStyleAttributes::getStroke() const 1831 { 1832 if(mbIsClipPathContent) 1833 { 1834 return 0; 1835 } 1836 else if(maStroke.isSet()) 1837 { 1838 if(maStroke.isCurrent()) 1839 { 1840 return getColor(); 1841 } 1842 else if(maStroke.isOn()) 1843 { 1844 return &maStroke.getBColor(); 1845 } 1846 } 1847 else 1848 { 1849 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1850 1851 if(pSvgStyleAttributes) 1852 { 1853 return pSvgStyleAttributes->getStroke(); 1854 } 1855 } 1856 1857 return 0; 1858 } 1859 1860 const basegfx::BColor& SvgStyleAttributes::getStopColor() const 1861 { 1862 if(maStopColor.isCurrent()) 1863 { 1864 return *getColor(); 1865 } 1866 else 1867 { 1868 return maStopColor.getBColor(); 1869 } 1870 } 1871 1872 const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeFill() const 1873 { 1874 if(mbIsClipPathContent) 1875 { 1876 return 0; 1877 } 1878 else if(mpSvgGradientNodeFill) 1879 { 1880 return mpSvgGradientNodeFill; 1881 } 1882 else 1883 { 1884 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1885 1886 if(pSvgStyleAttributes) 1887 { 1888 return pSvgStyleAttributes->getSvgGradientNodeFill(); 1889 } 1890 } 1891 1892 return 0; 1893 } 1894 1895 const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeStroke() const 1896 { 1897 if(mbIsClipPathContent) 1898 { 1899 return 0; 1900 } 1901 else if(mpSvgGradientNodeStroke) 1902 { 1903 return mpSvgGradientNodeStroke; 1904 } 1905 else 1906 { 1907 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1908 1909 if(pSvgStyleAttributes) 1910 { 1911 return pSvgStyleAttributes->getSvgGradientNodeStroke(); 1912 } 1913 } 1914 1915 return 0; 1916 } 1917 1918 const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeFill() const 1919 { 1920 if(mbIsClipPathContent) 1921 { 1922 return 0; 1923 } 1924 else if(mpSvgPatternNodeFill) 1925 { 1926 return mpSvgPatternNodeFill; 1927 } 1928 else 1929 { 1930 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1931 1932 if(pSvgStyleAttributes) 1933 { 1934 return pSvgStyleAttributes->getSvgPatternNodeFill(); 1935 } 1936 } 1937 1938 return 0; 1939 } 1940 1941 const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeStroke() const 1942 { 1943 if(mbIsClipPathContent) 1944 { 1945 return 0; 1946 } 1947 else if(mpSvgPatternNodeStroke) 1948 { 1949 return mpSvgPatternNodeStroke; 1950 } 1951 else 1952 { 1953 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1954 1955 if(pSvgStyleAttributes) 1956 { 1957 return pSvgStyleAttributes->getSvgPatternNodeStroke(); 1958 } 1959 } 1960 1961 return 0; 1962 } 1963 1964 SvgNumber SvgStyleAttributes::getStrokeWidth() const 1965 { 1966 if(mbIsClipPathContent) 1967 { 1968 return SvgNumber(0.0); 1969 } 1970 else if(maStrokeWidth.isSet()) 1971 { 1972 return maStrokeWidth; 1973 } 1974 1975 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1976 1977 if(pSvgStyleAttributes) 1978 { 1979 return pSvgStyleAttributes->getStrokeWidth(); 1980 } 1981 1982 // default is 1 1983 return SvgNumber(1.0); 1984 } 1985 1986 SvgNumber SvgStyleAttributes::getStopOpacity() const 1987 { 1988 if(maStopOpacity.isSet()) 1989 { 1990 return maStopOpacity; 1991 } 1992 1993 // default is 1 1994 return SvgNumber(1.0); 1995 } 1996 1997 SvgNumber SvgStyleAttributes::getFillOpacity() const 1998 { 1999 if(mbIsClipPathContent) 2000 { 2001 return SvgNumber(1.0); 2002 } 2003 else if(maFillOpacity.isSet()) 2004 { 2005 return maFillOpacity; 2006 } 2007 2008 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2009 2010 if(pSvgStyleAttributes) 2011 { 2012 return pSvgStyleAttributes->getFillOpacity(); 2013 } 2014 2015 // default is 1 2016 return SvgNumber(1.0); 2017 } 2018 2019 FillRule SvgStyleAttributes::getFillRule() const 2020 { 2021 if(FillRule_notset != maFillRule) 2022 { 2023 return maFillRule; 2024 } 2025 2026 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2027 2028 if(pSvgStyleAttributes) 2029 { 2030 return pSvgStyleAttributes->getFillRule(); 2031 } 2032 2033 // default is NonZero 2034 return FillRule_nonzero; 2035 } 2036 2037 const SvgNumberVector& SvgStyleAttributes::getStrokeDasharray() const 2038 { 2039 if(!maStrokeDasharray.empty()) 2040 { 2041 return maStrokeDasharray; 2042 } 2043 else if(getStrokeDasharraySet()) 2044 { 2045 // #121221# is set to empty *by purpose*, do not visit parent styles 2046 return maStrokeDasharray; 2047 } 2048 2049 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2050 2051 if(pSvgStyleAttributes) 2052 { 2053 return pSvgStyleAttributes->getStrokeDasharray(); 2054 } 2055 2056 // default empty 2057 return maStrokeDasharray; 2058 } 2059 2060 SvgNumber SvgStyleAttributes::getStrokeDashOffset() const 2061 { 2062 if(maStrokeDashOffset.isSet()) 2063 { 2064 return maStrokeDashOffset; 2065 } 2066 2067 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2068 2069 if(pSvgStyleAttributes) 2070 { 2071 return pSvgStyleAttributes->getStrokeDashOffset(); 2072 } 2073 2074 // default is 0 2075 return SvgNumber(0.0); 2076 } 2077 2078 StrokeLinecap SvgStyleAttributes::getStrokeLinecap() const 2079 { 2080 if(maStrokeLinecap != StrokeLinecap_notset) 2081 { 2082 return maStrokeLinecap; 2083 } 2084 2085 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2086 2087 if(pSvgStyleAttributes) 2088 { 2089 return pSvgStyleAttributes->getStrokeLinecap(); 2090 } 2091 2092 // default is StrokeLinecap_butt 2093 return StrokeLinecap_butt; 2094 } 2095 2096 StrokeLinejoin SvgStyleAttributes::getStrokeLinejoin() const 2097 { 2098 if(maStrokeLinejoin != StrokeLinejoin_notset) 2099 { 2100 return maStrokeLinejoin; 2101 } 2102 2103 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2104 2105 if(pSvgStyleAttributes) 2106 { 2107 return pSvgStyleAttributes->getStrokeLinejoin(); 2108 } 2109 2110 // default is StrokeLinejoin_butt 2111 return StrokeLinejoin_miter; 2112 } 2113 2114 SvgNumber SvgStyleAttributes::getStrokeMiterLimit() const 2115 { 2116 if(maStrokeMiterLimit.isSet()) 2117 { 2118 return maStrokeMiterLimit; 2119 } 2120 2121 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2122 2123 if(pSvgStyleAttributes) 2124 { 2125 return pSvgStyleAttributes->getStrokeMiterLimit(); 2126 } 2127 2128 // default is 4 2129 return SvgNumber(4.0); 2130 } 2131 2132 SvgNumber SvgStyleAttributes::getStrokeOpacity() const 2133 { 2134 if(maStrokeOpacity.isSet()) 2135 { 2136 return maStrokeOpacity; 2137 } 2138 2139 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2140 2141 if(pSvgStyleAttributes) 2142 { 2143 return pSvgStyleAttributes->getStrokeOpacity(); 2144 } 2145 2146 // default is 1 2147 return SvgNumber(1.0); 2148 } 2149 2150 const SvgStringVector& SvgStyleAttributes::getFontFamily() const 2151 { 2152 if(!maFontFamily.empty()) 2153 { 2154 return maFontFamily; 2155 } 2156 2157 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2158 2159 if(pSvgStyleAttributes) 2160 { 2161 return pSvgStyleAttributes->getFontFamily(); 2162 } 2163 2164 // default is empty 2165 return maFontFamily; 2166 } 2167 2168 SvgNumber SvgStyleAttributes::getFontSize() const 2169 { 2170 if(maFontSize.isSet()) 2171 { 2172 return maFontSize; 2173 } 2174 2175 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2176 2177 if(pSvgStyleAttributes) 2178 { 2179 return pSvgStyleAttributes->getFontSize(); 2180 } 2181 2182 // default is 'medium' 2183 return SvgNumber(12.0); 2184 } 2185 2186 FontStretch SvgStyleAttributes::getFontStretch() const 2187 { 2188 if(maFontStretch != FontStretch_notset) 2189 { 2190 if(FontStretch_wider != maFontStretch && FontStretch_narrower != maFontStretch) 2191 { 2192 return maFontStretch; 2193 } 2194 } 2195 2196 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2197 2198 if(pSvgStyleAttributes) 2199 { 2200 FontStretch aInherited = pSvgStyleAttributes->getFontStretch(); 2201 2202 if(FontStretch_wider == maFontStretch) 2203 { 2204 aInherited = getWider(aInherited); 2205 } 2206 else if(FontStretch_narrower == maFontStretch) 2207 { 2208 aInherited = getNarrower(aInherited); 2209 } 2210 2211 return aInherited; 2212 } 2213 2214 // default is FontStretch_normal 2215 return FontStretch_normal; 2216 } 2217 2218 FontStyle SvgStyleAttributes::getFontStyle() const 2219 { 2220 if(maFontStyle != FontStyle_notset) 2221 { 2222 return maFontStyle; 2223 } 2224 2225 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2226 2227 if(pSvgStyleAttributes) 2228 { 2229 return pSvgStyleAttributes->getFontStyle(); 2230 } 2231 2232 // default is FontStyle_normal 2233 return FontStyle_normal; 2234 } 2235 2236 FontWeight SvgStyleAttributes::getFontWeight() const 2237 { 2238 if(maFontWeight != FontWeight_notset) 2239 { 2240 if(FontWeight_bolder != maFontWeight && FontWeight_lighter != maFontWeight) 2241 { 2242 return maFontWeight; 2243 } 2244 } 2245 2246 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2247 2248 if(pSvgStyleAttributes) 2249 { 2250 FontWeight aInherited = pSvgStyleAttributes->getFontWeight(); 2251 2252 if(FontWeight_bolder == maFontWeight) 2253 { 2254 aInherited = getBolder(aInherited); 2255 } 2256 else if(FontWeight_lighter == maFontWeight) 2257 { 2258 aInherited = getLighter(aInherited); 2259 } 2260 2261 return aInherited; 2262 } 2263 2264 // default is FontWeight_400 (FontWeight_normal) 2265 return FontWeight_400; 2266 } 2267 2268 TextAlign SvgStyleAttributes::getTextAlign() const 2269 { 2270 if(maTextAlign != TextAlign_notset) 2271 { 2272 return maTextAlign; 2273 } 2274 2275 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2276 2277 if(pSvgStyleAttributes) 2278 { 2279 return pSvgStyleAttributes->getTextAlign(); 2280 } 2281 2282 // default is TextAlign_left 2283 return TextAlign_left; 2284 } 2285 2286 const SvgStyleAttributes* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const 2287 { 2288 if(maTextDecoration != TextDecoration_notset) 2289 { 2290 return this; 2291 } 2292 2293 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2294 2295 if(pSvgStyleAttributes) 2296 { 2297 return pSvgStyleAttributes->getTextDecorationDefiningSvgStyleAttributes(); 2298 } 2299 2300 // default is 0 2301 return 0; 2302 } 2303 2304 TextDecoration SvgStyleAttributes::getTextDecoration() const 2305 { 2306 const SvgStyleAttributes* pDefining = getTextDecorationDefiningSvgStyleAttributes(); 2307 2308 if(pDefining) 2309 { 2310 return pDefining->maTextDecoration; 2311 } 2312 else 2313 { 2314 // default is TextDecoration_none 2315 return TextDecoration_none; 2316 } 2317 } 2318 2319 TextAnchor SvgStyleAttributes::getTextAnchor() const 2320 { 2321 if(maTextAnchor != TextAnchor_notset) 2322 { 2323 return maTextAnchor; 2324 } 2325 2326 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2327 2328 if(pSvgStyleAttributes) 2329 { 2330 return pSvgStyleAttributes->getTextAnchor(); 2331 } 2332 2333 // default is TextAnchor_start 2334 return TextAnchor_start; 2335 } 2336 2337 const basegfx::BColor* SvgStyleAttributes::getColor() const 2338 { 2339 if(maColor.isSet()) 2340 { 2341 if(maColor.isCurrent()) 2342 { 2343 OSL_ENSURE(false, "Svg error: current color uses current color (!)"); 2344 return 0; 2345 } 2346 else if(maColor.isOn()) 2347 { 2348 return &maColor.getBColor(); 2349 } 2350 } 2351 else 2352 { 2353 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2354 2355 if(pSvgStyleAttributes) 2356 { 2357 return pSvgStyleAttributes->getColor(); 2358 } 2359 } 2360 2361 return 0; 2362 } 2363 2364 rtl::OUString SvgStyleAttributes::getMarkerStartXLink() const 2365 { 2366 if(maMarkerStartXLink.getLength()) 2367 { 2368 return maMarkerStartXLink; 2369 } 2370 2371 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2372 2373 if(pSvgStyleAttributes) 2374 { 2375 return pSvgStyleAttributes->getMarkerStartXLink(); 2376 } 2377 2378 return rtl::OUString(); 2379 } 2380 2381 const SvgMarkerNode* SvgStyleAttributes::accessMarkerStartXLink() const 2382 { 2383 if(!mpMarkerStartXLink) 2384 { 2385 const rtl::OUString aMarker(getMarkerStartXLink()); 2386 2387 if(aMarker.getLength()) 2388 { 2389 const_cast< SvgStyleAttributes* >(this)->mpMarkerStartXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerStartXLink())); 2390 } 2391 } 2392 2393 return mpMarkerStartXLink; 2394 } 2395 2396 rtl::OUString SvgStyleAttributes::getMarkerMidXLink() const 2397 { 2398 if(maMarkerMidXLink.getLength()) 2399 { 2400 return maMarkerMidXLink; 2401 } 2402 2403 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2404 2405 if(pSvgStyleAttributes) 2406 { 2407 return pSvgStyleAttributes->getMarkerMidXLink(); 2408 } 2409 2410 return rtl::OUString(); 2411 } 2412 2413 const SvgMarkerNode* SvgStyleAttributes::accessMarkerMidXLink() const 2414 { 2415 if(!mpMarkerMidXLink) 2416 { 2417 const rtl::OUString aMarker(getMarkerMidXLink()); 2418 2419 if(aMarker.getLength()) 2420 { 2421 const_cast< SvgStyleAttributes* >(this)->mpMarkerMidXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerMidXLink())); 2422 } 2423 } 2424 2425 return mpMarkerMidXLink; 2426 } 2427 2428 rtl::OUString SvgStyleAttributes::getMarkerEndXLink() const 2429 { 2430 if(maMarkerEndXLink.getLength()) 2431 { 2432 return maMarkerEndXLink; 2433 } 2434 2435 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2436 2437 if(pSvgStyleAttributes) 2438 { 2439 return pSvgStyleAttributes->getMarkerEndXLink(); 2440 } 2441 2442 return rtl::OUString(); 2443 } 2444 2445 const SvgMarkerNode* SvgStyleAttributes::accessMarkerEndXLink() const 2446 { 2447 if(!mpMarkerEndXLink) 2448 { 2449 const rtl::OUString aMarker(getMarkerEndXLink()); 2450 2451 if(aMarker.getLength()) 2452 { 2453 const_cast< SvgStyleAttributes* >(this)->mpMarkerEndXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerEndXLink())); 2454 } 2455 } 2456 2457 return mpMarkerEndXLink; 2458 } 2459 2460 } // end of namespace svgreader 2461 } // end of namespace svgio 2462 2463 ////////////////////////////////////////////////////////////////////////////// 2464 // eof 2465