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