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