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