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