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