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