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