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