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