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