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