bump product version to 4.1.6.2
[LibreOffice.git] / svgio / source / svgreader / svgstyleattributes.cxx
blob140107f5d604ea175aac1f179226a929183d2b22
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svgio/svgreader/svgstyleattributes.hxx>
21 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
22 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
23 #include <svgio/svgreader/svgnode.hxx>
24 #include <svgio/svgreader/svgdocument.hxx>
25 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
26 #include <svgio/svgreader/svggradientnode.hxx>
27 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
28 #include <basegfx/vector/b2enums.hxx>
29 #include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
30 #include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
31 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
32 #include <svgio/svgreader/svgclippathnode.hxx>
33 #include <svgio/svgreader/svgmasknode.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <svgio/svgreader/svgmarkernode.hxx>
36 #include <basegfx/curve/b2dcubicbezier.hxx>
37 #include <svgio/svgreader/svgpatternnode.hxx>
38 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
39 #include <basegfx/polygon/b2dpolygontools.hxx>
40 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
42 //////////////////////////////////////////////////////////////////////////////
44 namespace svgio
46 namespace svgreader
48 basegfx::B2DLineJoin StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin)
50 if(StrokeLinejoin_round == aStrokeLinejoin)
52 return basegfx::B2DLINEJOIN_ROUND;
54 else if(StrokeLinejoin_bevel == aStrokeLinejoin)
56 return basegfx::B2DLINEJOIN_BEVEL;
59 return basegfx::B2DLINEJOIN_MITER;
62 com::sun::star::drawing::LineCap StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap)
64 switch(aStrokeLinecap)
66 default: /* StrokeLinecap_notset, StrokeLinecap_butt */
68 return com::sun::star::drawing::LineCap_BUTT;
70 case StrokeLinecap_round:
72 return com::sun::star::drawing::LineCap_ROUND;
74 case StrokeLinecap_square:
76 return com::sun::star::drawing::LineCap_SQUARE;
81 FontStretch getWider(FontStretch aSource)
83 switch(aSource)
85 case FontStretch_ultra_condensed: aSource = FontStretch_extra_condensed; break;
86 case FontStretch_extra_condensed: aSource = FontStretch_condensed; break;
87 case FontStretch_condensed: aSource = FontStretch_semi_condensed; break;
88 case FontStretch_semi_condensed: aSource = FontStretch_normal; break;
89 case FontStretch_normal: aSource = FontStretch_semi_expanded; break;
90 case FontStretch_semi_expanded: aSource = FontStretch_expanded; break;
91 case FontStretch_expanded: aSource = FontStretch_extra_expanded; break;
92 case FontStretch_extra_expanded: aSource = FontStretch_ultra_expanded; break;
93 default: break;
96 return aSource;
99 FontStretch getNarrower(FontStretch aSource)
101 switch(aSource)
103 case FontStretch_extra_condensed: aSource = FontStretch_ultra_condensed; break;
104 case FontStretch_condensed: aSource = FontStretch_extra_condensed; break;
105 case FontStretch_semi_condensed: aSource = FontStretch_condensed; break;
106 case FontStretch_normal: aSource = FontStretch_semi_condensed; break;
107 case FontStretch_semi_expanded: aSource = FontStretch_normal; break;
108 case FontStretch_expanded: aSource = FontStretch_semi_expanded; break;
109 case FontStretch_extra_expanded: aSource = FontStretch_expanded; break;
110 case FontStretch_ultra_expanded: aSource = FontStretch_extra_expanded; break;
111 default: break;
114 return aSource;
117 FontWeight getBolder(FontWeight aSource)
119 switch(aSource)
121 case FontWeight_100: aSource = FontWeight_200; break;
122 case FontWeight_200: aSource = FontWeight_300; break;
123 case FontWeight_300: aSource = FontWeight_400; break;
124 case FontWeight_400: aSource = FontWeight_500; break;
125 case FontWeight_500: aSource = FontWeight_600; break;
126 case FontWeight_600: aSource = FontWeight_700; break;
127 case FontWeight_700: aSource = FontWeight_800; break;
128 case FontWeight_800: aSource = FontWeight_900; break;
129 default: break;
132 return aSource;
135 FontWeight getLighter(FontWeight aSource)
137 switch(aSource)
139 case FontWeight_200: aSource = FontWeight_100; break;
140 case FontWeight_300: aSource = FontWeight_200; break;
141 case FontWeight_400: aSource = FontWeight_300; break;
142 case FontWeight_500: aSource = FontWeight_400; break;
143 case FontWeight_600: aSource = FontWeight_500; break;
144 case FontWeight_700: aSource = FontWeight_600; break;
145 case FontWeight_800: aSource = FontWeight_700; break;
146 case FontWeight_900: aSource = FontWeight_800; break;
147 default: break;
150 return aSource;
153 ::FontWeight getVclFontWeight(FontWeight aSource)
155 ::FontWeight nRetval(WEIGHT_NORMAL);
157 switch(aSource)
159 case FontWeight_100: nRetval = WEIGHT_ULTRALIGHT; break;
160 case FontWeight_200: nRetval = WEIGHT_LIGHT; break;
161 case FontWeight_300: nRetval = WEIGHT_SEMILIGHT; break;
162 case FontWeight_400: nRetval = WEIGHT_NORMAL; break;
163 case FontWeight_500: nRetval = WEIGHT_MEDIUM; break;
164 case FontWeight_600: nRetval = WEIGHT_SEMIBOLD; break;
165 case FontWeight_700: nRetval = WEIGHT_BOLD; break;
166 case FontWeight_800: nRetval = WEIGHT_ULTRABOLD; break;
167 case FontWeight_900: nRetval = WEIGHT_BLACK; break;
168 default: break;
171 return nRetval;
174 void SvgStyleAttributes::readStyle(const OUString& rCandidate)
176 const sal_Int32 nLen(rCandidate.getLength());
177 sal_Int32 nPos(0);
179 while(nPos < nLen)
181 const sal_Int32 nInitPos(nPos);
182 skip_char(rCandidate, sal_Unicode(' '), nPos, nLen);
183 OUStringBuffer aTokenName;
184 copyString(rCandidate, nPos, aTokenName, nLen);
186 if(aTokenName.getLength())
188 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(':'), nPos, nLen);
189 OUStringBuffer aTokenValue;
190 copyToLimiter(rCandidate, sal_Unicode(';'), nPos, aTokenValue, nLen);
191 skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(';'), nPos, nLen);
192 const OUString aOUTokenName(aTokenName.makeStringAndClear());
193 const OUString aOUTokenValue(aTokenValue.makeStringAndClear());
195 parseStyleAttribute(aOUTokenName, StrToSVGToken(aOUTokenName), aOUTokenValue);
198 if(nInitPos == nPos)
200 OSL_ENSURE(false, "Could not interpret on current position (!)");
201 nPos++;
206 const SvgStyleAttributes* SvgStyleAttributes::getParentStyle() const
208 if(getCssStyleParent())
210 return getCssStyleParent();
213 if(mrOwner.getParent())
215 return mrOwner.getParent()->getSvgStyleAttributes();
218 return 0;
221 void SvgStyleAttributes::add_text(
222 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
223 drawinglayer::primitive2d::Primitive2DSequence& rSource) const
225 if(rSource.hasElements())
227 // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D
228 // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill())
229 // set. When another fill is used and also evtl. stroke is set it gets necessary to
230 // dismantle to geometry and add needed primitives
231 const basegfx::BColor* pFill = getFill();
232 const SvgGradientNode* pFillGradient = getSvgGradientNodeFill();
233 const SvgPatternNode* pFillPattern = getSvgPatternNodeFill();
234 const basegfx::BColor* pStroke = getStroke();
235 const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke();
236 const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke();
237 basegfx::B2DPolyPolygon aMergedArea;
239 if(pFillGradient || pFillPattern || pStroke || pStrokeGradient || pStrokePattern)
241 // text geometry is needed, create
242 // use neutral ViewInformation and create LineGeometryExtractor2D
243 const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
244 drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D);
246 // proccess
247 aExtractor.process(rSource);
249 // get results
250 const drawinglayer::processor2d::TextAsPolygonDataNodeVector& rResult = aExtractor.getTarget();
251 const sal_uInt32 nResultCount(rResult.size());
252 basegfx::B2DPolyPolygonVector aTextFillVector;
253 aTextFillVector.reserve(nResultCount);
255 for(sal_uInt32 a(0); a < nResultCount; a++)
257 const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a];
259 if(rCandidate.getIsFilled())
261 aTextFillVector.push_back(rCandidate.getB2DPolyPolygon());
265 if(!aTextFillVector.empty())
267 aMergedArea = basegfx::tools::mergeToSinglePolyPolygon(aTextFillVector);
271 const bool bStrokeUsed(pStroke || pStrokeGradient || pStrokePattern);
273 // add fill. Use geometry even for simple color fill when stroke
274 // is used, else text rendering and the geometry-based stroke will
275 // normally not really match optically due to divrese system text
276 // renderers
277 if(aMergedArea.count() && (pFillGradient || pFillPattern || bStrokeUsed))
279 // create text fill content based on geometry
280 add_fill(aMergedArea, rTarget, aMergedArea.getB2DRange());
282 else if(pFill)
284 // add the already prepared primitives for single color fill
285 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, rSource);
288 // add stroke
289 if(aMergedArea.count() && bStrokeUsed)
291 // create text stroke content
292 add_stroke(aMergedArea, rTarget, aMergedArea.getB2DRange());
297 void SvgStyleAttributes::add_fillGradient(
298 const basegfx::B2DPolyPolygon& rPath,
299 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
300 const SvgGradientNode& rFillGradient,
301 const basegfx::B2DRange& rGeoRange) const
303 // create fill content
304 drawinglayer::primitive2d::SvgGradientEntryVector aSvgGradientEntryVector;
306 // get the color stops
307 rFillGradient.collectGradientEntries(aSvgGradientEntryVector);
309 if(!aSvgGradientEntryVector.empty())
311 basegfx::B2DHomMatrix aGeoToUnit;
313 if(rFillGradient.getGradientTransform())
315 aGeoToUnit = *rFillGradient.getGradientTransform();
318 if(userSpaceOnUse == rFillGradient.getGradientUnits())
320 aGeoToUnit.translate(-rGeoRange.getMinX(), -rGeoRange.getMinY());
321 aGeoToUnit.scale(1.0 / rGeoRange.getWidth(), 1.0 / rGeoRange.getHeight());
324 if(SVGTokenLinearGradient == rFillGradient.getType())
326 basegfx::B2DPoint aStart(0.0, 0.0);
327 basegfx::B2DPoint aEnd(1.0, 0.0);
329 if(userSpaceOnUse == rFillGradient.getGradientUnits())
331 // all possible units
332 aStart.setX(rFillGradient.getX1().solve(mrOwner, xcoordinate));
333 aStart.setY(rFillGradient.getY1().solve(mrOwner, ycoordinate));
334 aEnd.setX(rFillGradient.getX2().solve(mrOwner, xcoordinate));
335 aEnd.setY(rFillGradient.getY2().solve(mrOwner, ycoordinate));
337 else
339 // fractions or percent relative to object bounds
340 const SvgNumber X1(rFillGradient.getX1());
341 const SvgNumber Y1(rFillGradient.getY1());
342 const SvgNumber X2(rFillGradient.getX2());
343 const SvgNumber Y2(rFillGradient.getY2());
345 aStart.setX(Unit_percent == X1.getUnit() ? X1.getNumber() * 0.01 : X1.getNumber());
346 aStart.setY(Unit_percent == Y1.getUnit() ? Y1.getNumber() * 0.01 : Y1.getNumber());
347 aEnd.setX(Unit_percent == X2.getUnit() ? X2.getNumber() * 0.01 : X2.getNumber());
348 aEnd.setY(Unit_percent == Y2.getUnit() ? Y2.getNumber() * 0.01 : Y2.getNumber());
351 if(!aGeoToUnit.isIdentity())
353 aStart *= aGeoToUnit;
354 aEnd *= aGeoToUnit;
357 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
358 rTarget,
359 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
360 rPath,
361 aSvgGradientEntryVector,
362 aStart,
363 aEnd,
364 userSpaceOnUse != rFillGradient.getGradientUnits(),
365 rFillGradient.getSpreadMethod()));
367 else
369 basegfx::B2DPoint aStart(0.5, 0.5);
370 basegfx::B2DPoint aFocal;
371 double fRadius(0.5);
372 const SvgNumber* pFx = rFillGradient.getFx();
373 const SvgNumber* pFy = rFillGradient.getFy();
374 const bool bFocal(pFx || pFy);
376 if(userSpaceOnUse == rFillGradient.getGradientUnits())
378 // all possible units
379 aStart.setX(rFillGradient.getCx().solve(mrOwner, xcoordinate));
380 aStart.setY(rFillGradient.getCy().solve(mrOwner, ycoordinate));
381 fRadius = rFillGradient.getR().solve(mrOwner, length);
383 if(bFocal)
385 aFocal.setX(pFx ? pFx->solve(mrOwner, xcoordinate) : aStart.getX());
386 aFocal.setY(pFy ? pFy->solve(mrOwner, ycoordinate) : aStart.getY());
389 else
391 // fractions or percent relative to object bounds
392 const SvgNumber Cx(rFillGradient.getCx());
393 const SvgNumber Cy(rFillGradient.getCy());
394 const SvgNumber R(rFillGradient.getR());
396 aStart.setX(Unit_percent == Cx.getUnit() ? Cx.getNumber() * 0.01 : Cx.getNumber());
397 aStart.setY(Unit_percent == Cy.getUnit() ? Cy.getNumber() * 0.01 : Cy.getNumber());
398 fRadius = (Unit_percent == R.getUnit()) ? R.getNumber() * 0.01 : R.getNumber();
400 if(bFocal)
402 aFocal.setX(pFx ? (Unit_percent == pFx->getUnit() ? pFx->getNumber() * 0.01 : pFx->getNumber()) : aStart.getX());
403 aFocal.setY(pFy ? (Unit_percent == pFy->getUnit() ? pFy->getNumber() * 0.01 : pFy->getNumber()) : aStart.getY());
407 if(!aGeoToUnit.isIdentity())
409 aStart *= aGeoToUnit;
410 fRadius = (aGeoToUnit * basegfx::B2DVector(fRadius, 0.0)).getLength();
412 if(bFocal)
414 aFocal *= aGeoToUnit;
418 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
419 rTarget,
420 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
421 rPath,
422 aSvgGradientEntryVector,
423 aStart,
424 fRadius,
425 userSpaceOnUse != rFillGradient.getGradientUnits(),
426 rFillGradient.getSpreadMethod(),
427 bFocal ? &aFocal : 0));
432 void SvgStyleAttributes::add_fillPatternTransform(
433 const basegfx::B2DPolyPolygon& rPath,
434 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
435 const SvgPatternNode& rFillPattern,
436 const basegfx::B2DRange& rGeoRange) const
438 // prepare fill polyPolygon with given pattern, check for patternTransform
439 if(rFillPattern.getPatternTransform() && !rFillPattern.getPatternTransform()->isIdentity())
441 // PatternTransform is active; Handle by filling the inverse transformed
442 // path and back-transforming the result
443 basegfx::B2DPolyPolygon aPath(rPath);
444 basegfx::B2DHomMatrix aInv(*rFillPattern.getPatternTransform());
445 drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
447 aInv.invert();
448 aPath.transform(aInv);
449 add_fillPattern(aPath, aNewTarget, rFillPattern, aPath.getB2DRange());
451 if(aNewTarget.hasElements())
453 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
454 rTarget,
455 new drawinglayer::primitive2d::TransformPrimitive2D(
456 *rFillPattern.getPatternTransform(),
457 aNewTarget));
460 else
462 // no patternTransform, create fillPattern directly
463 add_fillPattern(rPath, rTarget, rFillPattern, rGeoRange);
467 void SvgStyleAttributes::add_fillPattern(
468 const basegfx::B2DPolyPolygon& rPath,
469 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
470 const SvgPatternNode& rFillPattern,
471 const basegfx::B2DRange& rGeoRange) const
473 // fill polyPolygon with given pattern
474 const drawinglayer::primitive2d::Primitive2DSequence& rPrimitives = rFillPattern.getPatternPrimitives();
476 if(rPrimitives.hasElements())
478 double fTargetWidth(rGeoRange.getWidth());
479 double fTargetHeight(rGeoRange.getHeight());
481 if(fTargetWidth > 0.0 && fTargetHeight > 0.0)
483 // get relative values from pattern
484 double fX(0.0);
485 double fY(0.0);
486 double fW(0.0);
487 double fH(0.0);
489 rFillPattern.getValuesRelative(fX, fY, fW, fH, rGeoRange, mrOwner);
491 if(fW > 0.0 && fH > 0.0)
493 // build the reference range relative to the rGeoRange
494 const basegfx::B2DRange aReferenceRange(fX, fY, fX + fW, fY + fH);
496 // find out how the content is mapped to the reference range
497 basegfx::B2DHomMatrix aMapPrimitivesToUnitRange;
498 const basegfx::B2DRange* pViewBox = rFillPattern.getViewBox();
500 if(pViewBox)
502 // use viewBox/preserveAspectRatio
503 const SvgAspectRatio& rRatio = rFillPattern.getSvgAspectRatio();
504 const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
506 if(rRatio.isSet())
508 // let mapping be created from SvgAspectRatio
509 aMapPrimitivesToUnitRange = rRatio.createMapping(aUnitRange, *pViewBox);
511 else
513 // choose default mapping
514 aMapPrimitivesToUnitRange = rRatio.createLinearMapping(aUnitRange, *pViewBox);
517 else
519 // use patternContentUnits
520 const SvgUnits aPatternContentUnits(rFillPattern.getPatternContentUnits() ? *rFillPattern.getPatternContentUnits() : userSpaceOnUse);
522 if(userSpaceOnUse == aPatternContentUnits)
524 // create relative mapping to unit coordinates
525 aMapPrimitivesToUnitRange.scale(1.0 / (fW * fTargetWidth), 1.0 / (fH * fTargetHeight));
527 else
529 aMapPrimitivesToUnitRange.scale(1.0 / fW, 1.0 / fH);
533 // apply aMapPrimitivesToUnitRange to content when used
534 drawinglayer::primitive2d::Primitive2DSequence aPrimitives(rPrimitives);
536 if(!aMapPrimitivesToUnitRange.isIdentity())
538 const drawinglayer::primitive2d::Primitive2DReference xRef(
539 new drawinglayer::primitive2d::TransformPrimitive2D(
540 aMapPrimitivesToUnitRange,
541 aPrimitives));
543 aPrimitives = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
546 // embed in PatternFillPrimitive2D
547 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
548 rTarget,
549 new drawinglayer::primitive2d::PatternFillPrimitive2D(
550 rPath,
551 aPrimitives,
552 aReferenceRange));
558 void SvgStyleAttributes::add_fill(
559 const basegfx::B2DPolyPolygon& rPath,
560 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
561 const basegfx::B2DRange& rGeoRange) const
563 const basegfx::BColor* pFill = getFill();
564 const SvgGradientNode* pFillGradient = getSvgGradientNodeFill();
565 const SvgPatternNode* pFillPattern = getSvgPatternNodeFill();
567 if(pFill || pFillGradient || pFillPattern)
569 const double fFillOpacity(getFillOpacity().solve(mrOwner, length));
571 if(basegfx::fTools::more(fFillOpacity, 0.0))
573 drawinglayer::primitive2d::Primitive2DSequence aNewFill;
575 if(pFillGradient)
577 // create fill content with SVG gradient primitive
578 add_fillGradient(rPath, aNewFill, *pFillGradient, rGeoRange);
580 else if(pFillPattern)
582 // create fill content with SVG pattern primitive
583 add_fillPatternTransform(rPath, aNewFill, *pFillPattern, rGeoRange);
585 else // if(pFill)
587 // create fill content
588 aNewFill.realloc(1);
589 aNewFill[0] = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
590 rPath,
591 *pFill);
594 if(aNewFill.hasElements())
596 if(basegfx::fTools::less(fFillOpacity, 1.0))
598 // embed in UnifiedTransparencePrimitive2D
599 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
600 rTarget,
601 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
602 aNewFill,
603 1.0 - fFillOpacity));
605 else
607 // append
608 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewFill);
615 void SvgStyleAttributes::add_stroke(
616 const basegfx::B2DPolyPolygon& rPath,
617 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
618 const basegfx::B2DRange& rGeoRange) const
620 const basegfx::BColor* pStroke = getStroke();
621 const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke();
622 const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke();
624 if(pStroke || pStrokeGradient || pStrokePattern)
626 drawinglayer::primitive2d::Primitive2DSequence aNewStroke;
627 const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner, length));
629 if(basegfx::fTools::more(fStrokeOpacity, 0.0))
631 // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all
632 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner, length) : 1.0);
634 if(basegfx::fTools::more(fStrokeWidth, 0.0))
636 // get LineJoin, LineCap and stroke array
637 const basegfx::B2DLineJoin aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin()));
638 const com::sun::star::drawing::LineCap aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap()));
639 ::std::vector< double > aDashArray;
641 if(!getStrokeDasharray().empty())
643 aDashArray = solveSvgNumberVector(getStrokeDasharray(), mrOwner, length);
646 // todo: Handle getStrokeDashOffset()
648 // prepare line attribute
649 drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive;
650 const drawinglayer::attribute::LineAttribute aLineAttribute(
651 pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0),
652 fStrokeWidth,
653 aB2DLineJoin,
654 aLineCap);
656 if(aDashArray.empty())
658 aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
659 rPath,
660 aLineAttribute);
662 else
664 const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(aDashArray);
666 aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
667 rPath,
668 aLineAttribute,
669 aStrokeAttribute);
672 if(pStrokeGradient || pStrokePattern)
674 // put primitive into Primitive2DReference and Primitive2DSequence
675 const drawinglayer::primitive2d::Primitive2DSequence aSeq(&aNewLinePrimitive, 1);
677 // use neutral ViewInformation and create LineGeometryExtractor2D
678 const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
679 drawinglayer::processor2d::LineGeometryExtractor2D aExtractor(aViewInformation2D);
681 // proccess
682 aExtractor.process(aSeq);
684 // check for fill rsults
685 const basegfx::B2DPolyPolygonVector& rLineFillVector(aExtractor.getExtractedLineFills());
687 if(!rLineFillVector.empty())
689 const basegfx::B2DPolyPolygon aMergedArea(
690 basegfx::tools::mergeToSinglePolyPolygon(
691 rLineFillVector));
693 if(aMergedArea.count())
695 if(pStrokeGradient)
697 // create fill content with SVG gradient primitive. Use original GeoRange,
698 // e.g. from circle without LineWidth
699 add_fillGradient(aMergedArea, aNewStroke, *pStrokeGradient, rGeoRange);
701 else // if(pStrokePattern)
703 // create fill content with SVG pattern primitive. Use GeoRange
704 // from the expanded data, e.g. circle with extended geo by half linewidth
705 add_fillPatternTransform(aMergedArea, aNewStroke, *pStrokePattern, aMergedArea.getB2DRange());
710 else // if(pStroke)
712 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aNewStroke, aNewLinePrimitive);
715 if(aNewStroke.hasElements())
717 if(basegfx::fTools::less(fStrokeOpacity, 1.0))
719 // embed in UnifiedTransparencePrimitive2D
720 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
721 rTarget,
722 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
723 aNewStroke,
724 1.0 - fStrokeOpacity));
726 else
728 // append
729 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewStroke);
737 double get_markerRotation(
738 const SvgMarkerNode& rMarker,
739 const basegfx::B2DPolygon& rPolygon,
740 const sal_uInt32 nIndex)
742 double fAngle(0.0);
743 const sal_uInt32 nPointCount(rPolygon.count());
745 if(nPointCount)
747 if(rMarker.getOrientAuto())
749 const bool bPrev(rPolygon.isClosed() || nIndex > 0);
750 basegfx::B2DCubicBezier aSegment;
751 basegfx::B2DVector aPrev;
752 basegfx::B2DVector aNext;
754 if(bPrev)
756 rPolygon.getBezierSegment((nIndex - 1) % nPointCount, aSegment);
757 aPrev = aSegment.getTangent(1.0);
760 const bool bNext(rPolygon.isClosed() || nIndex + 1 < nPointCount);
762 if(bNext)
764 rPolygon.getBezierSegment(nIndex % nPointCount, aSegment);
765 aNext = aSegment.getTangent(0.0);
768 if(bPrev && bNext)
770 fAngle = atan2(aPrev.getY() + aNext.getY(), aPrev.getX() + aNext.getX());
772 else if(bPrev)
774 fAngle = atan2(aPrev.getY(), aPrev.getX());
776 else if(bNext)
778 fAngle = atan2(aNext.getY(), aNext.getX());
781 else
783 fAngle = rMarker.getAngle();
787 return fAngle;
790 bool SvgStyleAttributes::prepare_singleMarker(
791 drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives,
792 basegfx::B2DHomMatrix& rMarkerTransform,
793 basegfx::B2DRange& rClipRange,
794 const SvgMarkerNode& rMarker) const
796 // reset return values
797 rMarkerTransform.identity();
798 rClipRange.reset();
800 // get marker primitive representation
801 rMarkerPrimitives = rMarker.getMarkerPrimitives();
803 if(rMarkerPrimitives.hasElements())
805 basegfx::B2DRange aPrimitiveRange(0.0, 0.0, 1.0, 1.0);
806 const basegfx::B2DRange* pViewBox = rMarker.getViewBox();
808 if(pViewBox)
810 aPrimitiveRange = *pViewBox;
813 if(aPrimitiveRange.getWidth() > 0.0 && aPrimitiveRange.getHeight() > 0.0)
815 double fTargetWidth(rMarker.getMarkerWidth().isSet() ? rMarker.getMarkerWidth().solve(mrOwner, xcoordinate) : 3.0);
816 double fTargetHeight(rMarker.getMarkerHeight().isSet() ? rMarker.getMarkerHeight().solve(mrOwner, xcoordinate) : 3.0);
817 const bool bStrokeWidth(SvgMarkerNode::strokeWidth == rMarker.getMarkerUnits());
818 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner, length) : 1.0);
820 if(bStrokeWidth)
822 // relative to strokeWidth
823 fTargetWidth *= fStrokeWidth;
824 fTargetHeight *= fStrokeWidth;
827 if(fTargetWidth > 0.0 && fTargetHeight > 0.0)
829 // create mapping
830 const basegfx::B2DRange aTargetRange(0.0, 0.0, fTargetWidth, fTargetHeight);
831 const SvgAspectRatio& rRatio = rMarker.getSvgAspectRatio();
833 if(rRatio.isSet())
835 // let mapping be created from SvgAspectRatio
836 rMarkerTransform = rRatio.createMapping(aTargetRange, aPrimitiveRange);
838 if(rRatio.isMeetOrSlice())
840 // need to clip
841 rClipRange = aPrimitiveRange;
844 else
846 if(!pViewBox)
848 if(bStrokeWidth)
850 // adapt to strokewidth if needed
851 rMarkerTransform.scale(fStrokeWidth, fStrokeWidth);
854 else
856 // choose default mapping
857 rMarkerTransform = rRatio.createLinearMapping(aTargetRange, aPrimitiveRange);
861 // get and apply reference point. Initially it's in marker local coordinate system
862 basegfx::B2DPoint aRefPoint(
863 rMarker.getRefX().isSet() ? rMarker.getRefX().solve(mrOwner, xcoordinate) : 0.0,
864 rMarker.getRefY().isSet() ? rMarker.getRefY().solve(mrOwner, ycoordinate) : 0.0);
866 // apply MarkerTransform to have it in mapped coordinates
867 aRefPoint *= rMarkerTransform;
869 // apply by moving RepPoint to (0.0)
870 rMarkerTransform.translate(-aRefPoint.getX(), -aRefPoint.getY());
872 return true;
877 return false;
880 void SvgStyleAttributes::add_singleMarker(
881 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
882 const drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives,
883 const basegfx::B2DHomMatrix& rMarkerTransform,
884 const basegfx::B2DRange& rClipRange,
885 const SvgMarkerNode& rMarker,
886 const basegfx::B2DPolygon& rCandidate,
887 const sal_uInt32 nIndex) const
889 const sal_uInt32 nPointCount(rCandidate.count());
891 if(nPointCount)
893 // get and apply rotation
894 basegfx::B2DHomMatrix aCombinedTransform(rMarkerTransform);
895 aCombinedTransform.rotate(get_markerRotation(rMarker, rCandidate, nIndex));
897 // get and apply target position
898 const basegfx::B2DPoint aPoint(rCandidate.getB2DPoint(nIndex % nPointCount));
899 aCombinedTransform.translate(aPoint.getX(), aPoint.getY());
901 // prepare marker
902 drawinglayer::primitive2d::Primitive2DReference xMarker(
903 new drawinglayer::primitive2d::TransformPrimitive2D(
904 aCombinedTransform,
905 rMarkerPrimitives));
907 if(!rClipRange.isEmpty())
909 // marker needs to be clipped, it's bigger as the mapping
910 basegfx::B2DPolyPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(rClipRange));
912 aClipPolygon.transform(aCombinedTransform);
913 xMarker = new drawinglayer::primitive2d::MaskPrimitive2D(
914 aClipPolygon,
915 drawinglayer::primitive2d::Primitive2DSequence(&xMarker, 1));
918 // add marker
919 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMarker);
923 void SvgStyleAttributes::add_markers(
924 const basegfx::B2DPolyPolygon& rPath,
925 drawinglayer::primitive2d::Primitive2DSequence& rTarget) const
927 // try to access linked markers
928 const SvgMarkerNode* pStart = accessMarkerStartXLink();
929 const SvgMarkerNode* pMid = accessMarkerMidXLink();
930 const SvgMarkerNode* pEnd = accessMarkerEndXLink();
932 if(pStart || pMid || pEnd)
934 const sal_uInt32 nCount(rPath.count());
936 for (sal_uInt32 a(0); a < nCount; a++)
938 const basegfx::B2DPolygon aCandidate(rPath.getB2DPolygon(a));
939 const sal_uInt32 nPointCount(aCandidate.count());
941 if(nPointCount)
943 const sal_uInt32 nMarkerCount(aCandidate.isClosed() ? nPointCount + 1 : nPointCount);
944 drawinglayer::primitive2d::Primitive2DSequence aMarkerPrimitives;
945 basegfx::B2DHomMatrix aMarkerTransform;
946 basegfx::B2DRange aClipRange;
947 const SvgMarkerNode* pPrepared = 0;
949 if(pStart)
951 if(prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pStart))
953 pPrepared = pStart;
954 add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, 0);
958 if(pMid && nMarkerCount > 2)
960 if(pMid == pPrepared || prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pMid))
962 pPrepared = pMid;
964 for(sal_uInt32 b(1); b < nMarkerCount - 1; b++)
966 add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, b);
971 if(pEnd)
973 if(pEnd == pPrepared || prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pEnd))
975 pPrepared = pEnd;
976 add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, nMarkerCount - 1);
984 void SvgStyleAttributes::add_path(
985 const basegfx::B2DPolyPolygon& rPath,
986 drawinglayer::primitive2d::Primitive2DSequence& rTarget) const
988 const bool bIsLine(1 == rPath.count()
989 && !rPath.areControlPointsUsed()
990 && 2 == rPath.getB2DPolygon(0).count());
992 if(!rPath.count())
994 return;
997 const basegfx::B2DRange aGeoRange(rPath.getB2DRange());
999 if(aGeoRange.isEmpty())
1001 return;
1004 if(!bIsLine && // not for lines
1005 (basegfx::fTools::equalZero(aGeoRange.getWidth())
1006 || basegfx::fTools::equalZero(aGeoRange.getHeight())))
1008 return;
1011 const double fOpacity(getOpacity().getNumber());
1013 if(basegfx::fTools::equalZero(fOpacity))
1015 return;
1018 if(!bIsLine)
1020 basegfx::B2DPolyPolygon aPath(rPath);
1021 const bool bNeedToCheckClipRule(SVGTokenPath == mrOwner.getType() || SVGTokenPolygon == mrOwner.getType());
1022 const bool bClipPathIsNonzero(!bIsLine && bNeedToCheckClipRule && mbIsClipPathContent && FillRule_nonzero == maClipRule);
1023 const bool bFillRuleIsNonzero(!bIsLine && bNeedToCheckClipRule && !mbIsClipPathContent && FillRule_nonzero == getFillRule());
1025 if(bClipPathIsNonzero || bFillRuleIsNonzero)
1027 // nonzero is wanted, solve geometrically (see description on basegfx)
1028 aPath = basegfx::tools::createNonzeroConform(aPath);
1031 add_fill(aPath, rTarget, aGeoRange);
1034 add_stroke(rPath, rTarget, aGeoRange);
1036 // Svg supports markers for path, polygon, polyline and line
1037 if(SVGTokenPath == mrOwner.getType() || // path
1038 SVGTokenPolygon == mrOwner.getType() || // polygon, polyline
1039 SVGTokenLine == mrOwner.getType()) // line
1041 // try to add markers
1042 add_markers(rPath, rTarget);
1046 void SvgStyleAttributes::add_postProcess(
1047 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
1048 const drawinglayer::primitive2d::Primitive2DSequence& rSource,
1049 const basegfx::B2DHomMatrix* pTransform) const
1051 if(rSource.hasElements())
1053 const double fOpacity(getOpacity().getNumber());
1055 if(basegfx::fTools::equalZero(fOpacity))
1057 return;
1060 drawinglayer::primitive2d::Primitive2DSequence aSource(rSource);
1062 if(basegfx::fTools::less(fOpacity, 1.0))
1064 // embed in UnifiedTransparencePrimitive2D
1065 const drawinglayer::primitive2d::Primitive2DReference xRef(
1066 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1067 aSource,
1068 1.0 - fOpacity));
1070 aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
1073 if(getClipPathXLink().getLength())
1075 // try to access linked ClipPath
1076 const SvgClipPathNode* mpClip = dynamic_cast< const SvgClipPathNode* >(mrOwner.getDocument().findSvgNodeById(getClipPathXLink()));
1078 if(mpClip)
1080 mpClip->apply(aSource);
1084 if(aSource.hasElements()) // test again, applied clipPath may have lead to empty geometry
1086 if(getMaskXLink().getLength())
1088 // try to access linked Mask
1089 const SvgMaskNode* mpMask = dynamic_cast< const SvgMaskNode* >(mrOwner.getDocument().findSvgNodeById(getMaskXLink()));
1091 if(mpMask)
1093 mpMask->apply(aSource);
1097 if(aSource.hasElements()) // test again, applied mask may have lead to empty geometry
1099 if(pTransform)
1101 // create embedding group element with transformation
1102 const drawinglayer::primitive2d::Primitive2DReference xRef(
1103 new drawinglayer::primitive2d::TransformPrimitive2D(
1104 *pTransform,
1105 aSource));
1107 aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
1110 // append to current target
1111 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSource);
1117 SvgStyleAttributes::SvgStyleAttributes(SvgNode& rOwner)
1118 : mrOwner(rOwner),
1119 mpCssStyleParent(0),
1120 maFill(),
1121 maStroke(),
1122 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
1123 maStrokeWidth(),
1124 maStopOpacity(),
1125 mpSvgGradientNodeFill(0),
1126 mpSvgGradientNodeStroke(0),
1127 mpSvgPatternNodeFill(0),
1128 mpSvgPatternNodeStroke(0),
1129 maFillOpacity(),
1130 maStrokeDasharray(),
1131 maStrokeDashOffset(),
1132 maStrokeLinecap(StrokeLinecap_notset),
1133 maStrokeLinejoin(StrokeLinejoin_notset),
1134 maStrokeMiterLimit(),
1135 maStrokeOpacity(),
1136 maFontFamily(),
1137 maFontSize(),
1138 maFontStretch(FontStretch_notset),
1139 maFontStyle(FontStyle_notset),
1140 maFontVariant(FontVariant_notset),
1141 maFontWeight(FontWeight_notset),
1142 maTextAlign(TextAlign_notset),
1143 maTextDecoration(TextDecoration_notset),
1144 maTextAnchor(TextAnchor_notset),
1145 maColor(),
1146 maOpacity(1.0),
1147 maTitle(),
1148 maDesc(),
1149 maClipPathXLink(),
1150 maMaskXLink(),
1151 maMarkerStartXLink(),
1152 mpMarkerStartXLink(0),
1153 maMarkerMidXLink(),
1154 mpMarkerMidXLink(0),
1155 maMarkerEndXLink(),
1156 mpMarkerEndXLink(0),
1157 maFillRule(FillRule_notset),
1158 maClipRule(FillRule_nonzero),
1159 mbIsClipPathContent(SVGTokenClipPathNode == mrOwner.getType()),
1160 mbStrokeDasharraySet(false)
1162 if(!mbIsClipPathContent)
1164 const SvgStyleAttributes* pParentStyle = getParentStyle();
1166 if(pParentStyle)
1168 mbIsClipPathContent = pParentStyle->mbIsClipPathContent;
1173 SvgStyleAttributes::~SvgStyleAttributes()
1177 void SvgStyleAttributes::parseStyleAttribute(const OUString& /*rTokenName*/, SVGToken aSVGToken, const OUString& aContent)
1179 switch(aSVGToken)
1181 case SVGTokenFill:
1183 SvgPaint aSvgPaint;
1184 OUString aURL;
1186 if(readSvgPaint(aContent, aSvgPaint, aURL))
1188 setFill(aSvgPaint);
1190 else if(aURL.getLength())
1192 const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL);
1194 if(pNode)
1196 if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType())
1198 setSvgGradientNodeFill(static_cast< const SvgGradientNode* >(pNode));
1200 else if(SVGTokenPattern == pNode->getType())
1202 setSvgPatternNodeFill(static_cast< const SvgPatternNode* >(pNode));
1206 break;
1208 case SVGTokenFillOpacity:
1210 SvgNumber aNum;
1212 if(readSingleNumber(aContent, aNum))
1214 if(aNum.isPositive())
1216 setFillOpacity(aNum);
1219 break;
1221 case SVGTokenFillRule:
1223 if(aContent.getLength())
1225 if(aContent.match(commonStrings::aStrNonzero))
1227 maFillRule = FillRule_nonzero;
1229 else if(aContent.match(commonStrings::aStrEvenOdd))
1231 maFillRule = FillRule_evenodd;
1234 break;
1236 case SVGTokenStroke:
1238 SvgPaint aSvgPaint;
1239 OUString aURL;
1241 if(readSvgPaint(aContent, aSvgPaint, aURL))
1243 setStroke(aSvgPaint);
1245 else if(aURL.getLength())
1247 const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL);
1249 if(pNode)
1251 if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType())
1253 setSvgGradientNodeStroke(static_cast< const SvgGradientNode* >(pNode));
1255 else if(SVGTokenPattern == pNode->getType())
1257 setSvgPatternNodeStroke(static_cast< const SvgPatternNode* >(pNode));
1261 break;
1263 case SVGTokenStrokeDasharray:
1265 if(aContent.getLength())
1267 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
1268 SvgNumberVector aVector;
1270 if(aContent.match(aStrNone))
1272 // #121221# The special value 'none' needs to be handled
1273 // in the sense that *when* it is set, the parent shall not
1274 // be used. Before this was only dependent on the array being
1275 // empty
1276 setStrokeDasharraySet(true);
1278 else if(readSvgNumberVector(aContent, aVector))
1280 setStrokeDasharray(aVector);
1283 break;
1285 case SVGTokenStrokeDashoffset:
1287 SvgNumber aNum;
1289 if(readSingleNumber(aContent, aNum))
1291 if(aNum.isPositive())
1293 setStrokeDashOffset(aNum);
1296 break;
1298 case SVGTokenStrokeLinecap:
1300 if(aContent.getLength())
1302 static OUString aStrButt(OUString::createFromAscii("butt"));
1303 static OUString aStrRound(OUString::createFromAscii("round"));
1304 static OUString aStrSquare(OUString::createFromAscii("square"));
1306 if(aContent.match(aStrButt))
1308 setStrokeLinecap(StrokeLinecap_butt);
1310 else if(aContent.match(aStrRound))
1312 setStrokeLinecap(StrokeLinecap_round);
1314 else if(aContent.match(aStrSquare))
1316 setStrokeLinecap(StrokeLinecap_square);
1319 break;
1321 case SVGTokenStrokeLinejoin:
1323 if(aContent.getLength())
1325 static OUString aStrMiter(OUString::createFromAscii("miter"));
1326 static OUString aStrRound(OUString::createFromAscii("round"));
1327 static OUString aStrBevel(OUString::createFromAscii("bevel"));
1329 if(aContent.match(aStrMiter))
1331 setStrokeLinejoin(StrokeLinejoin_miter);
1333 else if(aContent.match(aStrRound))
1335 setStrokeLinejoin(StrokeLinejoin_round);
1337 else if(aContent.match(aStrBevel))
1339 setStrokeLinejoin(StrokeLinejoin_bevel);
1342 break;
1344 case SVGTokenStrokeMiterlimit:
1346 SvgNumber aNum;
1348 if(readSingleNumber(aContent, aNum))
1350 if(aNum.isPositive())
1352 setStrokeMiterLimit(aNum);
1355 break;
1357 case SVGTokenStrokeOpacity:
1359 SvgNumber aNum;
1361 if(readSingleNumber(aContent, aNum))
1363 if(aNum.isPositive())
1365 setStrokeOpacity(aNum);
1368 break;
1370 case SVGTokenStrokeWidth:
1372 SvgNumber aNum;
1374 if(readSingleNumber(aContent, aNum))
1376 if(aNum.isPositive())
1378 setStrokeWidth(aNum);
1381 break;
1383 case SVGTokenStopColor:
1385 SvgPaint aSvgPaint;
1386 OUString aURL;
1388 if(readSvgPaint(aContent, aSvgPaint, aURL))
1390 setStopColor(aSvgPaint);
1392 break;
1394 case SVGTokenStopOpacity:
1396 SvgNumber aNum;
1398 if(readSingleNumber(aContent, aNum))
1400 if(aNum.isPositive())
1402 setStopOpacity(aNum);
1405 break;
1407 case SVGTokenFont:
1409 break;
1411 case SVGTokenFontFamily:
1413 SvgStringVector aSvgStringVector;
1415 if(readSvgStringVector(aContent, aSvgStringVector))
1417 setFontFamily(aSvgStringVector);
1419 break;
1421 case SVGTokenFontSize:
1423 SvgNumber aNum;
1425 if(readSingleNumber(aContent, aNum))
1427 setFontSize(aNum);
1429 break;
1431 case SVGTokenFontSizeAdjust:
1433 break;
1435 case SVGTokenFontStretch:
1437 if(aContent.getLength())
1439 static OUString aStrNormal(OUString::createFromAscii("normal"));
1440 static OUString aStrWider(OUString::createFromAscii("wider"));
1441 static OUString aStrNarrower(OUString::createFromAscii("narrower"));
1442 static OUString aStrUltra_condensed(OUString::createFromAscii("ultra-condensed"));
1443 static OUString aStrExtra_condensed(OUString::createFromAscii("extra-condensed"));
1444 static OUString aStrCondensed(OUString::createFromAscii("condensed"));
1445 static OUString aStrSemi_condensed(OUString::createFromAscii("semi-condensed"));
1446 static OUString aStrSemi_expanded(OUString::createFromAscii("semi-expanded"));
1447 static OUString aStrExpanded(OUString::createFromAscii("expanded"));
1448 static OUString aStrExtra_expanded(OUString::createFromAscii("extra-expanded"));
1449 static OUString aStrUltra_expanded(OUString::createFromAscii("ultra-expanded"));
1451 if(aContent.match(aStrNormal))
1453 setFontStretch(FontStretch_normal);
1455 else if(aContent.match(aStrWider))
1457 setFontStretch(FontStretch_wider);
1459 else if(aContent.match(aStrNarrower))
1461 setFontStretch(FontStretch_narrower);
1463 else if(aContent.match(aStrUltra_condensed))
1465 setFontStretch(FontStretch_ultra_condensed);
1467 else if(aContent.match(aStrExtra_condensed))
1469 setFontStretch(FontStretch_extra_condensed);
1471 else if(aContent.match(aStrCondensed))
1473 setFontStretch(FontStretch_condensed);
1475 else if(aContent.match(aStrSemi_condensed))
1477 setFontStretch(FontStretch_semi_condensed);
1479 else if(aContent.match(aStrSemi_expanded))
1481 setFontStretch(FontStretch_semi_expanded);
1483 else if(aContent.match(aStrExpanded))
1485 setFontStretch(FontStretch_expanded);
1487 else if(aContent.match(aStrExtra_expanded))
1489 setFontStretch(FontStretch_extra_expanded);
1491 else if(aContent.match(aStrUltra_expanded))
1493 setFontStretch(FontStretch_ultra_expanded);
1496 break;
1498 case SVGTokenFontStyle:
1500 if(aContent.getLength())
1502 static OUString aStrNormal(OUString::createFromAscii("normal"));
1503 static OUString aStrItalic(OUString::createFromAscii("italic"));
1504 static OUString aStrOblique(OUString::createFromAscii("oblique"));
1506 if(aContent.match(aStrNormal))
1508 setFontStyle(FontStyle_normal);
1510 else if(aContent.match(aStrItalic))
1512 setFontStyle(FontStyle_italic);
1514 else if(aContent.match(aStrOblique))
1516 setFontStyle(FontStyle_oblique);
1519 break;
1521 case SVGTokenFontVariant:
1523 if(aContent.getLength())
1525 static OUString aStrNormal(OUString::createFromAscii("normal"));
1526 static OUString aStrSmallCaps(OUString::createFromAscii("small-caps"));
1528 if(aContent.match(aStrNormal))
1530 setFontVariant(FontVariant_normal);
1532 else if(aContent.match(aStrSmallCaps))
1534 setFontVariant(FontVariant_small_caps);
1537 break;
1539 case SVGTokenFontWeight:
1541 if(aContent.getLength())
1543 static OUString aStrNormal(OUString::createFromAscii("normal"));
1544 static OUString aStrBold(OUString::createFromAscii("bold"));
1545 static OUString aStrBolder(OUString::createFromAscii("bolder"));
1546 static OUString aStrLighter(OUString::createFromAscii("lighter"));
1547 static OUString aStr100(OUString::createFromAscii("100"));
1548 static OUString aStr200(OUString::createFromAscii("200"));
1549 static OUString aStr300(OUString::createFromAscii("300"));
1550 static OUString aStr400(OUString::createFromAscii("400"));
1551 static OUString aStr500(OUString::createFromAscii("500"));
1552 static OUString aStr600(OUString::createFromAscii("600"));
1553 static OUString aStr700(OUString::createFromAscii("700"));
1554 static OUString aStr800(OUString::createFromAscii("800"));
1555 static OUString aStr900(OUString::createFromAscii("900"));
1557 if(aContent.match(aStr100))
1559 setFontWeight(FontWeight_100);
1561 else if(aContent.match(aStr200))
1563 setFontWeight(FontWeight_200);
1565 else if(aContent.match(aStr300))
1567 setFontWeight(FontWeight_300);
1569 else if(aContent.match(aStr400) || aContent.match(aStrNormal))
1571 setFontWeight(FontWeight_400);
1573 else if(aContent.match(aStr500))
1575 setFontWeight(FontWeight_500);
1577 else if(aContent.match(aStr600))
1579 setFontWeight(FontWeight_600);
1581 else if(aContent.match(aStr700) || aContent.match(aStrBold))
1583 setFontWeight(FontWeight_700);
1585 else if(aContent.match(aStr800))
1587 setFontWeight(FontWeight_800);
1589 else if(aContent.match(aStr900))
1591 setFontWeight(FontWeight_900);
1593 else if(aContent.match(aStrBolder))
1595 setFontWeight(FontWeight_bolder);
1597 else if(aContent.match(aStrLighter))
1599 setFontWeight(FontWeight_lighter);
1602 break;
1604 case SVGTokenDirection:
1606 break;
1608 case SVGTokenLetterSpacing:
1610 break;
1612 case SVGTokenTextDecoration:
1614 if(aContent.getLength())
1616 static OUString aStrNone(OUString::createFromAscii("none"));
1617 static OUString aStrUnderline(OUString::createFromAscii("underline"));
1618 static OUString aStrOverline(OUString::createFromAscii("overline"));
1619 static OUString aStrLineThrough(OUString::createFromAscii("line-through"));
1620 static OUString aStrBlink(OUString::createFromAscii("blink"));
1622 if(aContent.match(aStrNone))
1624 setTextDecoration(TextDecoration_none);
1626 else if(aContent.match(aStrUnderline))
1628 setTextDecoration(TextDecoration_underline);
1630 else if(aContent.match(aStrOverline))
1632 setTextDecoration(TextDecoration_overline);
1634 else if(aContent.match(aStrLineThrough))
1636 setTextDecoration(TextDecoration_line_through);
1638 else if(aContent.match(aStrBlink))
1640 setTextDecoration(TextDecoration_blink);
1643 break;
1645 case SVGTokenUnicodeBidi:
1647 break;
1649 case SVGTokenWordSpacing:
1651 break;
1653 case SVGTokenTextAnchor:
1655 if(aContent.getLength())
1657 static OUString aStrStart(OUString::createFromAscii("start"));
1658 static OUString aStrMiddle(OUString::createFromAscii("middle"));
1659 static OUString aStrEnd(OUString::createFromAscii("end"));
1661 if(aContent.match(aStrStart))
1663 setTextAnchor(TextAnchor_start);
1665 else if(aContent.match(aStrMiddle))
1667 setTextAnchor(TextAnchor_middle);
1669 else if(aContent.match(aStrEnd))
1671 setTextAnchor(TextAnchor_end);
1674 break;
1676 case SVGTokenTextAlign:
1678 if(aContent.getLength())
1680 static OUString aStrLeft(OUString::createFromAscii("left"));
1681 static OUString aStrRight(OUString::createFromAscii("right"));
1682 static OUString aStrCenter(OUString::createFromAscii("center"));
1683 static OUString aStrJustify(OUString::createFromAscii("justify"));
1685 if(aContent.match(aStrLeft))
1687 setTextAlign(TextAlign_left);
1689 else if(aContent.match(aStrRight))
1691 setTextAlign(TextAlign_right);
1693 else if(aContent.match(aStrCenter))
1695 setTextAlign(TextAlign_center);
1697 else if(aContent.match(aStrJustify))
1699 setTextAlign(TextAlign_justify);
1702 break;
1704 case SVGTokenColor:
1706 SvgPaint aSvgPaint;
1707 OUString aURL;
1709 if(readSvgPaint(aContent, aSvgPaint, aURL))
1711 setColor(aSvgPaint);
1713 break;
1715 case SVGTokenOpacity:
1717 SvgNumber aNum;
1719 if(readSingleNumber(aContent, aNum))
1721 setOpacity(SvgNumber(basegfx::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet()));
1723 break;
1725 case SVGTokenTitle:
1727 setTitle(aContent);
1728 break;
1730 case SVGTokenDesc:
1732 setDesc(aContent);
1733 break;
1735 case SVGTokenClipPathProperty:
1737 readLocalUrl(aContent, maClipPathXLink);
1738 break;
1740 case SVGTokenMask:
1742 readLocalUrl(aContent, maMaskXLink);
1743 break;
1745 case SVGTokenClipRule:
1747 if(aContent.getLength())
1749 if(aContent.match(commonStrings::aStrNonzero))
1751 maClipRule = FillRule_nonzero;
1753 else if(aContent.match(commonStrings::aStrEvenOdd))
1755 maClipRule = FillRule_evenodd;
1758 break;
1760 case SVGTokenMarker:
1762 readLocalUrl(aContent, maMarkerEndXLink);
1763 maMarkerStartXLink = maMarkerMidXLink = maMarkerEndXLink;
1764 break;
1766 case SVGTokenMarkerStart:
1768 readLocalUrl(aContent, maMarkerStartXLink);
1769 break;
1771 case SVGTokenMarkerMid:
1773 readLocalUrl(aContent, maMarkerMidXLink);
1774 break;
1776 case SVGTokenMarkerEnd:
1778 readLocalUrl(aContent, maMarkerEndXLink);
1779 break;
1781 default:
1783 break;
1788 const basegfx::BColor* SvgStyleAttributes::getFill() const
1790 if(mbIsClipPathContent)
1792 static basegfx::BColor aBlack(0.0, 0.0, 0.0);
1794 return &aBlack;
1796 else if(maFill.isSet())
1798 if(maFill.isCurrent())
1800 return getColor();
1802 else if(maFill.isOn())
1804 return &maFill.getBColor();
1807 else
1809 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1811 if(pSvgStyleAttributes)
1813 return pSvgStyleAttributes->getFill();
1817 return 0;
1820 const basegfx::BColor* SvgStyleAttributes::getStroke() const
1822 if(mbIsClipPathContent)
1824 return 0;
1826 else if(maStroke.isSet())
1828 if(maStroke.isCurrent())
1830 return getColor();
1832 else if(maStroke.isOn())
1834 return &maStroke.getBColor();
1837 else
1839 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1841 if(pSvgStyleAttributes)
1843 return pSvgStyleAttributes->getStroke();
1847 return 0;
1850 const basegfx::BColor& SvgStyleAttributes::getStopColor() const
1852 if(maStopColor.isCurrent())
1854 return *getColor();
1856 else
1858 return maStopColor.getBColor();
1862 const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeFill() const
1864 if(mbIsClipPathContent)
1866 return 0;
1868 else if(mpSvgGradientNodeFill)
1870 return mpSvgGradientNodeFill;
1872 else
1874 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1876 if(pSvgStyleAttributes)
1878 return pSvgStyleAttributes->getSvgGradientNodeFill();
1882 return 0;
1885 const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeStroke() const
1887 if(mbIsClipPathContent)
1889 return 0;
1891 else if(mpSvgGradientNodeStroke)
1893 return mpSvgGradientNodeStroke;
1895 else
1897 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1899 if(pSvgStyleAttributes)
1901 return pSvgStyleAttributes->getSvgGradientNodeStroke();
1905 return 0;
1908 const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeFill() const
1910 if(mbIsClipPathContent)
1912 return 0;
1914 else if(mpSvgPatternNodeFill)
1916 return mpSvgPatternNodeFill;
1918 else
1920 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1922 if(pSvgStyleAttributes)
1924 return pSvgStyleAttributes->getSvgPatternNodeFill();
1928 return 0;
1931 const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeStroke() const
1933 if(mbIsClipPathContent)
1935 return 0;
1937 else if(mpSvgPatternNodeStroke)
1939 return mpSvgPatternNodeStroke;
1941 else
1943 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1945 if(pSvgStyleAttributes)
1947 return pSvgStyleAttributes->getSvgPatternNodeStroke();
1951 return 0;
1954 const SvgNumber SvgStyleAttributes::getStrokeWidth() const
1956 if(mbIsClipPathContent)
1958 return SvgNumber(0.0);
1960 else if(maStrokeWidth.isSet())
1962 return maStrokeWidth;
1965 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1967 if(pSvgStyleAttributes)
1969 return pSvgStyleAttributes->getStrokeWidth();
1972 // default is 1
1973 return SvgNumber(1.0);
1976 const SvgNumber SvgStyleAttributes::getStopOpacity() const
1978 if(maStopOpacity.isSet())
1980 return maStopOpacity;
1983 // default is 1
1984 return SvgNumber(1.0);
1987 const SvgNumber SvgStyleAttributes::getFillOpacity() const
1989 if(mbIsClipPathContent)
1991 return SvgNumber(1.0);
1993 else if(maFillOpacity.isSet())
1995 return maFillOpacity;
1998 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2000 if(pSvgStyleAttributes)
2002 return pSvgStyleAttributes->getFillOpacity();
2005 // default is 1
2006 return SvgNumber(1.0);
2009 FillRule SvgStyleAttributes::getFillRule() const
2011 if(FillRule_notset != maFillRule)
2013 return maFillRule;
2016 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2018 if(pSvgStyleAttributes)
2020 return pSvgStyleAttributes->getFillRule();
2023 // default is NonZero
2024 return FillRule_nonzero;
2027 const SvgNumberVector& SvgStyleAttributes::getStrokeDasharray() const
2029 if(!maStrokeDasharray.empty())
2031 return maStrokeDasharray;
2033 else if(getStrokeDasharraySet())
2035 // #121221# is set to empty *by purpose*, do not visit parent styles
2036 return maStrokeDasharray;
2039 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2041 if(pSvgStyleAttributes)
2043 return pSvgStyleAttributes->getStrokeDasharray();
2046 // default empty
2047 return maStrokeDasharray;
2050 const SvgNumber SvgStyleAttributes::getStrokeDashOffset() const
2052 if(maStrokeDashOffset.isSet())
2054 return maStrokeDashOffset;
2057 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2059 if(pSvgStyleAttributes)
2061 return pSvgStyleAttributes->getStrokeDashOffset();
2064 // default is 0
2065 return SvgNumber(0.0);
2068 StrokeLinecap SvgStyleAttributes::getStrokeLinecap() const
2070 if(maStrokeLinecap != StrokeLinecap_notset)
2072 return maStrokeLinecap;
2075 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2077 if(pSvgStyleAttributes)
2079 return pSvgStyleAttributes->getStrokeLinecap();
2082 // default is StrokeLinecap_butt
2083 return StrokeLinecap_butt;
2086 StrokeLinejoin SvgStyleAttributes::getStrokeLinejoin() const
2088 if(maStrokeLinejoin != StrokeLinejoin_notset)
2090 return maStrokeLinejoin;
2093 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2095 if(pSvgStyleAttributes)
2097 return pSvgStyleAttributes->getStrokeLinejoin();
2100 // default is StrokeLinejoin_butt
2101 return StrokeLinejoin_miter;
2104 const SvgNumber SvgStyleAttributes::getStrokeMiterLimit() const
2106 if(maStrokeMiterLimit.isSet())
2108 return maStrokeMiterLimit;
2111 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2113 if(pSvgStyleAttributes)
2115 return pSvgStyleAttributes->getStrokeMiterLimit();
2118 // default is 4
2119 return SvgNumber(4.0);
2122 const SvgNumber SvgStyleAttributes::getStrokeOpacity() const
2124 if(maStrokeOpacity.isSet())
2126 return maStrokeOpacity;
2129 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2131 if(pSvgStyleAttributes)
2133 return pSvgStyleAttributes->getStrokeOpacity();
2136 // default is 1
2137 return SvgNumber(1.0);
2140 const SvgStringVector& SvgStyleAttributes::getFontFamily() const
2142 if(!maFontFamily.empty())
2144 return maFontFamily;
2147 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2149 if(pSvgStyleAttributes)
2151 return pSvgStyleAttributes->getFontFamily();
2154 // default is empty
2155 return maFontFamily;
2158 const SvgNumber SvgStyleAttributes::getFontSize() const
2160 if(maFontSize.isSet())
2162 return maFontSize;
2165 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2167 if(pSvgStyleAttributes)
2169 return pSvgStyleAttributes->getFontSize();
2172 // default is 'medium'
2173 return SvgNumber(12.0);
2176 FontStretch SvgStyleAttributes::getFontStretch() const
2178 if(maFontStretch != FontStretch_notset)
2180 if(FontStretch_wider != maFontStretch && FontStretch_narrower != maFontStretch)
2182 return maFontStretch;
2186 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2188 if(pSvgStyleAttributes)
2190 FontStretch aInherited = pSvgStyleAttributes->getFontStretch();
2192 if(FontStretch_wider == maFontStretch)
2194 aInherited = getWider(aInherited);
2196 else if(FontStretch_narrower == maFontStretch)
2198 aInherited = getNarrower(aInherited);
2201 return aInherited;
2204 // default is FontStretch_normal
2205 return FontStretch_normal;
2208 FontStyle SvgStyleAttributes::getFontStyle() const
2210 if(maFontStyle != FontStyle_notset)
2212 return maFontStyle;
2215 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2217 if(pSvgStyleAttributes)
2219 return pSvgStyleAttributes->getFontStyle();
2222 // default is FontStyle_normal
2223 return FontStyle_normal;
2226 FontWeight SvgStyleAttributes::getFontWeight() const
2228 if(maFontWeight != FontWeight_notset)
2230 if(FontWeight_bolder != maFontWeight && FontWeight_lighter != maFontWeight)
2232 return maFontWeight;
2236 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2238 if(pSvgStyleAttributes)
2240 FontWeight aInherited = pSvgStyleAttributes->getFontWeight();
2242 if(FontWeight_bolder == maFontWeight)
2244 aInherited = getBolder(aInherited);
2246 else if(FontWeight_lighter == maFontWeight)
2248 aInherited = getLighter(aInherited);
2251 return aInherited;
2254 // default is FontWeight_400 (FontWeight_normal)
2255 return FontWeight_400;
2258 TextAlign SvgStyleAttributes::getTextAlign() const
2260 if(maTextAlign != TextAlign_notset)
2262 return maTextAlign;
2265 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2267 if(pSvgStyleAttributes)
2269 return pSvgStyleAttributes->getTextAlign();
2272 // default is TextAlign_left
2273 return TextAlign_left;
2276 const SvgStyleAttributes* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const
2278 if(maTextDecoration != TextDecoration_notset)
2280 return this;
2283 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2285 if(pSvgStyleAttributes)
2287 return pSvgStyleAttributes->getTextDecorationDefiningSvgStyleAttributes();
2290 // default is 0
2291 return 0;
2294 TextDecoration SvgStyleAttributes::getTextDecoration() const
2296 const SvgStyleAttributes* pDefining = getTextDecorationDefiningSvgStyleAttributes();
2298 if(pDefining)
2300 return pDefining->maTextDecoration;
2302 else
2304 // default is TextDecoration_none
2305 return TextDecoration_none;
2309 TextAnchor SvgStyleAttributes::getTextAnchor() const
2311 if(maTextAnchor != TextAnchor_notset)
2313 return maTextAnchor;
2316 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2318 if(pSvgStyleAttributes)
2320 return pSvgStyleAttributes->getTextAnchor();
2323 // default is TextAnchor_start
2324 return TextAnchor_start;
2327 const basegfx::BColor* SvgStyleAttributes::getColor() const
2329 if(maColor.isSet())
2331 if(maColor.isCurrent())
2333 OSL_ENSURE(false, "Svg error: current color uses current color (!)");
2334 return 0;
2336 else if(maColor.isOn())
2338 return &maColor.getBColor();
2341 else
2343 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2345 if(pSvgStyleAttributes)
2347 return pSvgStyleAttributes->getColor();
2351 return 0;
2354 const OUString SvgStyleAttributes::getMarkerStartXLink() const
2356 if(maMarkerStartXLink.getLength())
2358 return maMarkerStartXLink;
2361 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2363 if(pSvgStyleAttributes)
2365 return pSvgStyleAttributes->getMarkerStartXLink();
2368 return OUString();
2371 const SvgMarkerNode* SvgStyleAttributes::accessMarkerStartXLink() const
2373 if(!mpMarkerStartXLink)
2375 const OUString aMarker(getMarkerStartXLink());
2377 if(aMarker.getLength())
2379 const_cast< SvgStyleAttributes* >(this)->mpMarkerStartXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerStartXLink()));
2383 return mpMarkerStartXLink;
2386 const OUString SvgStyleAttributes::getMarkerMidXLink() const
2388 if(maMarkerMidXLink.getLength())
2390 return maMarkerMidXLink;
2393 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2395 if(pSvgStyleAttributes)
2397 return pSvgStyleAttributes->getMarkerMidXLink();
2400 return OUString();
2403 const SvgMarkerNode* SvgStyleAttributes::accessMarkerMidXLink() const
2405 if(!mpMarkerMidXLink)
2407 const OUString aMarker(getMarkerMidXLink());
2409 if(aMarker.getLength())
2411 const_cast< SvgStyleAttributes* >(this)->mpMarkerMidXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerMidXLink()));
2415 return mpMarkerMidXLink;
2418 const OUString SvgStyleAttributes::getMarkerEndXLink() const
2420 if(maMarkerEndXLink.getLength())
2422 return maMarkerEndXLink;
2425 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2427 if(pSvgStyleAttributes)
2429 return pSvgStyleAttributes->getMarkerEndXLink();
2432 return OUString();
2435 const SvgMarkerNode* SvgStyleAttributes::accessMarkerEndXLink() const
2437 if(!mpMarkerEndXLink)
2439 const OUString aMarker(getMarkerEndXLink());
2441 if(aMarker.getLength())
2443 const_cast< SvgStyleAttributes* >(this)->mpMarkerEndXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerEndXLink()));
2447 return mpMarkerEndXLink;
2450 } // end of namespace svgreader
2451 } // end of namespace svgio
2453 //////////////////////////////////////////////////////////////////////////////
2454 // eof
2456 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */