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