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