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