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