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