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(pTransform)
1083                 {
1084                     // create embedding group element with transformation. This applies the given
1085                     // transformation to the graphical content, but *not* to mask and/or clip (as needed)
1086                     const drawinglayer::primitive2d::Primitive2DReference xRef(
1087                         new drawinglayer::primitive2d::TransformPrimitive2D(
1088                             *pTransform,
1089                             aSource));
1090 
1091                     aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
1092                 }
1093 
1094                 if(getClipPathXLink().getLength())
1095                 {
1096                     // try to access linked ClipPath
1097                     const SvgClipPathNode* mpClip = dynamic_cast< const SvgClipPathNode* >(mrOwner.getDocument().findSvgNodeById(getClipPathXLink()));
1098 
1099                     if(mpClip)
1100                     {
1101                         mpClip->apply(aSource);
1102                     }
1103                 }
1104 
1105                 if(aSource.hasElements()) // test again, applied clipPath may have lead to empty geometry
1106                 {
1107                     if(getMaskXLink().getLength())
1108                     {
1109                         // try to access linked Mask
1110                         const SvgMaskNode* mpMask = dynamic_cast< const SvgMaskNode* >(mrOwner.getDocument().findSvgNodeById(getMaskXLink()));
1111 
1112                         if(mpMask)
1113                         {
1114                             mpMask->apply(aSource);
1115                         }
1116                     }
1117 
1118                     if(aSource.hasElements()) // test again, applied mask may have lead to empty geometry
1119                     {
1120                         // append to current target
1121                         drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSource);
1122                     }
1123                 }
1124             }
1125         }
1126 
1127         SvgStyleAttributes::SvgStyleAttributes(SvgNode& rOwner)
1128         :   mrOwner(rOwner),
1129             mpCssStyleParent(0),
1130             maFill(),
1131             maStroke(),
1132             maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
1133             maStrokeWidth(),
1134             maStopOpacity(),
1135             mpSvgGradientNodeFill(0),
1136             mpSvgGradientNodeStroke(0),
1137             mpSvgPatternNodeFill(0),
1138             mpSvgPatternNodeStroke(0),
1139             maFillOpacity(),
1140             maStrokeDasharray(),
1141             maStrokeDashOffset(),
1142             maStrokeLinecap(StrokeLinecap_notset),
1143             maStrokeLinejoin(StrokeLinejoin_notset),
1144             maStrokeMiterLimit(),
1145             maStrokeOpacity(),
1146             maFontFamily(),
1147             maFontSize(),
1148             maFontStretch(FontStretch_notset),
1149             maFontStyle(FontStyle_notset),
1150             maFontVariant(FontVariant_notset),
1151             maFontWeight(FontWeight_notset),
1152             maTextAlign(TextAlign_notset),
1153             maTextDecoration(TextDecoration_notset),
1154             maTextAnchor(TextAnchor_notset),
1155             maColor(),
1156             maOpacity(1.0),
1157             maTitle(),
1158             maDesc(),
1159             maClipPathXLink(),
1160             maMaskXLink(),
1161             maMarkerStartXLink(),
1162             mpMarkerStartXLink(0),
1163             maMarkerMidXLink(),
1164             mpMarkerMidXLink(0),
1165             maMarkerEndXLink(),
1166             mpMarkerEndXLink(0),
1167             maFillRule(FillRule_notset),
1168             maClipRule(FillRule_nonzero),
1169             mbIsClipPathContent(SVGTokenClipPathNode == mrOwner.getType()),
1170             mbStrokeDasharraySet(false)
1171         {
1172             if(!mbIsClipPathContent)
1173             {
1174                 const SvgStyleAttributes* pParentStyle = getParentStyle();
1175 
1176                 if(pParentStyle)
1177                 {
1178                     mbIsClipPathContent = pParentStyle->mbIsClipPathContent;
1179                 }
1180             }
1181         }
1182 
1183         SvgStyleAttributes::~SvgStyleAttributes()
1184         {
1185         }
1186 
1187         void SvgStyleAttributes::parseStyleAttribute(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent)
1188         {
1189             switch(aSVGToken)
1190             {
1191                 case SVGTokenFill:
1192                 {
1193                     SvgPaint aSvgPaint;
1194                     rtl::OUString aURL;
1195 
1196                     if(readSvgPaint(aContent, aSvgPaint, aURL))
1197                     {
1198                         setFill(aSvgPaint);
1199                     }
1200                     else if(aURL.getLength())
1201                     {
1202                         const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL);
1203 
1204                         if(pNode)
1205                         {
1206                             if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient  == pNode->getType())
1207                             {
1208                                 setSvgGradientNodeFill(static_cast< const SvgGradientNode* >(pNode));
1209                             }
1210                             else if(SVGTokenPattern == pNode->getType())
1211                             {
1212                                 setSvgPatternNodeFill(static_cast< const SvgPatternNode* >(pNode));
1213                             }
1214                         }
1215                     }
1216                     break;
1217                 }
1218                 case SVGTokenFillOpacity:
1219                 {
1220                     SvgNumber aNum;
1221 
1222                     if(readSingleNumber(aContent, aNum))
1223                     {
1224                         if(aNum.isPositive())
1225                         {
1226                             setFillOpacity(aNum);
1227                         }
1228                     }
1229                     break;
1230                 }
1231                 case SVGTokenFillRule:
1232                 {
1233                     if(aContent.getLength())
1234                     {
1235                         if(aContent.match(commonStrings::aStrNonzero))
1236                         {
1237                             maFillRule = FillRule_nonzero;
1238                         }
1239                         else if(aContent.match(commonStrings::aStrEvenOdd))
1240                         {
1241                             maFillRule = FillRule_evenodd;
1242                         }
1243                     }
1244                     break;
1245                 }
1246                 case SVGTokenStroke:
1247                 {
1248                     SvgPaint aSvgPaint;
1249                     rtl::OUString aURL;
1250 
1251                     if(readSvgPaint(aContent, aSvgPaint, aURL))
1252                     {
1253                         setStroke(aSvgPaint);
1254                     }
1255                     else if(aURL.getLength())
1256                     {
1257                         const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL);
1258 
1259                         if(pNode)
1260                         {
1261                             if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient  == pNode->getType())
1262                             {
1263                                 setSvgGradientNodeStroke(static_cast< const SvgGradientNode* >(pNode));
1264                             }
1265                             else if(SVGTokenPattern == pNode->getType())
1266                             {
1267                                 setSvgPatternNodeStroke(static_cast< const SvgPatternNode* >(pNode));
1268                             }
1269                         }
1270                     }
1271                     break;
1272                 }
1273                 case SVGTokenStrokeDasharray:
1274                 {
1275                     if(aContent.getLength())
1276                     {
1277                         static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
1278                         SvgNumberVector aVector;
1279 
1280                         if(aContent.match(aStrNone))
1281                         {
1282                             // #121221# The special value 'none' needs to be handled
1283                             // in the sense that *when* it is set, the parent shall not
1284                             // be used. Before this was only dependent on the array being
1285                             // empty
1286                             setStrokeDasharraySet(true);
1287                         }
1288                         else if(readSvgNumberVector(aContent, aVector))
1289                         {
1290                             setStrokeDasharray(aVector);
1291                         }
1292                     }
1293                     break;
1294                 }
1295                 case SVGTokenStrokeDashoffset:
1296                 {
1297                     SvgNumber aNum;
1298 
1299                     if(readSingleNumber(aContent, aNum))
1300                     {
1301                         if(aNum.isPositive())
1302                         {
1303                             setStrokeDashOffset(aNum);
1304                         }
1305                     }
1306                     break;
1307                 }
1308                 case SVGTokenStrokeLinecap:
1309                 {
1310                     if(aContent.getLength())
1311                     {
1312                         static rtl::OUString aStrButt(rtl::OUString::createFromAscii("butt"));
1313                         static rtl::OUString aStrRound(rtl::OUString::createFromAscii("round"));
1314                         static rtl::OUString aStrSquare(rtl::OUString::createFromAscii("square"));
1315 
1316                         if(aContent.match(aStrButt))
1317                         {
1318                             setStrokeLinecap(StrokeLinecap_butt);
1319                         }
1320                         else if(aContent.match(aStrRound))
1321                         {
1322                             setStrokeLinecap(StrokeLinecap_round);
1323                         }
1324                         else if(aContent.match(aStrSquare))
1325                         {
1326                             setStrokeLinecap(StrokeLinecap_square);
1327                         }
1328                     }
1329                     break;
1330                 }
1331                 case SVGTokenStrokeLinejoin:
1332                 {
1333                     if(aContent.getLength())
1334                     {
1335                         static rtl::OUString aStrMiter(rtl::OUString::createFromAscii("miter"));
1336                         static rtl::OUString aStrRound(rtl::OUString::createFromAscii("round"));
1337                         static rtl::OUString aStrBevel(rtl::OUString::createFromAscii("bevel"));
1338 
1339                         if(aContent.match(aStrMiter))
1340                         {
1341                             setStrokeLinejoin(StrokeLinejoin_miter);
1342                         }
1343                         else if(aContent.match(aStrRound))
1344                         {
1345                             setStrokeLinejoin(StrokeLinejoin_round);
1346                         }
1347                         else if(aContent.match(aStrBevel))
1348                         {
1349                             setStrokeLinejoin(StrokeLinejoin_bevel);
1350                         }
1351                     }
1352                     break;
1353                 }
1354                 case SVGTokenStrokeMiterlimit:
1355                 {
1356                     SvgNumber aNum;
1357 
1358                     if(readSingleNumber(aContent, aNum))
1359                     {
1360                         if(aNum.isPositive())
1361                         {
1362                             setStrokeMiterLimit(aNum);
1363                         }
1364                     }
1365                     break;
1366                 }
1367                 case SVGTokenStrokeOpacity:
1368                 {
1369                     SvgNumber aNum;
1370 
1371                     if(readSingleNumber(aContent, aNum))
1372                     {
1373                         if(aNum.isPositive())
1374                         {
1375                             setStrokeOpacity(aNum);
1376                         }
1377                     }
1378                     break;
1379                 }
1380                 case SVGTokenStrokeWidth:
1381                 {
1382                     SvgNumber aNum;
1383 
1384                     if(readSingleNumber(aContent, aNum))
1385                     {
1386                         if(aNum.isPositive())
1387                         {
1388                             setStrokeWidth(aNum);
1389                         }
1390                     }
1391                     break;
1392                 }
1393                 case SVGTokenStopColor:
1394                 {
1395                     SvgPaint aSvgPaint;
1396                     rtl::OUString aURL;
1397 
1398                     if(readSvgPaint(aContent, aSvgPaint, aURL))
1399                     {
1400                         setStopColor(aSvgPaint);
1401                     }
1402                     break;
1403                 }
1404                 case SVGTokenStopOpacity:
1405                 {
1406                     SvgNumber aNum;
1407 
1408                     if(readSingleNumber(aContent, aNum))
1409                     {
1410                         if(aNum.isPositive())
1411                         {
1412                             setStopOpacity(aNum);
1413                         }
1414                     }
1415                     break;
1416                 }
1417                 case SVGTokenFont:
1418                 {
1419                     break;
1420                 }
1421                 case SVGTokenFontFamily:
1422                 {
1423                     SvgStringVector aSvgStringVector;
1424 
1425                     if(readSvgStringVector(aContent, aSvgStringVector))
1426                     {
1427                         setFontFamily(aSvgStringVector);
1428                     }
1429                     break;
1430                 }
1431                 case SVGTokenFontSize:
1432                 {
1433                     SvgNumber aNum;
1434 
1435                     if(readSingleNumber(aContent, aNum))
1436                     {
1437                         setFontSize(aNum);
1438                     }
1439                     break;
1440                 }
1441                 case SVGTokenFontSizeAdjust:
1442                 {
1443                     break;
1444                 }
1445                 case SVGTokenFontStretch:
1446                 {
1447                     if(aContent.getLength())
1448                     {
1449                         static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal"));
1450                         static rtl::OUString aStrWider(rtl::OUString::createFromAscii("wider"));
1451                         static rtl::OUString aStrNarrower(rtl::OUString::createFromAscii("narrower"));
1452                         static rtl::OUString aStrUltra_condensed(rtl::OUString::createFromAscii("ultra-condensed"));
1453                         static rtl::OUString aStrExtra_condensed(rtl::OUString::createFromAscii("extra-condensed"));
1454                         static rtl::OUString aStrCondensed(rtl::OUString::createFromAscii("condensed"));
1455                         static rtl::OUString aStrSemi_condensed(rtl::OUString::createFromAscii("semi-condensed"));
1456                         static rtl::OUString aStrSemi_expanded(rtl::OUString::createFromAscii("semi-expanded"));
1457                         static rtl::OUString aStrExpanded(rtl::OUString::createFromAscii("expanded"));
1458                         static rtl::OUString aStrExtra_expanded(rtl::OUString::createFromAscii("extra-expanded"));
1459                         static rtl::OUString aStrUltra_expanded(rtl::OUString::createFromAscii("ultra-expanded"));
1460 
1461                         if(aContent.match(aStrNormal))
1462                         {
1463                             setFontStretch(FontStretch_normal);
1464                         }
1465                         else if(aContent.match(aStrWider))
1466                         {
1467                             setFontStretch(FontStretch_wider);
1468                         }
1469                         else if(aContent.match(aStrNarrower))
1470                         {
1471                             setFontStretch(FontStretch_narrower);
1472                         }
1473                         else if(aContent.match(aStrUltra_condensed))
1474                         {
1475                             setFontStretch(FontStretch_ultra_condensed);
1476                         }
1477                         else if(aContent.match(aStrExtra_condensed))
1478                         {
1479                             setFontStretch(FontStretch_extra_condensed);
1480                         }
1481                         else if(aContent.match(aStrCondensed))
1482                         {
1483                             setFontStretch(FontStretch_condensed);
1484                         }
1485                         else if(aContent.match(aStrSemi_condensed))
1486                         {
1487                             setFontStretch(FontStretch_semi_condensed);
1488                         }
1489                         else if(aContent.match(aStrSemi_expanded))
1490                         {
1491                             setFontStretch(FontStretch_semi_expanded);
1492                         }
1493                         else if(aContent.match(aStrExpanded))
1494                         {
1495                             setFontStretch(FontStretch_expanded);
1496                         }
1497                         else if(aContent.match(aStrExtra_expanded))
1498                         {
1499                             setFontStretch(FontStretch_extra_expanded);
1500                         }
1501                         else if(aContent.match(aStrUltra_expanded))
1502                         {
1503                             setFontStretch(FontStretch_ultra_expanded);
1504                         }
1505                     }
1506                     break;
1507                 }
1508                 case SVGTokenFontStyle:
1509                 {
1510                     if(aContent.getLength())
1511                     {
1512                         static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal"));
1513                         static rtl::OUString aStrItalic(rtl::OUString::createFromAscii("italic"));
1514                         static rtl::OUString aStrOblique(rtl::OUString::createFromAscii("oblique"));
1515 
1516                         if(aContent.match(aStrNormal))
1517                         {
1518                             setFontStyle(FontStyle_normal);
1519                         }
1520                         else if(aContent.match(aStrItalic))
1521                         {
1522                             setFontStyle(FontStyle_italic);
1523                         }
1524                         else if(aContent.match(aStrOblique))
1525                         {
1526                             setFontStyle(FontStyle_oblique);
1527                         }
1528                     }
1529                     break;
1530                 }
1531                 case SVGTokenFontVariant:
1532                 {
1533                     if(aContent.getLength())
1534                     {
1535                         static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal"));
1536                         static rtl::OUString aStrSmallCaps(rtl::OUString::createFromAscii("small-caps"));
1537 
1538                         if(aContent.match(aStrNormal))
1539                         {
1540                             setFontVariant(FontVariant_normal);
1541                         }
1542                         else if(aContent.match(aStrSmallCaps))
1543                         {
1544                             setFontVariant(FontVariant_small_caps);
1545                         }
1546                     }
1547                     break;
1548                 }
1549                 case SVGTokenFontWeight:
1550                 {
1551                     if(aContent.getLength())
1552                     {
1553                         static rtl::OUString aStrNormal(rtl::OUString::createFromAscii("normal"));
1554                         static rtl::OUString aStrBold(rtl::OUString::createFromAscii("bold"));
1555                         static rtl::OUString aStrBolder(rtl::OUString::createFromAscii("bolder"));
1556                         static rtl::OUString aStrLighter(rtl::OUString::createFromAscii("lighter"));
1557                         static rtl::OUString aStr100(rtl::OUString::createFromAscii("100"));
1558                         static rtl::OUString aStr200(rtl::OUString::createFromAscii("200"));
1559                         static rtl::OUString aStr300(rtl::OUString::createFromAscii("300"));
1560                         static rtl::OUString aStr400(rtl::OUString::createFromAscii("400"));
1561                         static rtl::OUString aStr500(rtl::OUString::createFromAscii("500"));
1562                         static rtl::OUString aStr600(rtl::OUString::createFromAscii("600"));
1563                         static rtl::OUString aStr700(rtl::OUString::createFromAscii("700"));
1564                         static rtl::OUString aStr800(rtl::OUString::createFromAscii("800"));
1565                         static rtl::OUString aStr900(rtl::OUString::createFromAscii("900"));
1566 
1567                         if(aContent.match(aStr100))
1568                         {
1569                             setFontWeight(FontWeight_100);
1570                         }
1571                         else if(aContent.match(aStr200))
1572                         {
1573                             setFontWeight(FontWeight_200);
1574                         }
1575                         else if(aContent.match(aStr300))
1576                         {
1577                             setFontWeight(FontWeight_300);
1578                         }
1579                         else if(aContent.match(aStr400) || aContent.match(aStrNormal))
1580                         {
1581                             setFontWeight(FontWeight_400);
1582                         }
1583                         else if(aContent.match(aStr500))
1584                         {
1585                             setFontWeight(FontWeight_500);
1586                         }
1587                         else if(aContent.match(aStr600))
1588                         {
1589                             setFontWeight(FontWeight_600);
1590                         }
1591                         else if(aContent.match(aStr700) || aContent.match(aStrBold))
1592                         {
1593                             setFontWeight(FontWeight_700);
1594                         }
1595                         else if(aContent.match(aStr800))
1596                         {
1597                             setFontWeight(FontWeight_800);
1598                         }
1599                         else if(aContent.match(aStr900))
1600                         {
1601                             setFontWeight(FontWeight_900);
1602                         }
1603                         else if(aContent.match(aStrBolder))
1604                         {
1605                             setFontWeight(FontWeight_bolder);
1606                         }
1607                         else if(aContent.match(aStrLighter))
1608                         {
1609                             setFontWeight(FontWeight_lighter);
1610                         }
1611                     }
1612                     break;
1613                 }
1614                 case SVGTokenDirection:
1615                 {
1616                     break;
1617                 }
1618                 case SVGTokenLetterSpacing:
1619                 {
1620                     break;
1621                 }
1622                 case SVGTokenTextDecoration:
1623                 {
1624                     if(aContent.getLength())
1625                     {
1626                         static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
1627                         static rtl::OUString aStrUnderline(rtl::OUString::createFromAscii("underline"));
1628                         static rtl::OUString aStrOverline(rtl::OUString::createFromAscii("overline"));
1629                         static rtl::OUString aStrLineThrough(rtl::OUString::createFromAscii("line-through"));
1630                         static rtl::OUString aStrBlink(rtl::OUString::createFromAscii("blink"));
1631 
1632                         if(aContent.match(aStrNone))
1633                         {
1634                             setTextDecoration(TextDecoration_none);
1635                         }
1636                         else if(aContent.match(aStrUnderline))
1637                         {
1638                             setTextDecoration(TextDecoration_underline);
1639                         }
1640                         else if(aContent.match(aStrOverline))
1641                         {
1642                             setTextDecoration(TextDecoration_overline);
1643                         }
1644                         else if(aContent.match(aStrLineThrough))
1645                         {
1646                             setTextDecoration(TextDecoration_line_through);
1647                         }
1648                         else if(aContent.match(aStrBlink))
1649                         {
1650                             setTextDecoration(TextDecoration_blink);
1651                         }
1652                     }
1653                     break;
1654                 }
1655                 case SVGTokenUnicodeBidi:
1656                 {
1657                     break;
1658                 }
1659                 case SVGTokenWordSpacing:
1660                 {
1661                     break;
1662                 }
1663                 case SVGTokenTextAnchor:
1664                 {
1665                     if(aContent.getLength())
1666                     {
1667                         static rtl::OUString aStrStart(rtl::OUString::createFromAscii("start"));
1668                         static rtl::OUString aStrMiddle(rtl::OUString::createFromAscii("middle"));
1669                         static rtl::OUString aStrEnd(rtl::OUString::createFromAscii("end"));
1670 
1671                         if(aContent.match(aStrStart))
1672                         {
1673                             setTextAnchor(TextAnchor_start);
1674                         }
1675                         else if(aContent.match(aStrMiddle))
1676                         {
1677                             setTextAnchor(TextAnchor_middle);
1678                         }
1679                         else if(aContent.match(aStrEnd))
1680                         {
1681                             setTextAnchor(TextAnchor_end);
1682                         }
1683                     }
1684                     break;
1685                 }
1686                 case SVGTokenTextAlign:
1687                 {
1688                     if(aContent.getLength())
1689                     {
1690                         static rtl::OUString aStrLeft(rtl::OUString::createFromAscii("left"));
1691                         static rtl::OUString aStrRight(rtl::OUString::createFromAscii("right"));
1692                         static rtl::OUString aStrCenter(rtl::OUString::createFromAscii("center"));
1693                         static rtl::OUString aStrJustify(rtl::OUString::createFromAscii("justify"));
1694 
1695                         if(aContent.match(aStrLeft))
1696                         {
1697                             setTextAlign(TextAlign_left);
1698                         }
1699                         else if(aContent.match(aStrRight))
1700                         {
1701                             setTextAlign(TextAlign_right);
1702                         }
1703                         else if(aContent.match(aStrCenter))
1704                         {
1705                             setTextAlign(TextAlign_center);
1706                         }
1707                         else if(aContent.match(aStrJustify))
1708                         {
1709                             setTextAlign(TextAlign_justify);
1710                         }
1711                     }
1712                     break;
1713                 }
1714                 case SVGTokenColor:
1715                 {
1716                     SvgPaint aSvgPaint;
1717                     rtl::OUString aURL;
1718 
1719                     if(readSvgPaint(aContent, aSvgPaint, aURL))
1720                     {
1721                         setColor(aSvgPaint);
1722                     }
1723                     break;
1724                 }
1725                 case SVGTokenOpacity:
1726                 {
1727                     SvgNumber aNum;
1728 
1729                     if(readSingleNumber(aContent, aNum))
1730                     {
1731                         setOpacity(SvgNumber(basegfx::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet()));
1732                     }
1733                     break;
1734                 }
1735                 case SVGTokenTitle:
1736                 {
1737                     setTitle(aContent);
1738                     break;
1739                 }
1740                 case SVGTokenDesc:
1741                 {
1742                     setDesc(aContent);
1743                     break;
1744                 }
1745                 case SVGTokenClipPathProperty:
1746                 {
1747                     readLocalUrl(aContent, maClipPathXLink);
1748                     break;
1749                 }
1750                 case SVGTokenMask:
1751                 {
1752                     readLocalUrl(aContent, maMaskXLink);
1753                     break;
1754                 }
1755                 case SVGTokenClipRule:
1756                 {
1757                     if(aContent.getLength())
1758                     {
1759                         if(aContent.match(commonStrings::aStrNonzero))
1760                         {
1761                             maClipRule = FillRule_nonzero;
1762                         }
1763                         else if(aContent.match(commonStrings::aStrEvenOdd))
1764                         {
1765                             maClipRule = FillRule_evenodd;
1766                         }
1767                     }
1768                     break;
1769                 }
1770                 case SVGTokenMarker:
1771                 {
1772                     readLocalUrl(aContent, maMarkerEndXLink);
1773                     maMarkerStartXLink = maMarkerMidXLink = maMarkerEndXLink;
1774                     break;
1775                 }
1776                 case SVGTokenMarkerStart:
1777                 {
1778                     readLocalUrl(aContent, maMarkerStartXLink);
1779                     break;
1780                 }
1781                 case SVGTokenMarkerMid:
1782                 {
1783                     readLocalUrl(aContent, maMarkerMidXLink);
1784                     break;
1785                 }
1786                 case SVGTokenMarkerEnd:
1787                 {
1788                     readLocalUrl(aContent, maMarkerEndXLink);
1789                     break;
1790                 }
1791                 default:
1792                 {
1793                     break;
1794                 }
1795             }
1796         }
1797 
1798         const basegfx::BColor* SvgStyleAttributes::getFill() const
1799         {
1800             if(mbIsClipPathContent)
1801             {
1802                 static basegfx::BColor aBlack(0.0, 0.0, 0.0);
1803 
1804                 return &aBlack;
1805             }
1806             else if(maFill.isSet())
1807             {
1808                 if(maFill.isCurrent())
1809                 {
1810                     return getColor();
1811                 }
1812                 else if(maFill.isOn())
1813                 {
1814                     return &maFill.getBColor();
1815                 }
1816             }
1817             else
1818             {
1819                 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1820 
1821                 if(pSvgStyleAttributes)
1822                 {
1823                     return pSvgStyleAttributes->getFill();
1824                 }
1825             }
1826 
1827             return 0;
1828         }
1829 
1830         const basegfx::BColor* SvgStyleAttributes::getStroke() const
1831         {
1832             if(mbIsClipPathContent)
1833             {
1834                 return 0;
1835             }
1836             else if(maStroke.isSet())
1837             {
1838                 if(maStroke.isCurrent())
1839                 {
1840                     return getColor();
1841                 }
1842                 else if(maStroke.isOn())
1843                 {
1844                     return &maStroke.getBColor();
1845                 }
1846             }
1847             else
1848             {
1849                 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1850 
1851                 if(pSvgStyleAttributes)
1852                 {
1853                     return pSvgStyleAttributes->getStroke();
1854                 }
1855             }
1856 
1857             return 0;
1858         }
1859 
1860         const basegfx::BColor& SvgStyleAttributes::getStopColor() const
1861         {
1862             if(maStopColor.isCurrent())
1863             {
1864                 return *getColor();
1865             }
1866             else
1867             {
1868                 return maStopColor.getBColor();
1869             }
1870         }
1871 
1872         const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeFill() const
1873         {
1874             if(mbIsClipPathContent)
1875             {
1876                 return 0;
1877             }
1878             else if(mpSvgGradientNodeFill)
1879             {
1880                 return mpSvgGradientNodeFill;
1881             }
1882             else
1883             {
1884                 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1885 
1886                 if(pSvgStyleAttributes)
1887                 {
1888                     return pSvgStyleAttributes->getSvgGradientNodeFill();
1889                 }
1890             }
1891 
1892             return 0;
1893         }
1894 
1895         const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeStroke() const
1896         {
1897             if(mbIsClipPathContent)
1898             {
1899                 return 0;
1900             }
1901             else if(mpSvgGradientNodeStroke)
1902             {
1903                 return mpSvgGradientNodeStroke;
1904             }
1905             else
1906             {
1907                 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1908 
1909                 if(pSvgStyleAttributes)
1910                 {
1911                     return pSvgStyleAttributes->getSvgGradientNodeStroke();
1912                 }
1913             }
1914 
1915             return 0;
1916         }
1917 
1918         const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeFill() const
1919         {
1920             if(mbIsClipPathContent)
1921             {
1922                 return 0;
1923             }
1924             else if(mpSvgPatternNodeFill)
1925             {
1926                 return mpSvgPatternNodeFill;
1927             }
1928             else
1929             {
1930                 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1931 
1932                 if(pSvgStyleAttributes)
1933                 {
1934                     return pSvgStyleAttributes->getSvgPatternNodeFill();
1935                 }
1936             }
1937 
1938             return 0;
1939         }
1940 
1941         const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeStroke() const
1942         {
1943             if(mbIsClipPathContent)
1944             {
1945                 return 0;
1946             }
1947             else if(mpSvgPatternNodeStroke)
1948             {
1949                 return mpSvgPatternNodeStroke;
1950             }
1951             else
1952             {
1953                 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1954 
1955                 if(pSvgStyleAttributes)
1956                 {
1957                     return pSvgStyleAttributes->getSvgPatternNodeStroke();
1958                 }
1959             }
1960 
1961             return 0;
1962         }
1963 
1964         SvgNumber SvgStyleAttributes::getStrokeWidth() const
1965         {
1966             if(mbIsClipPathContent)
1967             {
1968                 return SvgNumber(0.0);
1969             }
1970             else if(maStrokeWidth.isSet())
1971             {
1972                 return maStrokeWidth;
1973             }
1974 
1975             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1976 
1977             if(pSvgStyleAttributes)
1978             {
1979                 return pSvgStyleAttributes->getStrokeWidth();
1980             }
1981 
1982             // default is 1
1983             return SvgNumber(1.0);
1984         }
1985 
1986         SvgNumber SvgStyleAttributes::getStopOpacity() const
1987         {
1988             if(maStopOpacity.isSet())
1989             {
1990                 return maStopOpacity;
1991             }
1992 
1993             // default is 1
1994             return SvgNumber(1.0);
1995         }
1996 
1997         SvgNumber SvgStyleAttributes::getFillOpacity() const
1998         {
1999             if(mbIsClipPathContent)
2000             {
2001                 return SvgNumber(1.0);
2002             }
2003             else if(maFillOpacity.isSet())
2004             {
2005                 return maFillOpacity;
2006             }
2007 
2008             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2009 
2010             if(pSvgStyleAttributes)
2011             {
2012                 return pSvgStyleAttributes->getFillOpacity();
2013             }
2014 
2015             // default is 1
2016             return SvgNumber(1.0);
2017         }
2018 
2019         FillRule SvgStyleAttributes::getFillRule() const
2020         {
2021             if(FillRule_notset != maFillRule)
2022             {
2023                 return maFillRule;
2024             }
2025 
2026             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2027 
2028             if(pSvgStyleAttributes)
2029             {
2030                 return pSvgStyleAttributes->getFillRule();
2031             }
2032 
2033             // default is NonZero
2034             return FillRule_nonzero;
2035         }
2036 
2037         const SvgNumberVector& SvgStyleAttributes::getStrokeDasharray() const
2038         {
2039             if(!maStrokeDasharray.empty())
2040             {
2041                 return maStrokeDasharray;
2042             }
2043             else if(getStrokeDasharraySet())
2044             {
2045                 // #121221# is set to empty *by purpose*, do not visit parent styles
2046                 return maStrokeDasharray;
2047             }
2048 
2049             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2050 
2051             if(pSvgStyleAttributes)
2052             {
2053                 return pSvgStyleAttributes->getStrokeDasharray();
2054             }
2055 
2056             // default empty
2057             return maStrokeDasharray;
2058         }
2059 
2060         SvgNumber SvgStyleAttributes::getStrokeDashOffset() const
2061         {
2062             if(maStrokeDashOffset.isSet())
2063             {
2064                 return maStrokeDashOffset;
2065             }
2066 
2067             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2068 
2069             if(pSvgStyleAttributes)
2070             {
2071                 return pSvgStyleAttributes->getStrokeDashOffset();
2072             }
2073 
2074             // default is 0
2075             return SvgNumber(0.0);
2076         }
2077 
2078         StrokeLinecap SvgStyleAttributes::getStrokeLinecap() const
2079         {
2080             if(maStrokeLinecap != StrokeLinecap_notset)
2081             {
2082                 return maStrokeLinecap;
2083             }
2084 
2085             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2086 
2087             if(pSvgStyleAttributes)
2088             {
2089                 return pSvgStyleAttributes->getStrokeLinecap();
2090             }
2091 
2092             // default is StrokeLinecap_butt
2093             return StrokeLinecap_butt;
2094         }
2095 
2096         StrokeLinejoin SvgStyleAttributes::getStrokeLinejoin() const
2097         {
2098             if(maStrokeLinejoin != StrokeLinejoin_notset)
2099             {
2100                 return maStrokeLinejoin;
2101             }
2102 
2103             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2104 
2105             if(pSvgStyleAttributes)
2106             {
2107                 return pSvgStyleAttributes->getStrokeLinejoin();
2108             }
2109 
2110             // default is StrokeLinejoin_butt
2111             return StrokeLinejoin_miter;
2112         }
2113 
2114         SvgNumber SvgStyleAttributes::getStrokeMiterLimit() const
2115         {
2116             if(maStrokeMiterLimit.isSet())
2117             {
2118                 return maStrokeMiterLimit;
2119             }
2120 
2121             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2122 
2123             if(pSvgStyleAttributes)
2124             {
2125                 return pSvgStyleAttributes->getStrokeMiterLimit();
2126             }
2127 
2128             // default is 4
2129             return SvgNumber(4.0);
2130         }
2131 
2132         SvgNumber SvgStyleAttributes::getStrokeOpacity() const
2133         {
2134             if(maStrokeOpacity.isSet())
2135             {
2136                 return maStrokeOpacity;
2137             }
2138 
2139             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2140 
2141             if(pSvgStyleAttributes)
2142             {
2143                 return pSvgStyleAttributes->getStrokeOpacity();
2144             }
2145 
2146             // default is 1
2147             return SvgNumber(1.0);
2148         }
2149 
2150         const SvgStringVector& SvgStyleAttributes::getFontFamily() const
2151         {
2152             if(!maFontFamily.empty())
2153             {
2154                 return maFontFamily;
2155             }
2156 
2157             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2158 
2159             if(pSvgStyleAttributes)
2160             {
2161                 return pSvgStyleAttributes->getFontFamily();
2162             }
2163 
2164             // default is empty
2165             return maFontFamily;
2166         }
2167 
2168         SvgNumber SvgStyleAttributes::getFontSize() const
2169         {
2170             if(maFontSize.isSet())
2171             {
2172                 return maFontSize;
2173             }
2174 
2175             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2176 
2177             if(pSvgStyleAttributes)
2178             {
2179                 return pSvgStyleAttributes->getFontSize();
2180             }
2181 
2182             // default is 'medium'
2183             return SvgNumber(12.0);
2184         }
2185 
2186         FontStretch SvgStyleAttributes::getFontStretch() const
2187         {
2188             if(maFontStretch != FontStretch_notset)
2189             {
2190                 if(FontStretch_wider != maFontStretch && FontStretch_narrower != maFontStretch)
2191                 {
2192                     return maFontStretch;
2193                 }
2194             }
2195 
2196             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2197 
2198             if(pSvgStyleAttributes)
2199             {
2200                 FontStretch aInherited = pSvgStyleAttributes->getFontStretch();
2201 
2202                 if(FontStretch_wider == maFontStretch)
2203                 {
2204                     aInherited = getWider(aInherited);
2205                 }
2206                 else if(FontStretch_narrower == maFontStretch)
2207                 {
2208                     aInherited = getNarrower(aInherited);
2209                 }
2210 
2211                 return aInherited;
2212             }
2213 
2214             // default is FontStretch_normal
2215             return FontStretch_normal;
2216         }
2217 
2218         FontStyle SvgStyleAttributes::getFontStyle() const
2219         {
2220             if(maFontStyle != FontStyle_notset)
2221             {
2222                 return maFontStyle;
2223             }
2224 
2225             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2226 
2227             if(pSvgStyleAttributes)
2228             {
2229                 return pSvgStyleAttributes->getFontStyle();
2230             }
2231 
2232             // default is FontStyle_normal
2233             return FontStyle_normal;
2234         }
2235 
2236         FontWeight SvgStyleAttributes::getFontWeight() const
2237         {
2238             if(maFontWeight != FontWeight_notset)
2239             {
2240                 if(FontWeight_bolder != maFontWeight && FontWeight_lighter != maFontWeight)
2241                 {
2242                     return maFontWeight;
2243                 }
2244             }
2245 
2246             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2247 
2248             if(pSvgStyleAttributes)
2249             {
2250                 FontWeight aInherited = pSvgStyleAttributes->getFontWeight();
2251 
2252                 if(FontWeight_bolder == maFontWeight)
2253                 {
2254                     aInherited = getBolder(aInherited);
2255                 }
2256                 else if(FontWeight_lighter == maFontWeight)
2257                 {
2258                     aInherited = getLighter(aInherited);
2259                 }
2260 
2261                 return aInherited;
2262             }
2263 
2264             // default is FontWeight_400 (FontWeight_normal)
2265             return FontWeight_400;
2266         }
2267 
2268         TextAlign SvgStyleAttributes::getTextAlign() const
2269         {
2270             if(maTextAlign != TextAlign_notset)
2271             {
2272                 return maTextAlign;
2273             }
2274 
2275             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2276 
2277             if(pSvgStyleAttributes)
2278             {
2279                 return pSvgStyleAttributes->getTextAlign();
2280             }
2281 
2282             // default is TextAlign_left
2283             return TextAlign_left;
2284         }
2285 
2286         const SvgStyleAttributes* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const
2287         {
2288             if(maTextDecoration != TextDecoration_notset)
2289             {
2290                 return this;
2291             }
2292 
2293             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2294 
2295             if(pSvgStyleAttributes)
2296             {
2297                 return pSvgStyleAttributes->getTextDecorationDefiningSvgStyleAttributes();
2298             }
2299 
2300             // default is 0
2301             return 0;
2302         }
2303 
2304         TextDecoration SvgStyleAttributes::getTextDecoration() const
2305         {
2306             const SvgStyleAttributes* pDefining = getTextDecorationDefiningSvgStyleAttributes();
2307 
2308             if(pDefining)
2309             {
2310                 return pDefining->maTextDecoration;
2311             }
2312             else
2313             {
2314                 // default is TextDecoration_none
2315                 return TextDecoration_none;
2316             }
2317         }
2318 
2319         TextAnchor SvgStyleAttributes::getTextAnchor() const
2320         {
2321             if(maTextAnchor != TextAnchor_notset)
2322             {
2323                 return maTextAnchor;
2324             }
2325 
2326             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2327 
2328             if(pSvgStyleAttributes)
2329             {
2330                 return pSvgStyleAttributes->getTextAnchor();
2331             }
2332 
2333             // default is TextAnchor_start
2334             return TextAnchor_start;
2335         }
2336 
2337         const basegfx::BColor* SvgStyleAttributes::getColor() const
2338         {
2339             if(maColor.isSet())
2340             {
2341                 if(maColor.isCurrent())
2342                 {
2343                     OSL_ENSURE(false, "Svg error: current color uses current color (!)");
2344                     return 0;
2345                 }
2346                 else if(maColor.isOn())
2347                 {
2348                     return &maColor.getBColor();
2349                 }
2350             }
2351             else
2352             {
2353                 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2354 
2355                 if(pSvgStyleAttributes)
2356                 {
2357                     return pSvgStyleAttributes->getColor();
2358                 }
2359             }
2360 
2361             return 0;
2362         }
2363 
2364         rtl::OUString SvgStyleAttributes::getMarkerStartXLink() const
2365         {
2366             if(maMarkerStartXLink.getLength())
2367             {
2368                 return maMarkerStartXLink;
2369             }
2370 
2371             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2372 
2373             if(pSvgStyleAttributes)
2374             {
2375                 return pSvgStyleAttributes->getMarkerStartXLink();
2376             }
2377 
2378             return rtl::OUString();
2379         }
2380 
2381         const SvgMarkerNode* SvgStyleAttributes::accessMarkerStartXLink() const
2382         {
2383             if(!mpMarkerStartXLink)
2384             {
2385                 const rtl::OUString aMarker(getMarkerStartXLink());
2386 
2387                 if(aMarker.getLength())
2388                 {
2389                     const_cast< SvgStyleAttributes* >(this)->mpMarkerStartXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerStartXLink()));
2390                 }
2391             }
2392 
2393             return mpMarkerStartXLink;
2394         }
2395 
2396         rtl::OUString SvgStyleAttributes::getMarkerMidXLink() const
2397         {
2398             if(maMarkerMidXLink.getLength())
2399             {
2400                 return maMarkerMidXLink;
2401             }
2402 
2403             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2404 
2405             if(pSvgStyleAttributes)
2406             {
2407                 return pSvgStyleAttributes->getMarkerMidXLink();
2408             }
2409 
2410             return rtl::OUString();
2411         }
2412 
2413         const SvgMarkerNode* SvgStyleAttributes::accessMarkerMidXLink() const
2414         {
2415             if(!mpMarkerMidXLink)
2416             {
2417                 const rtl::OUString aMarker(getMarkerMidXLink());
2418 
2419                 if(aMarker.getLength())
2420                 {
2421                     const_cast< SvgStyleAttributes* >(this)->mpMarkerMidXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerMidXLink()));
2422                 }
2423             }
2424 
2425             return mpMarkerMidXLink;
2426         }
2427 
2428         rtl::OUString SvgStyleAttributes::getMarkerEndXLink() const
2429         {
2430             if(maMarkerEndXLink.getLength())
2431             {
2432                 return maMarkerEndXLink;
2433             }
2434 
2435             const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2436 
2437             if(pSvgStyleAttributes)
2438             {
2439                 return pSvgStyleAttributes->getMarkerEndXLink();
2440             }
2441 
2442             return rtl::OUString();
2443         }
2444 
2445         const SvgMarkerNode* SvgStyleAttributes::accessMarkerEndXLink() const
2446         {
2447             if(!mpMarkerEndXLink)
2448             {
2449                 const rtl::OUString aMarker(getMarkerEndXLink());
2450 
2451                 if(aMarker.getLength())
2452                 {
2453                     const_cast< SvgStyleAttributes* >(this)->mpMarkerEndXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerEndXLink()));
2454                 }
2455             }
2456 
2457             return mpMarkerEndXLink;
2458         }
2459 
2460     } // end of namespace svgreader
2461 } // end of namespace svgio
2462 
2463 //////////////////////////////////////////////////////////////////////////////
2464 // eof
2465