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