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