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