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