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