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