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.supportsParentStyle() && 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 // process 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 // process 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 bool SvgStyleAttributes::prepare_singleMarker( 750 drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives, 751 basegfx::B2DHomMatrix& rMarkerTransform, 752 basegfx::B2DRange& rClipRange, 753 const SvgMarkerNode& rMarker) const 754 { 755 // reset return values 756 rMarkerTransform.identity(); 757 rClipRange.reset(); 758 759 // get marker primitive representation 760 rMarkerPrimitives = rMarker.getMarkerPrimitives(); 761 762 if(rMarkerPrimitives.hasElements()) 763 { 764 basegfx::B2DRange aPrimitiveRange(0.0, 0.0, 1.0, 1.0); 765 const basegfx::B2DRange* pViewBox = rMarker.getViewBox(); 766 767 if(pViewBox) 768 { 769 aPrimitiveRange = *pViewBox; 770 } 771 772 if(aPrimitiveRange.getWidth() > 0.0 && aPrimitiveRange.getHeight() > 0.0) 773 { 774 double fTargetWidth(rMarker.getMarkerWidth().isSet() ? rMarker.getMarkerWidth().solve(mrOwner, xcoordinate) : 3.0); 775 double fTargetHeight(rMarker.getMarkerHeight().isSet() ? rMarker.getMarkerHeight().solve(mrOwner, xcoordinate) : 3.0); 776 const bool bStrokeWidth(SvgMarkerNode::strokeWidth == rMarker.getMarkerUnits()); 777 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner, length) : 1.0); 778 779 if(bStrokeWidth) 780 { 781 // relative to strokeWidth 782 fTargetWidth *= fStrokeWidth; 783 fTargetHeight *= fStrokeWidth; 784 } 785 786 if(fTargetWidth > 0.0 && fTargetHeight > 0.0) 787 { 788 // create mapping 789 const basegfx::B2DRange aTargetRange(0.0, 0.0, fTargetWidth, fTargetHeight); 790 const SvgAspectRatio& rRatio = rMarker.getSvgAspectRatio(); 791 792 if(rRatio.isSet()) 793 { 794 // let mapping be created from SvgAspectRatio 795 rMarkerTransform = rRatio.createMapping(aTargetRange, aPrimitiveRange); 796 797 if(rRatio.isMeetOrSlice()) 798 { 799 // need to clip 800 rClipRange = aPrimitiveRange; 801 } 802 } 803 else 804 { 805 if(!pViewBox) 806 { 807 if(bStrokeWidth) 808 { 809 // adapt to strokewidth if needed 810 rMarkerTransform.scale(fStrokeWidth, fStrokeWidth); 811 } 812 } 813 else 814 { 815 // choose default mapping 816 rMarkerTransform = rRatio.createLinearMapping(aTargetRange, aPrimitiveRange); 817 } 818 } 819 820 // get and apply reference point. Initially it's in marker local coordinate system 821 basegfx::B2DPoint aRefPoint( 822 rMarker.getRefX().isSet() ? rMarker.getRefX().solve(mrOwner, xcoordinate) : 0.0, 823 rMarker.getRefY().isSet() ? rMarker.getRefY().solve(mrOwner, ycoordinate) : 0.0); 824 825 // apply MarkerTransform to have it in mapped coordinates 826 aRefPoint *= rMarkerTransform; 827 828 // apply by moving RepPoint to (0.0) 829 rMarkerTransform.translate(-aRefPoint.getX(), -aRefPoint.getY()); 830 831 return true; 832 } 833 } 834 } 835 836 return false; 837 } 838 839 void SvgStyleAttributes::add_markers( 840 const basegfx::B2DPolyPolygon& rPath, 841 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 842 const basegfx::tools::PointIndexSet* pHelpPointIndices) const 843 { 844 // try to access linked markers 845 const SvgMarkerNode* pStart = accessMarkerStartXLink(); 846 const SvgMarkerNode* pMid = accessMarkerMidXLink(); 847 const SvgMarkerNode* pEnd = accessMarkerEndXLink(); 848 849 if(pStart || pMid || pEnd) 850 { 851 const sal_uInt32 nSubPathCount(rPath.count()); 852 853 if(nSubPathCount) 854 { 855 // remember prepared marker; pStart, pMid and pEnd may all be equal when 856 // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end', 857 // see 'case SVGTokenMarker' in this file; thus in this case only one common 858 // marker in primitive form will be prepared 859 const SvgMarkerNode* pPrepared = 0; 860 861 // values for the prepared marker, results of prepare_singleMarker 862 drawinglayer::primitive2d::Primitive2DSequence aPreparedMarkerPrimitives; 863 basegfx::B2DHomMatrix aPreparedMarkerTransform; 864 basegfx::B2DRange aPreparedMarkerClipRange; 865 866 for (sal_uInt32 a(0); a < nSubPathCount; a++) 867 { 868 // iterate over sub-paths 869 const basegfx::B2DPolygon aSubPolygonPath(rPath.getB2DPolygon(a)); 870 const sal_uInt32 nSubPolygonPointCount(aSubPolygonPath.count()); 871 const bool bSubPolygonPathIsClosed(aSubPolygonPath.isClosed()); 872 873 if(nSubPolygonPointCount) 874 { 875 // for each sub-path, create one marker per point (when closed, two markers 876 // need to pe created for the 1st point) 877 const sal_uInt32 nTargetMarkerCount(bSubPolygonPathIsClosed ? nSubPolygonPointCount + 1 : nSubPolygonPointCount); 878 879 for (sal_uInt32 b(0); b < nTargetMarkerCount; b++) 880 { 881 const bool bIsFirstMarker(!a && !b); 882 const bool bIsLastMarker(nSubPathCount - 1 == a && nTargetMarkerCount - 1 == b); 883 const SvgMarkerNode* pNeeded = 0; 884 885 if(bIsFirstMarker) 886 { 887 // 1st point in 1st sub-polygon, use pStart 888 pNeeded = pStart; 889 } 890 else if(bIsLastMarker) 891 { 892 // last point in last sub-polygon, use pEnd 893 pNeeded = pEnd; 894 } 895 else 896 { 897 // anything in-between, use pMid 898 pNeeded = pMid; 899 } 900 901 if(pHelpPointIndices && !pHelpPointIndices->empty()) 902 { 903 const basegfx::tools::PointIndexSet::const_iterator aFound( 904 pHelpPointIndices->find(basegfx::tools::PointIndex(a, b))); 905 906 if(aFound != pHelpPointIndices->end()) 907 { 908 // this point is a pure helper point; do not create a marker for it 909 continue; 910 } 911 } 912 913 if(!pNeeded) 914 { 915 // no marker needs to be created for this point 916 continue; 917 } 918 919 if(pPrepared != pNeeded) 920 { 921 // if needed marker is not yet prepared, do it now 922 if(prepare_singleMarker(aPreparedMarkerPrimitives, aPreparedMarkerTransform, aPreparedMarkerClipRange, *pNeeded)) 923 { 924 pPrepared = pNeeded; 925 } 926 else 927 { 928 // error: could not prepare given marker 929 OSL_ENSURE(false, "OOps, could not prepare given marker as primitives (!)"); 930 pPrepared = 0; 931 continue; 932 } 933 } 934 935 // prepare complete transform 936 basegfx::B2DHomMatrix aCombinedTransform(aPreparedMarkerTransform); 937 938 // get rotation 939 if(pPrepared->getOrientAuto()) 940 { 941 const sal_uInt32 nPointIndex(b % nSubPolygonPointCount); 942 943 // get entering and leaving tangents; this will search backward/froward 944 // in the polygon to find tangents unequal to zero, skipping empty edges 945 // see basegfx descriptions) 946 // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker 947 // and entering tangent for end marker. To achieve this (if wanted) it is possibe 948 // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker. 949 // This is not done here, see comment 14 in task #1232379# 950 // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute 951 basegfx::B2DVector aEntering( 952 basegfx::tools::getTangentEnteringPoint( 953 aSubPolygonPath, 954 nPointIndex)); 955 basegfx::B2DVector aLeaving( 956 basegfx::tools::getTangentLeavingPoint( 957 aSubPolygonPath, 958 nPointIndex)); 959 const bool bEntering(!aEntering.equalZero()); 960 const bool bLeaving(!aLeaving.equalZero()); 961 962 if(bEntering || bLeaving) 963 { 964 basegfx::B2DVector aSum(0.0, 0.0); 965 966 if(bEntering) 967 { 968 aSum += aEntering.normalize(); 969 } 970 971 if(bLeaving) 972 { 973 aSum += aLeaving.normalize(); 974 } 975 976 if(!aSum.equalZero()) 977 { 978 const double fAngle(atan2(aSum.getY(), aSum.getX())); 979 980 // apply rotation 981 aCombinedTransform.rotate(fAngle); 982 } 983 } 984 } 985 else 986 { 987 // apply rotation 988 aCombinedTransform.rotate(pPrepared->getAngle()); 989 } 990 991 // get and apply target position 992 const basegfx::B2DPoint aPoint(aSubPolygonPath.getB2DPoint(b % nSubPolygonPointCount)); 993 994 aCombinedTransform.translate(aPoint.getX(), aPoint.getY()); 995 996 // prepare marker 997 drawinglayer::primitive2d::Primitive2DReference xMarker( 998 new drawinglayer::primitive2d::TransformPrimitive2D( 999 aCombinedTransform, 1000 aPreparedMarkerPrimitives)); 1001 1002 if(!aPreparedMarkerClipRange.isEmpty()) 1003 { 1004 // marker needs to be clipped, it's bigger as the mapping 1005 basegfx::B2DPolyPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(aPreparedMarkerClipRange)); 1006 1007 aClipPolygon.transform(aCombinedTransform); 1008 xMarker = new drawinglayer::primitive2d::MaskPrimitive2D( 1009 aClipPolygon, 1010 drawinglayer::primitive2d::Primitive2DSequence(&xMarker, 1)); 1011 } 1012 1013 // add marker 1014 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMarker); 1015 } 1016 } 1017 } 1018 } 1019 } 1020 } 1021 1022 void SvgStyleAttributes::add_path( 1023 const basegfx::B2DPolyPolygon& rPath, 1024 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 1025 const basegfx::tools::PointIndexSet* pHelpPointIndices) const 1026 { 1027 if(!rPath.count()) 1028 { 1029 // no geometry at all 1030 return; 1031 } 1032 1033 const basegfx::B2DRange aGeoRange(rPath.getB2DRange()); 1034 1035 if(aGeoRange.isEmpty()) 1036 { 1037 // no geometry range 1038 return; 1039 } 1040 1041 const double fOpacity(getOpacity().getNumber()); 1042 1043 if(basegfx::fTools::equalZero(fOpacity)) 1044 { 1045 // not visible 1046 return; 1047 } 1048 1049 // check if it's a line 1050 const bool bNoWidth(basegfx::fTools::equalZero(aGeoRange.getWidth())); 1051 const bool bNoHeight(basegfx::fTools::equalZero(aGeoRange.getHeight())); 1052 const bool bIsTwoPointLine(1 == rPath.count() 1053 && !rPath.areControlPointsUsed() 1054 && 2 == rPath.getB2DPolygon(0).count()); 1055 const bool bIsLine(bIsTwoPointLine || bNoWidth || bNoHeight); 1056 1057 if(!bIsLine) 1058 { 1059 // create fill 1060 basegfx::B2DPolyPolygon aPath(rPath); 1061 const bool bNeedToCheckClipRule(SVGTokenPath == mrOwner.getType() || SVGTokenPolygon == mrOwner.getType()); 1062 const bool bClipPathIsNonzero(!bIsLine && bNeedToCheckClipRule && mbIsClipPathContent && FillRule_nonzero == maClipRule); 1063 const bool bFillRuleIsNonzero(!bIsLine && bNeedToCheckClipRule && !mbIsClipPathContent && FillRule_nonzero == getFillRule()); 1064 1065 if(bClipPathIsNonzero || bFillRuleIsNonzero) 1066 { 1067 // nonzero is wanted, solve geometrically (see description on basegfx) 1068 aPath = basegfx::tools::createNonzeroConform(aPath); 1069 } 1070 1071 add_fill(aPath, rTarget, aGeoRange); 1072 } 1073 1074 // create stroke 1075 add_stroke(rPath, rTarget, aGeoRange); 1076 1077 // Svg supports markers for path, polygon, polyline and line 1078 if(SVGTokenPath == mrOwner.getType() || // path 1079 SVGTokenPolygon == mrOwner.getType() || // polygon, polyline 1080 SVGTokenLine == mrOwner.getType()) // line 1081 { 1082 // try to add markers 1083 add_markers(rPath, rTarget, pHelpPointIndices); 1084 } 1085 } 1086 1087 void SvgStyleAttributes::add_postProcess( 1088 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 1089 const drawinglayer::primitive2d::Primitive2DSequence& rSource, 1090 const basegfx::B2DHomMatrix* pTransform) const 1091 { 1092 if(rSource.hasElements()) 1093 { 1094 const double fOpacity(getOpacity().getNumber()); 1095 1096 if(basegfx::fTools::equalZero(fOpacity)) 1097 { 1098 return; 1099 } 1100 1101 drawinglayer::primitive2d::Primitive2DSequence aSource(rSource); 1102 1103 if(basegfx::fTools::less(fOpacity, 1.0)) 1104 { 1105 // embed in UnifiedTransparencePrimitive2D 1106 const drawinglayer::primitive2d::Primitive2DReference xRef( 1107 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 1108 aSource, 1109 1.0 - fOpacity)); 1110 1111 aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1); 1112 } 1113 1114 if(pTransform) 1115 { 1116 // create embedding group element with transformation. This applies the given 1117 // transformation to the graphical content, but *not* to mask and/or clip (as needed) 1118 const drawinglayer::primitive2d::Primitive2DReference xRef( 1119 new drawinglayer::primitive2d::TransformPrimitive2D( 1120 *pTransform, 1121 aSource)); 1122 1123 aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1); 1124 } 1125 1126 if(getClipPathXLink().getLength()) 1127 { 1128 // try to access linked ClipPath 1129 const SvgClipPathNode* mpClip = dynamic_cast< const SvgClipPathNode* >(mrOwner.getDocument().findSvgNodeById(getClipPathXLink())); 1130 1131 if(mpClip) 1132 { 1133 // #i124852# transform may be needed when userSpaceOnUse 1134 mpClip->apply(aSource, pTransform); 1135 } 1136 } 1137 1138 if(aSource.hasElements()) // test again, applied clipPath may have lead to empty geometry 1139 { 1140 if(getMaskXLink().getLength()) 1141 { 1142 // try to access linked Mask 1143 const SvgMaskNode* mpMask = dynamic_cast< const SvgMaskNode* >(mrOwner.getDocument().findSvgNodeById(getMaskXLink())); 1144 1145 if(mpMask) 1146 { 1147 // #i124852# transform may be needed when userSpaceOnUse 1148 mpMask->apply(aSource, pTransform); 1149 } 1150 } 1151 1152 if(aSource.hasElements()) // test again, applied mask may have lead to empty geometry 1153 { 1154 // append to current target 1155 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSource); 1156 } 1157 } 1158 } 1159 } 1160 1161 SvgStyleAttributes::SvgStyleAttributes(SvgNode& rOwner) 1162 : mrOwner(rOwner), 1163 mpCssStyleParent(0), 1164 maFill(), 1165 maStroke(), 1166 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true), 1167 maStrokeWidth(), 1168 maStopOpacity(), 1169 mpSvgGradientNodeFill(0), 1170 mpSvgGradientNodeStroke(0), 1171 mpSvgPatternNodeFill(0), 1172 mpSvgPatternNodeStroke(0), 1173 maFillOpacity(), 1174 maStrokeDasharray(), 1175 maStrokeDashOffset(), 1176 maStrokeLinecap(StrokeLinecap_notset), 1177 maStrokeLinejoin(StrokeLinejoin_notset), 1178 maStrokeMiterLimit(), 1179 maStrokeOpacity(), 1180 maFontFamily(), 1181 maFontSize(), 1182 maFontStretch(FontStretch_notset), 1183 maFontStyle(FontStyle_notset), 1184 maFontVariant(FontVariant_notset), 1185 maFontWeight(FontWeight_notset), 1186 maTextAlign(TextAlign_notset), 1187 maTextDecoration(TextDecoration_notset), 1188 maTextAnchor(TextAnchor_notset), 1189 maColor(), 1190 maOpacity(1.0), 1191 maTitle(), 1192 maDesc(), 1193 maClipPathXLink(), 1194 maMaskXLink(), 1195 maMarkerStartXLink(), 1196 mpMarkerStartXLink(0), 1197 maMarkerMidXLink(), 1198 mpMarkerMidXLink(0), 1199 maMarkerEndXLink(), 1200 mpMarkerEndXLink(0), 1201 maFillRule(FillRule_notset), 1202 maClipRule(FillRule_nonzero), 1203 maBaselineShift(BaselineShift_Baseline), 1204 maBaselineShiftNumber(0), 1205 mbIsClipPathContent(SVGTokenClipPathNode == mrOwner.getType()), 1206 mbStrokeDasharraySet(false) 1207 { 1208 if(!mbIsClipPathContent) 1209 { 1210 const SvgStyleAttributes* pParentStyle = getParentStyle(); 1211 1212 if(pParentStyle) 1213 { 1214 mbIsClipPathContent = pParentStyle->mbIsClipPathContent; 1215 } 1216 } 1217 } 1218 1219 SvgStyleAttributes::~SvgStyleAttributes() 1220 { 1221 } 1222 1223 void SvgStyleAttributes::parseStyleAttribute(const rtl::OUString& /* rTokenName */, SVGToken aSVGToken, const rtl::OUString& aContent) 1224 { 1225 switch(aSVGToken) 1226 { 1227 case SVGTokenFill: 1228 { 1229 SvgPaint aSvgPaint; 1230 rtl::OUString aURL; 1231 1232 if(readSvgPaint(aContent, aSvgPaint, aURL)) 1233 { 1234 setFill(aSvgPaint); 1235 } 1236 else if(aURL.getLength()) 1237 { 1238 const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL); 1239 1240 if(pNode) 1241 { 1242 if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType()) 1243 { 1244 setSvgGradientNodeFill(static_cast< const SvgGradientNode* >(pNode)); 1245 } 1246 else if(SVGTokenPattern == pNode->getType()) 1247 { 1248 setSvgPatternNodeFill(static_cast< const SvgPatternNode* >(pNode)); 1249 } 1250 } 1251 } 1252 break; 1253 } 1254 case SVGTokenFillOpacity: 1255 { 1256 SvgNumber aNum; 1257 1258 if(readSingleNumber(aContent, aNum)) 1259 { 1260 if(aNum.isPositive()) 1261 { 1262 setFillOpacity(aNum); 1263 } 1264 } 1265 break; 1266 } 1267 case SVGTokenFillRule: 1268 { 1269 if(aContent.getLength()) 1270 { 1271 if(aContent.match(commonStrings::aStrNonzero)) 1272 { 1273 maFillRule = FillRule_nonzero; 1274 } 1275 else if(aContent.match(commonStrings::aStrEvenOdd)) 1276 { 1277 maFillRule = FillRule_evenodd; 1278 } 1279 } 1280 break; 1281 } 1282 case SVGTokenStroke: 1283 { 1284 SvgPaint aSvgPaint; 1285 rtl::OUString aURL; 1286 1287 if(readSvgPaint(aContent, aSvgPaint, aURL)) 1288 { 1289 setStroke(aSvgPaint); 1290 } 1291 else if(aURL.getLength()) 1292 { 1293 const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL); 1294 1295 if(pNode) 1296 { 1297 if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType()) 1298 { 1299 setSvgGradientNodeStroke(static_cast< const SvgGradientNode* >(pNode)); 1300 } 1301 else if(SVGTokenPattern == pNode->getType()) 1302 { 1303 setSvgPatternNodeStroke(static_cast< const SvgPatternNode* >(pNode)); 1304 } 1305 } 1306 } 1307 break; 1308 } 1309 case SVGTokenStrokeDasharray: 1310 { 1311 if(aContent.getLength()) 1312 { 1313 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none")); 1314 SvgNumberVector aVector; 1315 1316 if(aContent.match(aStrNone)) 1317 { 1318 // #121221# The special value 'none' needs to be handled 1319 // in the sense that *when* it is set, the parent shall not 1320 // be used. Before this was only dependent on the array being 1321 // empty 1322 setStrokeDasharraySet(true); 1323 } 1324 else if(readSvgNumberVector(aContent, aVector)) 1325 { 1326 setStrokeDasharray(aVector); 1327 } 1328 } 1329 break; 1330 } 1331 case SVGTokenStrokeDashoffset: 1332 { 1333 SvgNumber aNum; 1334 1335 if(readSingleNumber(aContent, aNum)) 1336 { 1337 if(aNum.isPositive()) 1338 { 1339 setStrokeDashOffset(aNum); 1340 } 1341 } 1342 break; 1343 } 1344 case SVGTokenStrokeLinecap: 1345 { 1346 if(aContent.getLength()) 1347 { 1348 static rtl::OUString aStrButt(rtl::OUString::createFromAscii("butt")); 1349 static rtl::OUString aStrRound(rtl::OUString::createFromAscii("round")); 1350 static rtl::OUString aStrSquare(rtl::OUString::createFromAscii("square")); 1351 1352 if(aContent.match(aStrButt)) 1353 { 1354 setStrokeLinecap(StrokeLinecap_butt); 1355 } 1356 else if(aContent.match(aStrRound)) 1357 { 1358 setStrokeLinecap(StrokeLinecap_round); 1359 } 1360 else if(aContent.match(aStrSquare)) 1361 { 1362 setStrokeLinecap(StrokeLinecap_square); 1363 } 1364 } 1365 break; 1366 } 1367 case SVGTokenStrokeLinejoin: 1368 { 1369 if(aContent.getLength()) 1370 { 1371 static rtl::OUString aStrMiter(rtl::OUString::createFromAscii("miter")); 1372 static rtl::OUString aStrRound(rtl::OUString::createFromAscii("round")); 1373 static rtl::OUString aStrBevel(rtl::OUString::createFromAscii("bevel")); 1374 1375 if(aContent.match(aStrMiter)) 1376 { 1377 setStrokeLinejoin(StrokeLinejoin_miter); 1378 } 1379 else if(aContent.match(aStrRound)) 1380 { 1381 setStrokeLinejoin(StrokeLinejoin_round); 1382 } 1383 else if(aContent.match(aStrBevel)) 1384 { 1385 setStrokeLinejoin(StrokeLinejoin_bevel); 1386 } 1387 } 1388 break; 1389 } 1390 case SVGTokenStrokeMiterlimit: 1391 { 1392 SvgNumber aNum; 1393 1394 if(readSingleNumber(aContent, aNum)) 1395 { 1396 if(aNum.isPositive()) 1397 { 1398 setStrokeMiterLimit(aNum); 1399 } 1400 } 1401 break; 1402 } 1403 case SVGTokenStrokeOpacity: 1404 { 1405 SvgNumber aNum; 1406 1407 if(readSingleNumber(aContent, aNum)) 1408 { 1409 if(aNum.isPositive()) 1410 { 1411 setStrokeOpacity(aNum); 1412 } 1413 } 1414 break; 1415 } 1416 case SVGTokenStrokeWidth: 1417 { 1418 SvgNumber aNum; 1419 1420 if(readSingleNumber(aContent, aNum)) 1421 { 1422 if(aNum.isPositive()) 1423 { 1424 setStrokeWidth(aNum); 1425 } 1426 } 1427 break; 1428 } 1429 case SVGTokenStopColor: 1430 { 1431 SvgPaint aSvgPaint; 1432 rtl::OUString aURL; 1433 1434 if(readSvgPaint(aContent, aSvgPaint, aURL)) 1435 { 1436 setStopColor(aSvgPaint); 1437 } 1438 break; 1439 } 1440 case SVGTokenStopOpacity: 1441 { 1442 SvgNumber aNum; 1443 1444 if(readSingleNumber(aContent, aNum)) 1445 { 1446 if(aNum.isPositive()) 1447 { 1448 setStopOpacity(aNum); 1449 } 1450 } 1451 break; 1452 } 1453 case SVGTokenFont: 1454 { 1455 break; 1456 } 1457 case SVGTokenFontFamily: 1458 { 1459 SvgStringVector aSvgStringVector; 1460 1461 if(readSvgStringVector(aContent, aSvgStringVector)) 1462 { 1463 setFontFamily(aSvgStringVector); 1464 } 1465 break; 1466 } 1467 case SVGTokenFontSize: 1468 { 1469 SvgNumber aNum; 1470 1471 if(readSingleNumber(aContent, aNum)) 1472 { 1473 setFontSize(aNum); 1474 } 1475 break; 1476 } 1477 case SVGTokenFontSizeAdjust: 1478 { 1479 break; 1480 } 1481 case SVGTokenFontStretch: 1482 { 1483 if(aContent.getLength()) 1484 { 1485 static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal")); 1486 static rtl::OUString aStrWider(rtl::OUString::createFromAscii("wider")); 1487 static rtl::OUString aStrNarrower(rtl::OUString::createFromAscii("narrower")); 1488 static rtl::OUString aStrUltra_condensed(rtl::OUString::createFromAscii("ultra-condensed")); 1489 static rtl::OUString aStrExtra_condensed(rtl::OUString::createFromAscii("extra-condensed")); 1490 static rtl::OUString aStrCondensed(rtl::OUString::createFromAscii("condensed")); 1491 static rtl::OUString aStrSemi_condensed(rtl::OUString::createFromAscii("semi-condensed")); 1492 static rtl::OUString aStrSemi_expanded(rtl::OUString::createFromAscii("semi-expanded")); 1493 static rtl::OUString aStrExpanded(rtl::OUString::createFromAscii("expanded")); 1494 static rtl::OUString aStrExtra_expanded(rtl::OUString::createFromAscii("extra-expanded")); 1495 static rtl::OUString aStrUltra_expanded(rtl::OUString::createFromAscii("ultra-expanded")); 1496 1497 if(aContent.match(aStrNormal)) 1498 { 1499 setFontStretch(FontStretch_normal); 1500 } 1501 else if(aContent.match(aStrWider)) 1502 { 1503 setFontStretch(FontStretch_wider); 1504 } 1505 else if(aContent.match(aStrNarrower)) 1506 { 1507 setFontStretch(FontStretch_narrower); 1508 } 1509 else if(aContent.match(aStrUltra_condensed)) 1510 { 1511 setFontStretch(FontStretch_ultra_condensed); 1512 } 1513 else if(aContent.match(aStrExtra_condensed)) 1514 { 1515 setFontStretch(FontStretch_extra_condensed); 1516 } 1517 else if(aContent.match(aStrCondensed)) 1518 { 1519 setFontStretch(FontStretch_condensed); 1520 } 1521 else if(aContent.match(aStrSemi_condensed)) 1522 { 1523 setFontStretch(FontStretch_semi_condensed); 1524 } 1525 else if(aContent.match(aStrSemi_expanded)) 1526 { 1527 setFontStretch(FontStretch_semi_expanded); 1528 } 1529 else if(aContent.match(aStrExpanded)) 1530 { 1531 setFontStretch(FontStretch_expanded); 1532 } 1533 else if(aContent.match(aStrExtra_expanded)) 1534 { 1535 setFontStretch(FontStretch_extra_expanded); 1536 } 1537 else if(aContent.match(aStrUltra_expanded)) 1538 { 1539 setFontStretch(FontStretch_ultra_expanded); 1540 } 1541 } 1542 break; 1543 } 1544 case SVGTokenFontStyle: 1545 { 1546 if(aContent.getLength()) 1547 { 1548 static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal")); 1549 static rtl::OUString aStrItalic(rtl::OUString::createFromAscii("italic")); 1550 static rtl::OUString aStrOblique(rtl::OUString::createFromAscii("oblique")); 1551 1552 if(aContent.match(aStrNormal)) 1553 { 1554 setFontStyle(FontStyle_normal); 1555 } 1556 else if(aContent.match(aStrItalic)) 1557 { 1558 setFontStyle(FontStyle_italic); 1559 } 1560 else if(aContent.match(aStrOblique)) 1561 { 1562 setFontStyle(FontStyle_oblique); 1563 } 1564 } 1565 break; 1566 } 1567 case SVGTokenFontVariant: 1568 { 1569 if(aContent.getLength()) 1570 { 1571 static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal")); 1572 static rtl::OUString aStrSmallCaps(rtl::OUString::createFromAscii("small-caps")); 1573 1574 if(aContent.match(aStrNormal)) 1575 { 1576 setFontVariant(FontVariant_normal); 1577 } 1578 else if(aContent.match(aStrSmallCaps)) 1579 { 1580 setFontVariant(FontVariant_small_caps); 1581 } 1582 } 1583 break; 1584 } 1585 case SVGTokenFontWeight: 1586 { 1587 if(aContent.getLength()) 1588 { 1589 static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal")); 1590 static rtl::OUString aStrBold(rtl::OUString::createFromAscii("bold")); 1591 static rtl::OUString aStrBolder(rtl::OUString::createFromAscii("bolder")); 1592 static rtl::OUString aStrLighter(rtl::OUString::createFromAscii("lighter")); 1593 static rtl::OUString aStr100(rtl::OUString::createFromAscii("100")); 1594 static rtl::OUString aStr200(rtl::OUString::createFromAscii("200")); 1595 static rtl::OUString aStr300(rtl::OUString::createFromAscii("300")); 1596 static rtl::OUString aStr400(rtl::OUString::createFromAscii("400")); 1597 static rtl::OUString aStr500(rtl::OUString::createFromAscii("500")); 1598 static rtl::OUString aStr600(rtl::OUString::createFromAscii("600")); 1599 static rtl::OUString aStr700(rtl::OUString::createFromAscii("700")); 1600 static rtl::OUString aStr800(rtl::OUString::createFromAscii("800")); 1601 static rtl::OUString aStr900(rtl::OUString::createFromAscii("900")); 1602 1603 if(aContent.match(aStr100)) 1604 { 1605 setFontWeight(FontWeight_100); 1606 } 1607 else if(aContent.match(aStr200)) 1608 { 1609 setFontWeight(FontWeight_200); 1610 } 1611 else if(aContent.match(aStr300)) 1612 { 1613 setFontWeight(FontWeight_300); 1614 } 1615 else if(aContent.match(aStr400) || aContent.match(aStrNormal)) 1616 { 1617 setFontWeight(FontWeight_400); 1618 } 1619 else if(aContent.match(aStr500)) 1620 { 1621 setFontWeight(FontWeight_500); 1622 } 1623 else if(aContent.match(aStr600)) 1624 { 1625 setFontWeight(FontWeight_600); 1626 } 1627 else if(aContent.match(aStr700) || aContent.match(aStrBold)) 1628 { 1629 setFontWeight(FontWeight_700); 1630 } 1631 else if(aContent.match(aStr800)) 1632 { 1633 setFontWeight(FontWeight_800); 1634 } 1635 else if(aContent.match(aStr900)) 1636 { 1637 setFontWeight(FontWeight_900); 1638 } 1639 else if(aContent.match(aStrBolder)) 1640 { 1641 setFontWeight(FontWeight_bolder); 1642 } 1643 else if(aContent.match(aStrLighter)) 1644 { 1645 setFontWeight(FontWeight_lighter); 1646 } 1647 } 1648 break; 1649 } 1650 case SVGTokenDirection: 1651 { 1652 break; 1653 } 1654 case SVGTokenLetterSpacing: 1655 { 1656 break; 1657 } 1658 case SVGTokenTextDecoration: 1659 { 1660 if(aContent.getLength()) 1661 { 1662 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none")); 1663 static rtl::OUString aStrUnderline(rtl::OUString::createFromAscii("underline")); 1664 static rtl::OUString aStrOverline(rtl::OUString::createFromAscii("overline")); 1665 static rtl::OUString aStrLineThrough(rtl::OUString::createFromAscii("line-through")); 1666 static rtl::OUString aStrBlink(rtl::OUString::createFromAscii("blink")); 1667 1668 if(aContent.match(aStrNone)) 1669 { 1670 setTextDecoration(TextDecoration_none); 1671 } 1672 else if(aContent.match(aStrUnderline)) 1673 { 1674 setTextDecoration(TextDecoration_underline); 1675 } 1676 else if(aContent.match(aStrOverline)) 1677 { 1678 setTextDecoration(TextDecoration_overline); 1679 } 1680 else if(aContent.match(aStrLineThrough)) 1681 { 1682 setTextDecoration(TextDecoration_line_through); 1683 } 1684 else if(aContent.match(aStrBlink)) 1685 { 1686 setTextDecoration(TextDecoration_blink); 1687 } 1688 } 1689 break; 1690 } 1691 case SVGTokenUnicodeBidi: 1692 { 1693 break; 1694 } 1695 case SVGTokenWordSpacing: 1696 { 1697 break; 1698 } 1699 case SVGTokenTextAnchor: 1700 { 1701 if(aContent.getLength()) 1702 { 1703 static rtl::OUString aStrStart(rtl::OUString::createFromAscii("start")); 1704 static rtl::OUString aStrMiddle(rtl::OUString::createFromAscii("middle")); 1705 static rtl::OUString aStrEnd(rtl::OUString::createFromAscii("end")); 1706 1707 if(aContent.match(aStrStart)) 1708 { 1709 setTextAnchor(TextAnchor_start); 1710 } 1711 else if(aContent.match(aStrMiddle)) 1712 { 1713 setTextAnchor(TextAnchor_middle); 1714 } 1715 else if(aContent.match(aStrEnd)) 1716 { 1717 setTextAnchor(TextAnchor_end); 1718 } 1719 } 1720 break; 1721 } 1722 case SVGTokenTextAlign: 1723 { 1724 if(aContent.getLength()) 1725 { 1726 static rtl::OUString aStrLeft(rtl::OUString::createFromAscii("left")); 1727 static rtl::OUString aStrRight(rtl::OUString::createFromAscii("right")); 1728 static rtl::OUString aStrCenter(rtl::OUString::createFromAscii("center")); 1729 static rtl::OUString aStrJustify(rtl::OUString::createFromAscii("justify")); 1730 1731 if(aContent.match(aStrLeft)) 1732 { 1733 setTextAlign(TextAlign_left); 1734 } 1735 else if(aContent.match(aStrRight)) 1736 { 1737 setTextAlign(TextAlign_right); 1738 } 1739 else if(aContent.match(aStrCenter)) 1740 { 1741 setTextAlign(TextAlign_center); 1742 } 1743 else if(aContent.match(aStrJustify)) 1744 { 1745 setTextAlign(TextAlign_justify); 1746 } 1747 } 1748 break; 1749 } 1750 case SVGTokenColor: 1751 { 1752 SvgPaint aSvgPaint; 1753 rtl::OUString aURL; 1754 1755 if(readSvgPaint(aContent, aSvgPaint, aURL)) 1756 { 1757 setColor(aSvgPaint); 1758 } 1759 break; 1760 } 1761 case SVGTokenOpacity: 1762 { 1763 SvgNumber aNum; 1764 1765 if(readSingleNumber(aContent, aNum)) 1766 { 1767 setOpacity(SvgNumber(basegfx::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet())); 1768 } 1769 break; 1770 } 1771 case SVGTokenTitle: 1772 { 1773 setTitle(aContent); 1774 break; 1775 } 1776 case SVGTokenDesc: 1777 { 1778 setDesc(aContent); 1779 break; 1780 } 1781 case SVGTokenClipPathProperty: 1782 { 1783 readLocalUrl(aContent, maClipPathXLink); 1784 break; 1785 } 1786 case SVGTokenMask: 1787 { 1788 readLocalUrl(aContent, maMaskXLink); 1789 break; 1790 } 1791 case SVGTokenClipRule: 1792 { 1793 if(aContent.getLength()) 1794 { 1795 if(aContent.match(commonStrings::aStrNonzero)) 1796 { 1797 maClipRule = FillRule_nonzero; 1798 } 1799 else if(aContent.match(commonStrings::aStrEvenOdd)) 1800 { 1801 maClipRule = FillRule_evenodd; 1802 } 1803 } 1804 break; 1805 } 1806 case SVGTokenMarker: 1807 { 1808 readLocalUrl(aContent, maMarkerEndXLink); 1809 maMarkerStartXLink = maMarkerMidXLink = maMarkerEndXLink; 1810 break; 1811 } 1812 case SVGTokenMarkerStart: 1813 { 1814 readLocalUrl(aContent, maMarkerStartXLink); 1815 break; 1816 } 1817 case SVGTokenMarkerMid: 1818 { 1819 readLocalUrl(aContent, maMarkerMidXLink); 1820 break; 1821 } 1822 case SVGTokenMarkerEnd: 1823 { 1824 readLocalUrl(aContent, maMarkerEndXLink); 1825 break; 1826 } 1827 case SVGTokenDisplay: 1828 { 1829 // There may be display:none statements inside of style defines, e.g. the following line: 1830 // style="display:none" 1831 // taken from a svg example; this needs to be parsed and set at the owning node. Do not call 1832 // mrOwner.parseAttribute(...) here, this would lead to a recursion 1833 if(aContent.getLength()) 1834 { 1835 mrOwner.setDisplay(getDisplayFromContent(aContent)); 1836 } 1837 break; 1838 } 1839 case SVGTokenBaselineShift: 1840 { 1841 if(aContent.getLength()) 1842 { 1843 static rtl::OUString aStrSub(rtl::OUString::createFromAscii("sub")); 1844 static rtl::OUString aStrSuper(rtl::OUString::createFromAscii("super")); 1845 SvgNumber aNum; 1846 1847 if(aContent.match(aStrSub)) 1848 { 1849 setBaselineShift(BaselineShift_Sub); 1850 } 1851 else if(aContent.match(aStrSuper)) 1852 { 1853 setBaselineShift(BaselineShift_Super); 1854 } 1855 else if(readSingleNumber(aContent, aNum)) 1856 { 1857 setBaselineShiftNumber(aNum); 1858 1859 if(Unit_percent == aNum.getUnit()) 1860 { 1861 setBaselineShift(BaselineShift_Percentage); 1862 } 1863 else 1864 { 1865 setBaselineShift(BaselineShift_Length); 1866 } 1867 } 1868 else 1869 { 1870 // no BaselineShift or inherit (which is automatically) 1871 setBaselineShift(BaselineShift_Baseline); 1872 } 1873 } 1874 break; 1875 } 1876 default: 1877 { 1878 break; 1879 } 1880 } 1881 } 1882 1883 // #125258# ask if fill is a direct hard attribute (no hierarchy) 1884 bool SvgStyleAttributes::isFillSet() const 1885 { 1886 if(mbIsClipPathContent) 1887 { 1888 return false; 1889 } 1890 else if(maFill.isSet()) 1891 { 1892 return true; 1893 } 1894 1895 return false; 1896 } 1897 1898 const basegfx::BColor* SvgStyleAttributes::getFill() const 1899 { 1900 if(mbIsClipPathContent) 1901 { 1902 static basegfx::BColor aBlack(0.0, 0.0, 0.0); 1903 1904 return &aBlack; 1905 } 1906 else if(maFill.isSet()) 1907 { 1908 if(maFill.isCurrent()) 1909 { 1910 return getColor(); 1911 } 1912 else if(maFill.isOn()) 1913 { 1914 return &maFill.getBColor(); 1915 } 1916 } 1917 else 1918 { 1919 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1920 1921 if(pSvgStyleAttributes) 1922 { 1923 return pSvgStyleAttributes->getFill(); 1924 } 1925 } 1926 1927 return 0; 1928 } 1929 1930 const basegfx::BColor* SvgStyleAttributes::getStroke() const 1931 { 1932 if(mbIsClipPathContent) 1933 { 1934 return 0; 1935 } 1936 else if(maStroke.isSet()) 1937 { 1938 if(maStroke.isCurrent()) 1939 { 1940 return getColor(); 1941 } 1942 else if(maStroke.isOn()) 1943 { 1944 return &maStroke.getBColor(); 1945 } 1946 } 1947 else 1948 { 1949 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1950 1951 if(pSvgStyleAttributes) 1952 { 1953 return pSvgStyleAttributes->getStroke(); 1954 } 1955 } 1956 1957 return 0; 1958 } 1959 1960 const basegfx::BColor& SvgStyleAttributes::getStopColor() const 1961 { 1962 if(maStopColor.isCurrent()) 1963 { 1964 return *getColor(); 1965 } 1966 else 1967 { 1968 return maStopColor.getBColor(); 1969 } 1970 } 1971 1972 const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeFill() const 1973 { 1974 if(mbIsClipPathContent) 1975 { 1976 return 0; 1977 } 1978 else if(mpSvgGradientNodeFill) 1979 { 1980 return mpSvgGradientNodeFill; 1981 } 1982 else 1983 { 1984 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 1985 1986 if(pSvgStyleAttributes) 1987 { 1988 return pSvgStyleAttributes->getSvgGradientNodeFill(); 1989 } 1990 } 1991 1992 return 0; 1993 } 1994 1995 const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeStroke() const 1996 { 1997 if(mbIsClipPathContent) 1998 { 1999 return 0; 2000 } 2001 else if(mpSvgGradientNodeStroke) 2002 { 2003 return mpSvgGradientNodeStroke; 2004 } 2005 else 2006 { 2007 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2008 2009 if(pSvgStyleAttributes) 2010 { 2011 return pSvgStyleAttributes->getSvgGradientNodeStroke(); 2012 } 2013 } 2014 2015 return 0; 2016 } 2017 2018 const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeFill() const 2019 { 2020 if(mbIsClipPathContent) 2021 { 2022 return 0; 2023 } 2024 else if(mpSvgPatternNodeFill) 2025 { 2026 return mpSvgPatternNodeFill; 2027 } 2028 else 2029 { 2030 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2031 2032 if(pSvgStyleAttributes) 2033 { 2034 return pSvgStyleAttributes->getSvgPatternNodeFill(); 2035 } 2036 } 2037 2038 return 0; 2039 } 2040 2041 const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeStroke() const 2042 { 2043 if(mbIsClipPathContent) 2044 { 2045 return 0; 2046 } 2047 else if(mpSvgPatternNodeStroke) 2048 { 2049 return mpSvgPatternNodeStroke; 2050 } 2051 else 2052 { 2053 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2054 2055 if(pSvgStyleAttributes) 2056 { 2057 return pSvgStyleAttributes->getSvgPatternNodeStroke(); 2058 } 2059 } 2060 2061 return 0; 2062 } 2063 2064 SvgNumber SvgStyleAttributes::getStrokeWidth() const 2065 { 2066 if(mbIsClipPathContent) 2067 { 2068 return SvgNumber(0.0); 2069 } 2070 else if(maStrokeWidth.isSet()) 2071 { 2072 return maStrokeWidth; 2073 } 2074 2075 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2076 2077 if(pSvgStyleAttributes) 2078 { 2079 return pSvgStyleAttributes->getStrokeWidth(); 2080 } 2081 2082 // default is 1 2083 return SvgNumber(1.0); 2084 } 2085 2086 SvgNumber SvgStyleAttributes::getStopOpacity() const 2087 { 2088 if(maStopOpacity.isSet()) 2089 { 2090 return maStopOpacity; 2091 } 2092 2093 // default is 1 2094 return SvgNumber(1.0); 2095 } 2096 2097 SvgNumber SvgStyleAttributes::getFillOpacity() const 2098 { 2099 if(mbIsClipPathContent) 2100 { 2101 return SvgNumber(1.0); 2102 } 2103 else if(maFillOpacity.isSet()) 2104 { 2105 return maFillOpacity; 2106 } 2107 2108 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2109 2110 if(pSvgStyleAttributes) 2111 { 2112 return pSvgStyleAttributes->getFillOpacity(); 2113 } 2114 2115 // default is 1 2116 return SvgNumber(1.0); 2117 } 2118 2119 FillRule SvgStyleAttributes::getFillRule() const 2120 { 2121 if(FillRule_notset != maFillRule) 2122 { 2123 return maFillRule; 2124 } 2125 2126 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2127 2128 if(pSvgStyleAttributes) 2129 { 2130 return pSvgStyleAttributes->getFillRule(); 2131 } 2132 2133 // default is NonZero 2134 return FillRule_nonzero; 2135 } 2136 2137 const SvgNumberVector& SvgStyleAttributes::getStrokeDasharray() const 2138 { 2139 if(!maStrokeDasharray.empty()) 2140 { 2141 return maStrokeDasharray; 2142 } 2143 else if(getStrokeDasharraySet()) 2144 { 2145 // #121221# is set to empty *by purpose*, do not visit parent styles 2146 return maStrokeDasharray; 2147 } 2148 2149 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2150 2151 if(pSvgStyleAttributes) 2152 { 2153 return pSvgStyleAttributes->getStrokeDasharray(); 2154 } 2155 2156 // default empty 2157 return maStrokeDasharray; 2158 } 2159 2160 SvgNumber SvgStyleAttributes::getStrokeDashOffset() const 2161 { 2162 if(maStrokeDashOffset.isSet()) 2163 { 2164 return maStrokeDashOffset; 2165 } 2166 2167 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2168 2169 if(pSvgStyleAttributes) 2170 { 2171 return pSvgStyleAttributes->getStrokeDashOffset(); 2172 } 2173 2174 // default is 0 2175 return SvgNumber(0.0); 2176 } 2177 2178 StrokeLinecap SvgStyleAttributes::getStrokeLinecap() const 2179 { 2180 if(maStrokeLinecap != StrokeLinecap_notset) 2181 { 2182 return maStrokeLinecap; 2183 } 2184 2185 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2186 2187 if(pSvgStyleAttributes) 2188 { 2189 return pSvgStyleAttributes->getStrokeLinecap(); 2190 } 2191 2192 // default is StrokeLinecap_butt 2193 return StrokeLinecap_butt; 2194 } 2195 2196 StrokeLinejoin SvgStyleAttributes::getStrokeLinejoin() const 2197 { 2198 if(maStrokeLinejoin != StrokeLinejoin_notset) 2199 { 2200 return maStrokeLinejoin; 2201 } 2202 2203 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2204 2205 if(pSvgStyleAttributes) 2206 { 2207 return pSvgStyleAttributes->getStrokeLinejoin(); 2208 } 2209 2210 // default is StrokeLinejoin_butt 2211 return StrokeLinejoin_miter; 2212 } 2213 2214 SvgNumber SvgStyleAttributes::getStrokeMiterLimit() const 2215 { 2216 if(maStrokeMiterLimit.isSet()) 2217 { 2218 return maStrokeMiterLimit; 2219 } 2220 2221 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2222 2223 if(pSvgStyleAttributes) 2224 { 2225 return pSvgStyleAttributes->getStrokeMiterLimit(); 2226 } 2227 2228 // default is 4 2229 return SvgNumber(4.0); 2230 } 2231 2232 SvgNumber SvgStyleAttributes::getStrokeOpacity() const 2233 { 2234 if(maStrokeOpacity.isSet()) 2235 { 2236 return maStrokeOpacity; 2237 } 2238 2239 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2240 2241 if(pSvgStyleAttributes) 2242 { 2243 return pSvgStyleAttributes->getStrokeOpacity(); 2244 } 2245 2246 // default is 1 2247 return SvgNumber(1.0); 2248 } 2249 2250 const SvgStringVector& SvgStyleAttributes::getFontFamily() const 2251 { 2252 if(!maFontFamily.empty()) 2253 { 2254 return maFontFamily; 2255 } 2256 2257 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2258 2259 if(pSvgStyleAttributes) 2260 { 2261 return pSvgStyleAttributes->getFontFamily(); 2262 } 2263 2264 // default is empty 2265 return maFontFamily; 2266 } 2267 2268 SvgNumber SvgStyleAttributes::getFontSize() const 2269 { 2270 if(maFontSize.isSet()) 2271 { 2272 // #122524# Handle Unit_percent realtive to parent FontSize (see SVG1.1 2273 // spec 10.10 Font selection properties �font-size�, lastline (klick 'normative 2274 // definition of the property') 2275 if(Unit_percent == maFontSize.getUnit()) 2276 { 2277 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2278 2279 if(pSvgStyleAttributes) 2280 { 2281 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSize(); 2282 2283 return SvgNumber( 2284 aParentNumber.getNumber() * maFontSize.getNumber() * 0.01, 2285 aParentNumber.getUnit(), 2286 true); 2287 } 2288 } 2289 2290 return maFontSize; 2291 } 2292 2293 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2294 2295 if(pSvgStyleAttributes) 2296 { 2297 return pSvgStyleAttributes->getFontSize(); 2298 } 2299 2300 // default is 'medium' 2301 return SvgNumber(12.0); 2302 } 2303 2304 FontStretch SvgStyleAttributes::getFontStretch() const 2305 { 2306 if(maFontStretch != FontStretch_notset) 2307 { 2308 if(FontStretch_wider != maFontStretch && FontStretch_narrower != maFontStretch) 2309 { 2310 return maFontStretch; 2311 } 2312 } 2313 2314 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2315 2316 if(pSvgStyleAttributes) 2317 { 2318 FontStretch aInherited = pSvgStyleAttributes->getFontStretch(); 2319 2320 if(FontStretch_wider == maFontStretch) 2321 { 2322 aInherited = getWider(aInherited); 2323 } 2324 else if(FontStretch_narrower == maFontStretch) 2325 { 2326 aInherited = getNarrower(aInherited); 2327 } 2328 2329 return aInherited; 2330 } 2331 2332 // default is FontStretch_normal 2333 return FontStretch_normal; 2334 } 2335 2336 FontStyle SvgStyleAttributes::getFontStyle() const 2337 { 2338 if(maFontStyle != FontStyle_notset) 2339 { 2340 return maFontStyle; 2341 } 2342 2343 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2344 2345 if(pSvgStyleAttributes) 2346 { 2347 return pSvgStyleAttributes->getFontStyle(); 2348 } 2349 2350 // default is FontStyle_normal 2351 return FontStyle_normal; 2352 } 2353 2354 FontWeight SvgStyleAttributes::getFontWeight() const 2355 { 2356 if(maFontWeight != FontWeight_notset) 2357 { 2358 if(FontWeight_bolder != maFontWeight && FontWeight_lighter != maFontWeight) 2359 { 2360 return maFontWeight; 2361 } 2362 } 2363 2364 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2365 2366 if(pSvgStyleAttributes) 2367 { 2368 FontWeight aInherited = pSvgStyleAttributes->getFontWeight(); 2369 2370 if(FontWeight_bolder == maFontWeight) 2371 { 2372 aInherited = getBolder(aInherited); 2373 } 2374 else if(FontWeight_lighter == maFontWeight) 2375 { 2376 aInherited = getLighter(aInherited); 2377 } 2378 2379 return aInherited; 2380 } 2381 2382 // default is FontWeight_400 (FontWeight_normal) 2383 return FontWeight_400; 2384 } 2385 2386 TextAlign SvgStyleAttributes::getTextAlign() const 2387 { 2388 if(maTextAlign != TextAlign_notset) 2389 { 2390 return maTextAlign; 2391 } 2392 2393 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2394 2395 if(pSvgStyleAttributes) 2396 { 2397 return pSvgStyleAttributes->getTextAlign(); 2398 } 2399 2400 // default is TextAlign_left 2401 return TextAlign_left; 2402 } 2403 2404 const SvgStyleAttributes* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const 2405 { 2406 if(maTextDecoration != TextDecoration_notset) 2407 { 2408 return this; 2409 } 2410 2411 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2412 2413 if(pSvgStyleAttributes) 2414 { 2415 return pSvgStyleAttributes->getTextDecorationDefiningSvgStyleAttributes(); 2416 } 2417 2418 // default is 0 2419 return 0; 2420 } 2421 2422 TextDecoration SvgStyleAttributes::getTextDecoration() const 2423 { 2424 const SvgStyleAttributes* pDefining = getTextDecorationDefiningSvgStyleAttributes(); 2425 2426 if(pDefining) 2427 { 2428 return pDefining->maTextDecoration; 2429 } 2430 else 2431 { 2432 // default is TextDecoration_none 2433 return TextDecoration_none; 2434 } 2435 } 2436 2437 TextAnchor SvgStyleAttributes::getTextAnchor() const 2438 { 2439 if(maTextAnchor != TextAnchor_notset) 2440 { 2441 return maTextAnchor; 2442 } 2443 2444 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2445 2446 if(pSvgStyleAttributes) 2447 { 2448 return pSvgStyleAttributes->getTextAnchor(); 2449 } 2450 2451 // default is TextAnchor_start 2452 return TextAnchor_start; 2453 } 2454 2455 const basegfx::BColor* SvgStyleAttributes::getColor() const 2456 { 2457 if(maColor.isSet()) 2458 { 2459 if(maColor.isCurrent()) 2460 { 2461 OSL_ENSURE(false, "Svg error: current color uses current color (!)"); 2462 return 0; 2463 } 2464 else if(maColor.isOn()) 2465 { 2466 return &maColor.getBColor(); 2467 } 2468 } 2469 else 2470 { 2471 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2472 2473 if(pSvgStyleAttributes) 2474 { 2475 return pSvgStyleAttributes->getColor(); 2476 } 2477 } 2478 2479 return 0; 2480 } 2481 2482 rtl::OUString SvgStyleAttributes::getMarkerStartXLink() const 2483 { 2484 if(maMarkerStartXLink.getLength()) 2485 { 2486 return maMarkerStartXLink; 2487 } 2488 2489 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2490 2491 if(pSvgStyleAttributes) 2492 { 2493 return pSvgStyleAttributes->getMarkerStartXLink(); 2494 } 2495 2496 return rtl::OUString(); 2497 } 2498 2499 const SvgMarkerNode* SvgStyleAttributes::accessMarkerStartXLink() const 2500 { 2501 if(!mpMarkerStartXLink) 2502 { 2503 const rtl::OUString aMarker(getMarkerStartXLink()); 2504 2505 if(aMarker.getLength()) 2506 { 2507 const_cast< SvgStyleAttributes* >(this)->mpMarkerStartXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerStartXLink())); 2508 } 2509 } 2510 2511 return mpMarkerStartXLink; 2512 } 2513 2514 rtl::OUString SvgStyleAttributes::getMarkerMidXLink() const 2515 { 2516 if(maMarkerMidXLink.getLength()) 2517 { 2518 return maMarkerMidXLink; 2519 } 2520 2521 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2522 2523 if(pSvgStyleAttributes) 2524 { 2525 return pSvgStyleAttributes->getMarkerMidXLink(); 2526 } 2527 2528 return rtl::OUString(); 2529 } 2530 2531 const SvgMarkerNode* SvgStyleAttributes::accessMarkerMidXLink() const 2532 { 2533 if(!mpMarkerMidXLink) 2534 { 2535 const rtl::OUString aMarker(getMarkerMidXLink()); 2536 2537 if(aMarker.getLength()) 2538 { 2539 const_cast< SvgStyleAttributes* >(this)->mpMarkerMidXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerMidXLink())); 2540 } 2541 } 2542 2543 return mpMarkerMidXLink; 2544 } 2545 2546 rtl::OUString SvgStyleAttributes::getMarkerEndXLink() const 2547 { 2548 if(maMarkerEndXLink.getLength()) 2549 { 2550 return maMarkerEndXLink; 2551 } 2552 2553 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2554 2555 if(pSvgStyleAttributes) 2556 { 2557 return pSvgStyleAttributes->getMarkerEndXLink(); 2558 } 2559 2560 return rtl::OUString(); 2561 } 2562 2563 const SvgMarkerNode* SvgStyleAttributes::accessMarkerEndXLink() const 2564 { 2565 if(!mpMarkerEndXLink) 2566 { 2567 const rtl::OUString aMarker(getMarkerEndXLink()); 2568 2569 if(aMarker.getLength()) 2570 { 2571 const_cast< SvgStyleAttributes* >(this)->mpMarkerEndXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerEndXLink())); 2572 } 2573 } 2574 2575 return mpMarkerEndXLink; 2576 } 2577 2578 SvgNumber SvgStyleAttributes::getBaselineShiftNumber() const 2579 { 2580 // #122524# Handle Unit_percent realtive to parent BaselineShift 2581 if(Unit_percent == maBaselineShiftNumber.getUnit()) 2582 { 2583 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle(); 2584 2585 if(pSvgStyleAttributes) 2586 { 2587 const SvgNumber aParentNumber = pSvgStyleAttributes->getBaselineShiftNumber(); 2588 2589 return SvgNumber( 2590 aParentNumber.getNumber() * maBaselineShiftNumber.getNumber() * 0.01, 2591 aParentNumber.getUnit(), 2592 true); 2593 } 2594 } 2595 2596 return maBaselineShiftNumber; 2597 } 2598 } // end of namespace svgreader 2599 } // end of namespace svgio 2600 2601 ////////////////////////////////////////////////////////////////////////////// 2602 // eof 2603