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