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