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