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