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