1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svgio/svgreader/svgstyleattributes.hxx>
21 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
22 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
23 #include <svgio/svgreader/svgnode.hxx>
24 #include <svgio/svgreader/svgdocument.hxx>
25 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
26 #include <svgio/svgreader/svggradientnode.hxx>
27 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
28 #include <basegfx/vector/b2enums.hxx>
29 #include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
30 #include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
31 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
32 #include <svgio/svgreader/svgclippathnode.hxx>
33 #include <svgio/svgreader/svgmasknode.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <svgio/svgreader/svgmarkernode.hxx>
36 #include <basegfx/curve/b2dcubicbezier.hxx>
37 #include <svgio/svgreader/svgpatternnode.hxx>
38 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
39 #include <basegfx/polygon/b2dpolygontools.hxx>
40 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
42 //////////////////////////////////////////////////////////////////////////////
48 basegfx::B2DLineJoin
StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin
)
50 if(StrokeLinejoin_round
== aStrokeLinejoin
)
52 return basegfx::B2DLINEJOIN_ROUND
;
54 else if(StrokeLinejoin_bevel
== aStrokeLinejoin
)
56 return basegfx::B2DLINEJOIN_BEVEL
;
59 return basegfx::B2DLINEJOIN_MITER
;
62 com::sun::star::drawing::LineCap
StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap
)
64 switch(aStrokeLinecap
)
66 default: /* StrokeLinecap_notset, StrokeLinecap_butt */
68 return com::sun::star::drawing::LineCap_BUTT
;
70 case StrokeLinecap_round
:
72 return com::sun::star::drawing::LineCap_ROUND
;
74 case StrokeLinecap_square
:
76 return com::sun::star::drawing::LineCap_SQUARE
;
81 FontStretch
getWider(FontStretch aSource
)
85 case FontStretch_ultra_condensed
: aSource
= FontStretch_extra_condensed
; break;
86 case FontStretch_extra_condensed
: aSource
= FontStretch_condensed
; break;
87 case FontStretch_condensed
: aSource
= FontStretch_semi_condensed
; break;
88 case FontStretch_semi_condensed
: aSource
= FontStretch_normal
; break;
89 case FontStretch_normal
: aSource
= FontStretch_semi_expanded
; break;
90 case FontStretch_semi_expanded
: aSource
= FontStretch_expanded
; break;
91 case FontStretch_expanded
: aSource
= FontStretch_extra_expanded
; break;
92 case FontStretch_extra_expanded
: aSource
= FontStretch_ultra_expanded
; break;
99 FontStretch
getNarrower(FontStretch aSource
)
103 case FontStretch_extra_condensed
: aSource
= FontStretch_ultra_condensed
; break;
104 case FontStretch_condensed
: aSource
= FontStretch_extra_condensed
; break;
105 case FontStretch_semi_condensed
: aSource
= FontStretch_condensed
; break;
106 case FontStretch_normal
: aSource
= FontStretch_semi_condensed
; break;
107 case FontStretch_semi_expanded
: aSource
= FontStretch_normal
; break;
108 case FontStretch_expanded
: aSource
= FontStretch_semi_expanded
; break;
109 case FontStretch_extra_expanded
: aSource
= FontStretch_expanded
; break;
110 case FontStretch_ultra_expanded
: aSource
= FontStretch_extra_expanded
; break;
117 FontWeight
getBolder(FontWeight aSource
)
121 case FontWeight_100
: aSource
= FontWeight_200
; break;
122 case FontWeight_200
: aSource
= FontWeight_300
; break;
123 case FontWeight_300
: aSource
= FontWeight_400
; break;
124 case FontWeight_400
: aSource
= FontWeight_500
; break;
125 case FontWeight_500
: aSource
= FontWeight_600
; break;
126 case FontWeight_600
: aSource
= FontWeight_700
; break;
127 case FontWeight_700
: aSource
= FontWeight_800
; break;
128 case FontWeight_800
: aSource
= FontWeight_900
; break;
135 FontWeight
getLighter(FontWeight aSource
)
139 case FontWeight_200
: aSource
= FontWeight_100
; break;
140 case FontWeight_300
: aSource
= FontWeight_200
; break;
141 case FontWeight_400
: aSource
= FontWeight_300
; break;
142 case FontWeight_500
: aSource
= FontWeight_400
; break;
143 case FontWeight_600
: aSource
= FontWeight_500
; break;
144 case FontWeight_700
: aSource
= FontWeight_600
; break;
145 case FontWeight_800
: aSource
= FontWeight_700
; break;
146 case FontWeight_900
: aSource
= FontWeight_800
; break;
153 ::FontWeight
getVclFontWeight(FontWeight aSource
)
155 ::FontWeight
nRetval(WEIGHT_NORMAL
);
159 case FontWeight_100
: nRetval
= WEIGHT_ULTRALIGHT
; break;
160 case FontWeight_200
: nRetval
= WEIGHT_LIGHT
; break;
161 case FontWeight_300
: nRetval
= WEIGHT_SEMILIGHT
; break;
162 case FontWeight_400
: nRetval
= WEIGHT_NORMAL
; break;
163 case FontWeight_500
: nRetval
= WEIGHT_MEDIUM
; break;
164 case FontWeight_600
: nRetval
= WEIGHT_SEMIBOLD
; break;
165 case FontWeight_700
: nRetval
= WEIGHT_BOLD
; break;
166 case FontWeight_800
: nRetval
= WEIGHT_ULTRABOLD
; break;
167 case FontWeight_900
: nRetval
= WEIGHT_BLACK
; break;
174 void SvgStyleAttributes::readStyle(const OUString
& rCandidate
)
176 const sal_Int32
nLen(rCandidate
.getLength());
181 const sal_Int32
nInitPos(nPos
);
182 skip_char(rCandidate
, sal_Unicode(' '), nPos
, nLen
);
183 OUStringBuffer aTokenName
;
184 copyString(rCandidate
, nPos
, aTokenName
, nLen
);
186 if(aTokenName
.getLength())
188 skip_char(rCandidate
, sal_Unicode(' '), sal_Unicode(':'), nPos
, nLen
);
189 OUStringBuffer aTokenValue
;
190 copyToLimiter(rCandidate
, sal_Unicode(';'), nPos
, aTokenValue
, nLen
);
191 skip_char(rCandidate
, sal_Unicode(' '), sal_Unicode(';'), nPos
, nLen
);
192 const OUString
aOUTokenName(aTokenName
.makeStringAndClear());
193 const OUString
aOUTokenValue(aTokenValue
.makeStringAndClear());
195 parseStyleAttribute(aOUTokenName
, StrToSVGToken(aOUTokenName
), aOUTokenValue
);
200 OSL_ENSURE(false, "Could not interpret on current position (!)");
206 const SvgStyleAttributes
* SvgStyleAttributes::getParentStyle() const
208 if(getCssStyleParent())
210 return getCssStyleParent();
213 if(mrOwner
.getParent())
215 return mrOwner
.getParent()->getSvgStyleAttributes();
221 void SvgStyleAttributes::add_text(
222 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
223 drawinglayer::primitive2d::Primitive2DSequence
& rSource
) const
225 if(rSource
.hasElements())
227 // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D
228 // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill())
229 // set. When another fill is used and also evtl. stroke is set it gets necessary to
230 // dismantle to geometry and add needed primitives
231 const basegfx::BColor
* pFill
= getFill();
232 const SvgGradientNode
* pFillGradient
= getSvgGradientNodeFill();
233 const SvgPatternNode
* pFillPattern
= getSvgPatternNodeFill();
234 const basegfx::BColor
* pStroke
= getStroke();
235 const SvgGradientNode
* pStrokeGradient
= getSvgGradientNodeStroke();
236 const SvgPatternNode
* pStrokePattern
= getSvgPatternNodeStroke();
237 basegfx::B2DPolyPolygon aMergedArea
;
239 if(pFillGradient
|| pFillPattern
|| pStroke
|| pStrokeGradient
|| pStrokePattern
)
241 // text geometry is needed, create
242 // use neutral ViewInformation and create LineGeometryExtractor2D
243 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
244 drawinglayer::processor2d::TextAsPolygonExtractor2D
aExtractor(aViewInformation2D
);
247 aExtractor
.process(rSource
);
250 const drawinglayer::processor2d::TextAsPolygonDataNodeVector
& rResult
= aExtractor
.getTarget();
251 const sal_uInt32
nResultCount(rResult
.size());
252 basegfx::B2DPolyPolygonVector aTextFillVector
;
253 aTextFillVector
.reserve(nResultCount
);
255 for(sal_uInt32
a(0); a
< nResultCount
; a
++)
257 const drawinglayer::processor2d::TextAsPolygonDataNode
& rCandidate
= rResult
[a
];
259 if(rCandidate
.getIsFilled())
261 aTextFillVector
.push_back(rCandidate
.getB2DPolyPolygon());
265 if(!aTextFillVector
.empty())
267 aMergedArea
= basegfx::tools::mergeToSinglePolyPolygon(aTextFillVector
);
271 const bool bStrokeUsed(pStroke
|| pStrokeGradient
|| pStrokePattern
);
273 // add fill. Use geometry even for simple color fill when stroke
274 // is used, else text rendering and the geometry-based stroke will
275 // normally not really match optically due to divrese system text
277 if(aMergedArea
.count() && (pFillGradient
|| pFillPattern
|| bStrokeUsed
))
279 // create text fill content based on geometry
280 add_fill(aMergedArea
, rTarget
, aMergedArea
.getB2DRange());
284 // add the already prepared primitives for single color fill
285 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, rSource
);
289 if(aMergedArea
.count() && bStrokeUsed
)
291 // create text stroke content
292 add_stroke(aMergedArea
, rTarget
, aMergedArea
.getB2DRange());
297 void SvgStyleAttributes::add_fillGradient(
298 const basegfx::B2DPolyPolygon
& rPath
,
299 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
300 const SvgGradientNode
& rFillGradient
,
301 const basegfx::B2DRange
& rGeoRange
) const
303 // create fill content
304 drawinglayer::primitive2d::SvgGradientEntryVector aSvgGradientEntryVector
;
306 // get the color stops
307 rFillGradient
.collectGradientEntries(aSvgGradientEntryVector
);
309 if(!aSvgGradientEntryVector
.empty())
311 basegfx::B2DHomMatrix aGeoToUnit
;
313 if(rFillGradient
.getGradientTransform())
315 aGeoToUnit
= *rFillGradient
.getGradientTransform();
318 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
320 aGeoToUnit
.translate(-rGeoRange
.getMinX(), -rGeoRange
.getMinY());
321 aGeoToUnit
.scale(1.0 / rGeoRange
.getWidth(), 1.0 / rGeoRange
.getHeight());
324 if(SVGTokenLinearGradient
== rFillGradient
.getType())
326 basegfx::B2DPoint
aStart(0.0, 0.0);
327 basegfx::B2DPoint
aEnd(1.0, 0.0);
329 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
331 // all possible units
332 aStart
.setX(rFillGradient
.getX1().solve(mrOwner
, xcoordinate
));
333 aStart
.setY(rFillGradient
.getY1().solve(mrOwner
, ycoordinate
));
334 aEnd
.setX(rFillGradient
.getX2().solve(mrOwner
, xcoordinate
));
335 aEnd
.setY(rFillGradient
.getY2().solve(mrOwner
, ycoordinate
));
339 // fractions or percent relative to object bounds
340 const SvgNumber
X1(rFillGradient
.getX1());
341 const SvgNumber
Y1(rFillGradient
.getY1());
342 const SvgNumber
X2(rFillGradient
.getX2());
343 const SvgNumber
Y2(rFillGradient
.getY2());
345 aStart
.setX(Unit_percent
== X1
.getUnit() ? X1
.getNumber() * 0.01 : X1
.getNumber());
346 aStart
.setY(Unit_percent
== Y1
.getUnit() ? Y1
.getNumber() * 0.01 : Y1
.getNumber());
347 aEnd
.setX(Unit_percent
== X2
.getUnit() ? X2
.getNumber() * 0.01 : X2
.getNumber());
348 aEnd
.setY(Unit_percent
== Y2
.getUnit() ? Y2
.getNumber() * 0.01 : Y2
.getNumber());
351 if(!aGeoToUnit
.isIdentity())
353 aStart
*= aGeoToUnit
;
357 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
359 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
361 aSvgGradientEntryVector
,
364 userSpaceOnUse
!= rFillGradient
.getGradientUnits(),
365 rFillGradient
.getSpreadMethod()));
369 basegfx::B2DPoint
aStart(0.5, 0.5);
370 basegfx::B2DPoint aFocal
;
372 const SvgNumber
* pFx
= rFillGradient
.getFx();
373 const SvgNumber
* pFy
= rFillGradient
.getFy();
374 const bool bFocal(pFx
|| pFy
);
376 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
378 // all possible units
379 aStart
.setX(rFillGradient
.getCx().solve(mrOwner
, xcoordinate
));
380 aStart
.setY(rFillGradient
.getCy().solve(mrOwner
, ycoordinate
));
381 fRadius
= rFillGradient
.getR().solve(mrOwner
, length
);
385 aFocal
.setX(pFx
? pFx
->solve(mrOwner
, xcoordinate
) : aStart
.getX());
386 aFocal
.setY(pFy
? pFy
->solve(mrOwner
, ycoordinate
) : aStart
.getY());
391 // fractions or percent relative to object bounds
392 const SvgNumber
Cx(rFillGradient
.getCx());
393 const SvgNumber
Cy(rFillGradient
.getCy());
394 const SvgNumber
R(rFillGradient
.getR());
396 aStart
.setX(Unit_percent
== Cx
.getUnit() ? Cx
.getNumber() * 0.01 : Cx
.getNumber());
397 aStart
.setY(Unit_percent
== Cy
.getUnit() ? Cy
.getNumber() * 0.01 : Cy
.getNumber());
398 fRadius
= (Unit_percent
== R
.getUnit()) ? R
.getNumber() * 0.01 : R
.getNumber();
402 aFocal
.setX(pFx
? (Unit_percent
== pFx
->getUnit() ? pFx
->getNumber() * 0.01 : pFx
->getNumber()) : aStart
.getX());
403 aFocal
.setY(pFy
? (Unit_percent
== pFy
->getUnit() ? pFy
->getNumber() * 0.01 : pFy
->getNumber()) : aStart
.getY());
407 if(!aGeoToUnit
.isIdentity())
409 aStart
*= aGeoToUnit
;
410 fRadius
= (aGeoToUnit
* basegfx::B2DVector(fRadius
, 0.0)).getLength();
414 aFocal
*= aGeoToUnit
;
418 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
420 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
422 aSvgGradientEntryVector
,
425 userSpaceOnUse
!= rFillGradient
.getGradientUnits(),
426 rFillGradient
.getSpreadMethod(),
427 bFocal
? &aFocal
: 0));
432 void SvgStyleAttributes::add_fillPatternTransform(
433 const basegfx::B2DPolyPolygon
& rPath
,
434 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
435 const SvgPatternNode
& rFillPattern
,
436 const basegfx::B2DRange
& rGeoRange
) const
438 // prepare fill polyPolygon with given pattern, check for patternTransform
439 if(rFillPattern
.getPatternTransform() && !rFillPattern
.getPatternTransform()->isIdentity())
441 // PatternTransform is active; Handle by filling the inverse transformed
442 // path and back-transforming the result
443 basegfx::B2DPolyPolygon
aPath(rPath
);
444 basegfx::B2DHomMatrix
aInv(*rFillPattern
.getPatternTransform());
445 drawinglayer::primitive2d::Primitive2DSequence aNewTarget
;
448 aPath
.transform(aInv
);
449 add_fillPattern(aPath
, aNewTarget
, rFillPattern
, aPath
.getB2DRange());
451 if(aNewTarget
.hasElements())
453 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
455 new drawinglayer::primitive2d::TransformPrimitive2D(
456 *rFillPattern
.getPatternTransform(),
462 // no patternTransform, create fillPattern directly
463 add_fillPattern(rPath
, rTarget
, rFillPattern
, rGeoRange
);
467 void SvgStyleAttributes::add_fillPattern(
468 const basegfx::B2DPolyPolygon
& rPath
,
469 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
470 const SvgPatternNode
& rFillPattern
,
471 const basegfx::B2DRange
& rGeoRange
) const
473 // fill polyPolygon with given pattern
474 const drawinglayer::primitive2d::Primitive2DSequence
& rPrimitives
= rFillPattern
.getPatternPrimitives();
476 if(rPrimitives
.hasElements())
478 double fTargetWidth(rGeoRange
.getWidth());
479 double fTargetHeight(rGeoRange
.getHeight());
481 if(fTargetWidth
> 0.0 && fTargetHeight
> 0.0)
483 // get relative values from pattern
489 rFillPattern
.getValuesRelative(fX
, fY
, fW
, fH
, rGeoRange
, mrOwner
);
491 if(fW
> 0.0 && fH
> 0.0)
493 // build the reference range relative to the rGeoRange
494 const basegfx::B2DRange
aReferenceRange(fX
, fY
, fX
+ fW
, fY
+ fH
);
496 // find out how the content is mapped to the reference range
497 basegfx::B2DHomMatrix aMapPrimitivesToUnitRange
;
498 const basegfx::B2DRange
* pViewBox
= rFillPattern
.getViewBox();
502 // use viewBox/preserveAspectRatio
503 const SvgAspectRatio
& rRatio
= rFillPattern
.getSvgAspectRatio();
504 const basegfx::B2DRange
aUnitRange(0.0, 0.0, 1.0, 1.0);
508 // let mapping be created from SvgAspectRatio
509 aMapPrimitivesToUnitRange
= rRatio
.createMapping(aUnitRange
, *pViewBox
);
513 // choose default mapping
514 aMapPrimitivesToUnitRange
= rRatio
.createLinearMapping(aUnitRange
, *pViewBox
);
519 // use patternContentUnits
520 const SvgUnits
aPatternContentUnits(rFillPattern
.getPatternContentUnits() ? *rFillPattern
.getPatternContentUnits() : userSpaceOnUse
);
522 if(userSpaceOnUse
== aPatternContentUnits
)
524 // create relative mapping to unit coordinates
525 aMapPrimitivesToUnitRange
.scale(1.0 / (fW
* fTargetWidth
), 1.0 / (fH
* fTargetHeight
));
529 aMapPrimitivesToUnitRange
.scale(1.0 / fW
, 1.0 / fH
);
533 // apply aMapPrimitivesToUnitRange to content when used
534 drawinglayer::primitive2d::Primitive2DSequence
aPrimitives(rPrimitives
);
536 if(!aMapPrimitivesToUnitRange
.isIdentity())
538 const drawinglayer::primitive2d::Primitive2DReference
xRef(
539 new drawinglayer::primitive2d::TransformPrimitive2D(
540 aMapPrimitivesToUnitRange
,
543 aPrimitives
= drawinglayer::primitive2d::Primitive2DSequence(&xRef
, 1);
546 // embed in PatternFillPrimitive2D
547 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
549 new drawinglayer::primitive2d::PatternFillPrimitive2D(
558 void SvgStyleAttributes::add_fill(
559 const basegfx::B2DPolyPolygon
& rPath
,
560 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
561 const basegfx::B2DRange
& rGeoRange
) const
563 const basegfx::BColor
* pFill
= getFill();
564 const SvgGradientNode
* pFillGradient
= getSvgGradientNodeFill();
565 const SvgPatternNode
* pFillPattern
= getSvgPatternNodeFill();
567 if(pFill
|| pFillGradient
|| pFillPattern
)
569 const double fFillOpacity(getFillOpacity().solve(mrOwner
, length
));
571 if(basegfx::fTools::more(fFillOpacity
, 0.0))
573 drawinglayer::primitive2d::Primitive2DSequence aNewFill
;
577 // create fill content with SVG gradient primitive
578 add_fillGradient(rPath
, aNewFill
, *pFillGradient
, rGeoRange
);
580 else if(pFillPattern
)
582 // create fill content with SVG pattern primitive
583 add_fillPatternTransform(rPath
, aNewFill
, *pFillPattern
, rGeoRange
);
587 // create fill content
589 aNewFill
[0] = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
594 if(aNewFill
.hasElements())
596 if(basegfx::fTools::less(fFillOpacity
, 1.0))
598 // embed in UnifiedTransparencePrimitive2D
599 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
601 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
603 1.0 - fFillOpacity
));
608 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, aNewFill
);
615 void SvgStyleAttributes::add_stroke(
616 const basegfx::B2DPolyPolygon
& rPath
,
617 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
618 const basegfx::B2DRange
& rGeoRange
) const
620 const basegfx::BColor
* pStroke
= getStroke();
621 const SvgGradientNode
* pStrokeGradient
= getSvgGradientNodeStroke();
622 const SvgPatternNode
* pStrokePattern
= getSvgPatternNodeStroke();
624 if(pStroke
|| pStrokeGradient
|| pStrokePattern
)
626 drawinglayer::primitive2d::Primitive2DSequence aNewStroke
;
627 const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner
, length
));
629 if(basegfx::fTools::more(fStrokeOpacity
, 0.0))
631 // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all
632 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner
, length
) : 1.0);
634 if(basegfx::fTools::more(fStrokeWidth
, 0.0))
636 // get LineJoin, LineCap and stroke array
637 const basegfx::B2DLineJoin
aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin()));
638 const com::sun::star::drawing::LineCap
aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap()));
639 ::std::vector
< double > aDashArray
;
641 if(!getStrokeDasharray().empty())
643 aDashArray
= solveSvgNumberVector(getStrokeDasharray(), mrOwner
, length
);
646 // todo: Handle getStrokeDashOffset()
648 // prepare line attribute
649 drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive
;
650 const drawinglayer::attribute::LineAttribute
aLineAttribute(
651 pStroke
? *pStroke
: basegfx::BColor(0.0, 0.0, 0.0),
656 if(aDashArray
.empty())
658 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
664 const drawinglayer::attribute::StrokeAttribute
aStrokeAttribute(aDashArray
);
666 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
672 if(pStrokeGradient
|| pStrokePattern
)
674 // put primitive into Primitive2DReference and Primitive2DSequence
675 const drawinglayer::primitive2d::Primitive2DSequence
aSeq(&aNewLinePrimitive
, 1);
677 // use neutral ViewInformation and create LineGeometryExtractor2D
678 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
679 drawinglayer::processor2d::LineGeometryExtractor2D
aExtractor(aViewInformation2D
);
682 aExtractor
.process(aSeq
);
684 // check for fill rsults
685 const basegfx::B2DPolyPolygonVector
& rLineFillVector(aExtractor
.getExtractedLineFills());
687 if(!rLineFillVector
.empty())
689 const basegfx::B2DPolyPolygon
aMergedArea(
690 basegfx::tools::mergeToSinglePolyPolygon(
693 if(aMergedArea
.count())
697 // create fill content with SVG gradient primitive. Use original GeoRange,
698 // e.g. from circle without LineWidth
699 add_fillGradient(aMergedArea
, aNewStroke
, *pStrokeGradient
, rGeoRange
);
701 else // if(pStrokePattern)
703 // create fill content with SVG pattern primitive. Use GeoRange
704 // from the expanded data, e.g. circle with extended geo by half linewidth
705 add_fillPatternTransform(aMergedArea
, aNewStroke
, *pStrokePattern
, aMergedArea
.getB2DRange());
712 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aNewStroke
, aNewLinePrimitive
);
715 if(aNewStroke
.hasElements())
717 if(basegfx::fTools::less(fStrokeOpacity
, 1.0))
719 // embed in UnifiedTransparencePrimitive2D
720 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
722 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
724 1.0 - fStrokeOpacity
));
729 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, aNewStroke
);
737 double get_markerRotation(
738 const SvgMarkerNode
& rMarker
,
739 const basegfx::B2DPolygon
& rPolygon
,
740 const sal_uInt32 nIndex
)
743 const sal_uInt32
nPointCount(rPolygon
.count());
747 if(rMarker
.getOrientAuto())
749 const bool bPrev(rPolygon
.isClosed() || nIndex
> 0);
750 basegfx::B2DCubicBezier aSegment
;
751 basegfx::B2DVector aPrev
;
752 basegfx::B2DVector aNext
;
756 rPolygon
.getBezierSegment((nIndex
- 1) % nPointCount
, aSegment
);
757 aPrev
= aSegment
.getTangent(1.0);
760 const bool bNext(rPolygon
.isClosed() || nIndex
+ 1 < nPointCount
);
764 rPolygon
.getBezierSegment(nIndex
% nPointCount
, aSegment
);
765 aNext
= aSegment
.getTangent(0.0);
770 fAngle
= atan2(aPrev
.getY() + aNext
.getY(), aPrev
.getX() + aNext
.getX());
774 fAngle
= atan2(aPrev
.getY(), aPrev
.getX());
778 fAngle
= atan2(aNext
.getY(), aNext
.getX());
783 fAngle
= rMarker
.getAngle();
790 bool SvgStyleAttributes::prepare_singleMarker(
791 drawinglayer::primitive2d::Primitive2DSequence
& rMarkerPrimitives
,
792 basegfx::B2DHomMatrix
& rMarkerTransform
,
793 basegfx::B2DRange
& rClipRange
,
794 const SvgMarkerNode
& rMarker
) const
796 // reset return values
797 rMarkerTransform
.identity();
800 // get marker primitive representation
801 rMarkerPrimitives
= rMarker
.getMarkerPrimitives();
803 if(rMarkerPrimitives
.hasElements())
805 basegfx::B2DRange
aPrimitiveRange(0.0, 0.0, 1.0, 1.0);
806 const basegfx::B2DRange
* pViewBox
= rMarker
.getViewBox();
810 aPrimitiveRange
= *pViewBox
;
813 if(aPrimitiveRange
.getWidth() > 0.0 && aPrimitiveRange
.getHeight() > 0.0)
815 double fTargetWidth(rMarker
.getMarkerWidth().isSet() ? rMarker
.getMarkerWidth().solve(mrOwner
, xcoordinate
) : 3.0);
816 double fTargetHeight(rMarker
.getMarkerHeight().isSet() ? rMarker
.getMarkerHeight().solve(mrOwner
, xcoordinate
) : 3.0);
817 const bool bStrokeWidth(SvgMarkerNode::strokeWidth
== rMarker
.getMarkerUnits());
818 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner
, length
) : 1.0);
822 // relative to strokeWidth
823 fTargetWidth
*= fStrokeWidth
;
824 fTargetHeight
*= fStrokeWidth
;
827 if(fTargetWidth
> 0.0 && fTargetHeight
> 0.0)
830 const basegfx::B2DRange
aTargetRange(0.0, 0.0, fTargetWidth
, fTargetHeight
);
831 const SvgAspectRatio
& rRatio
= rMarker
.getSvgAspectRatio();
835 // let mapping be created from SvgAspectRatio
836 rMarkerTransform
= rRatio
.createMapping(aTargetRange
, aPrimitiveRange
);
838 if(rRatio
.isMeetOrSlice())
841 rClipRange
= aPrimitiveRange
;
850 // adapt to strokewidth if needed
851 rMarkerTransform
.scale(fStrokeWidth
, fStrokeWidth
);
856 // choose default mapping
857 rMarkerTransform
= rRatio
.createLinearMapping(aTargetRange
, aPrimitiveRange
);
861 // get and apply reference point. Initially it's in marker local coordinate system
862 basegfx::B2DPoint
aRefPoint(
863 rMarker
.getRefX().isSet() ? rMarker
.getRefX().solve(mrOwner
, xcoordinate
) : 0.0,
864 rMarker
.getRefY().isSet() ? rMarker
.getRefY().solve(mrOwner
, ycoordinate
) : 0.0);
866 // apply MarkerTransform to have it in mapped coordinates
867 aRefPoint
*= rMarkerTransform
;
869 // apply by moving RepPoint to (0.0)
870 rMarkerTransform
.translate(-aRefPoint
.getX(), -aRefPoint
.getY());
880 void SvgStyleAttributes::add_singleMarker(
881 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
882 const drawinglayer::primitive2d::Primitive2DSequence
& rMarkerPrimitives
,
883 const basegfx::B2DHomMatrix
& rMarkerTransform
,
884 const basegfx::B2DRange
& rClipRange
,
885 const SvgMarkerNode
& rMarker
,
886 const basegfx::B2DPolygon
& rCandidate
,
887 const sal_uInt32 nIndex
) const
889 const sal_uInt32
nPointCount(rCandidate
.count());
893 // get and apply rotation
894 basegfx::B2DHomMatrix
aCombinedTransform(rMarkerTransform
);
895 aCombinedTransform
.rotate(get_markerRotation(rMarker
, rCandidate
, nIndex
));
897 // get and apply target position
898 const basegfx::B2DPoint
aPoint(rCandidate
.getB2DPoint(nIndex
% nPointCount
));
899 aCombinedTransform
.translate(aPoint
.getX(), aPoint
.getY());
902 drawinglayer::primitive2d::Primitive2DReference
xMarker(
903 new drawinglayer::primitive2d::TransformPrimitive2D(
907 if(!rClipRange
.isEmpty())
909 // marker needs to be clipped, it's bigger as the mapping
910 basegfx::B2DPolyPolygon
aClipPolygon(basegfx::tools::createPolygonFromRect(rClipRange
));
912 aClipPolygon
.transform(aCombinedTransform
);
913 xMarker
= new drawinglayer::primitive2d::MaskPrimitive2D(
915 drawinglayer::primitive2d::Primitive2DSequence(&xMarker
, 1));
919 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget
, xMarker
);
923 void SvgStyleAttributes::add_markers(
924 const basegfx::B2DPolyPolygon
& rPath
,
925 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
) const
927 // try to access linked markers
928 const SvgMarkerNode
* pStart
= accessMarkerStartXLink();
929 const SvgMarkerNode
* pMid
= accessMarkerMidXLink();
930 const SvgMarkerNode
* pEnd
= accessMarkerEndXLink();
932 if(pStart
|| pMid
|| pEnd
)
934 const sal_uInt32
nCount(rPath
.count());
936 for (sal_uInt32
a(0); a
< nCount
; a
++)
938 const basegfx::B2DPolygon
aCandidate(rPath
.getB2DPolygon(a
));
939 const sal_uInt32
nPointCount(aCandidate
.count());
943 const sal_uInt32
nMarkerCount(aCandidate
.isClosed() ? nPointCount
+ 1 : nPointCount
);
944 drawinglayer::primitive2d::Primitive2DSequence aMarkerPrimitives
;
945 basegfx::B2DHomMatrix aMarkerTransform
;
946 basegfx::B2DRange aClipRange
;
947 const SvgMarkerNode
* pPrepared
= 0;
951 if(prepare_singleMarker(aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pStart
))
954 add_singleMarker(rTarget
, aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pPrepared
, aCandidate
, 0);
958 if(pMid
&& nMarkerCount
> 2)
960 if(pMid
== pPrepared
|| prepare_singleMarker(aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pMid
))
964 for(sal_uInt32
b(1); b
< nMarkerCount
- 1; b
++)
966 add_singleMarker(rTarget
, aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pPrepared
, aCandidate
, b
);
973 if(pEnd
== pPrepared
|| prepare_singleMarker(aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pEnd
))
976 add_singleMarker(rTarget
, aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pPrepared
, aCandidate
, nMarkerCount
- 1);
984 void SvgStyleAttributes::add_path(
985 const basegfx::B2DPolyPolygon
& rPath
,
986 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
) const
988 const bool bIsLine(1 == rPath
.count()
989 && !rPath
.areControlPointsUsed()
990 && 2 == rPath
.getB2DPolygon(0).count());
997 const basegfx::B2DRange
aGeoRange(rPath
.getB2DRange());
999 if(aGeoRange
.isEmpty())
1004 if(!bIsLine
&& // not for lines
1005 (basegfx::fTools::equalZero(aGeoRange
.getWidth())
1006 || basegfx::fTools::equalZero(aGeoRange
.getHeight())))
1011 const double fOpacity(getOpacity().getNumber());
1013 if(basegfx::fTools::equalZero(fOpacity
))
1020 basegfx::B2DPolyPolygon
aPath(rPath
);
1021 const bool bNeedToCheckClipRule(SVGTokenPath
== mrOwner
.getType() || SVGTokenPolygon
== mrOwner
.getType());
1022 const bool bClipPathIsNonzero(!bIsLine
&& bNeedToCheckClipRule
&& mbIsClipPathContent
&& FillRule_nonzero
== maClipRule
);
1023 const bool bFillRuleIsNonzero(!bIsLine
&& bNeedToCheckClipRule
&& !mbIsClipPathContent
&& FillRule_nonzero
== getFillRule());
1025 if(bClipPathIsNonzero
|| bFillRuleIsNonzero
)
1027 // nonzero is wanted, solve geometrically (see description on basegfx)
1028 aPath
= basegfx::tools::createNonzeroConform(aPath
);
1031 add_fill(aPath
, rTarget
, aGeoRange
);
1034 add_stroke(rPath
, rTarget
, aGeoRange
);
1036 // Svg supports markers for path, polygon, polyline and line
1037 if(SVGTokenPath
== mrOwner
.getType() || // path
1038 SVGTokenPolygon
== mrOwner
.getType() || // polygon, polyline
1039 SVGTokenLine
== mrOwner
.getType()) // line
1041 // try to add markers
1042 add_markers(rPath
, rTarget
);
1046 void SvgStyleAttributes::add_postProcess(
1047 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
1048 const drawinglayer::primitive2d::Primitive2DSequence
& rSource
,
1049 const basegfx::B2DHomMatrix
* pTransform
) const
1051 if(rSource
.hasElements())
1053 const double fOpacity(getOpacity().getNumber());
1055 if(basegfx::fTools::equalZero(fOpacity
))
1060 drawinglayer::primitive2d::Primitive2DSequence
aSource(rSource
);
1062 if(basegfx::fTools::less(fOpacity
, 1.0))
1064 // embed in UnifiedTransparencePrimitive2D
1065 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1066 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1070 aSource
= drawinglayer::primitive2d::Primitive2DSequence(&xRef
, 1);
1073 if(getClipPathXLink().getLength())
1075 // try to access linked ClipPath
1076 const SvgClipPathNode
* mpClip
= dynamic_cast< const SvgClipPathNode
* >(mrOwner
.getDocument().findSvgNodeById(getClipPathXLink()));
1080 mpClip
->apply(aSource
);
1084 if(aSource
.hasElements()) // test again, applied clipPath may have lead to empty geometry
1086 if(getMaskXLink().getLength())
1088 // try to access linked Mask
1089 const SvgMaskNode
* mpMask
= dynamic_cast< const SvgMaskNode
* >(mrOwner
.getDocument().findSvgNodeById(getMaskXLink()));
1093 mpMask
->apply(aSource
);
1097 if(aSource
.hasElements()) // test again, applied mask may have lead to empty geometry
1101 // create embedding group element with transformation
1102 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1103 new drawinglayer::primitive2d::TransformPrimitive2D(
1107 aSource
= drawinglayer::primitive2d::Primitive2DSequence(&xRef
, 1);
1110 // append to current target
1111 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, aSource
);
1117 SvgStyleAttributes::SvgStyleAttributes(SvgNode
& rOwner
)
1119 mpCssStyleParent(0),
1122 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
1125 mpSvgGradientNodeFill(0),
1126 mpSvgGradientNodeStroke(0),
1127 mpSvgPatternNodeFill(0),
1128 mpSvgPatternNodeStroke(0),
1130 maStrokeDasharray(),
1131 maStrokeDashOffset(),
1132 maStrokeLinecap(StrokeLinecap_notset
),
1133 maStrokeLinejoin(StrokeLinejoin_notset
),
1134 maStrokeMiterLimit(),
1138 maFontStretch(FontStretch_notset
),
1139 maFontStyle(FontStyle_notset
),
1140 maFontVariant(FontVariant_notset
),
1141 maFontWeight(FontWeight_notset
),
1142 maTextAlign(TextAlign_notset
),
1143 maTextDecoration(TextDecoration_notset
),
1144 maTextAnchor(TextAnchor_notset
),
1151 maMarkerStartXLink(),
1152 mpMarkerStartXLink(0),
1154 mpMarkerMidXLink(0),
1156 mpMarkerEndXLink(0),
1157 maFillRule(FillRule_notset
),
1158 maClipRule(FillRule_nonzero
),
1159 mbIsClipPathContent(SVGTokenClipPathNode
== mrOwner
.getType()),
1160 mbStrokeDasharraySet(false)
1162 if(!mbIsClipPathContent
)
1164 const SvgStyleAttributes
* pParentStyle
= getParentStyle();
1168 mbIsClipPathContent
= pParentStyle
->mbIsClipPathContent
;
1173 SvgStyleAttributes::~SvgStyleAttributes()
1177 void SvgStyleAttributes::parseStyleAttribute(const OUString
& /*rTokenName*/, SVGToken aSVGToken
, const OUString
& aContent
)
1186 if(readSvgPaint(aContent
, aSvgPaint
, aURL
))
1190 else if(aURL
.getLength())
1192 const SvgNode
* pNode
= mrOwner
.getDocument().findSvgNodeById(aURL
);
1196 if(SVGTokenLinearGradient
== pNode
->getType() || SVGTokenRadialGradient
== pNode
->getType())
1198 setSvgGradientNodeFill(static_cast< const SvgGradientNode
* >(pNode
));
1200 else if(SVGTokenPattern
== pNode
->getType())
1202 setSvgPatternNodeFill(static_cast< const SvgPatternNode
* >(pNode
));
1208 case SVGTokenFillOpacity
:
1212 if(readSingleNumber(aContent
, aNum
))
1214 if(aNum
.isPositive())
1216 setFillOpacity(aNum
);
1221 case SVGTokenFillRule
:
1223 if(aContent
.getLength())
1225 if(aContent
.match(commonStrings::aStrNonzero
))
1227 maFillRule
= FillRule_nonzero
;
1229 else if(aContent
.match(commonStrings::aStrEvenOdd
))
1231 maFillRule
= FillRule_evenodd
;
1236 case SVGTokenStroke
:
1241 if(readSvgPaint(aContent
, aSvgPaint
, aURL
))
1243 setStroke(aSvgPaint
);
1245 else if(aURL
.getLength())
1247 const SvgNode
* pNode
= mrOwner
.getDocument().findSvgNodeById(aURL
);
1251 if(SVGTokenLinearGradient
== pNode
->getType() || SVGTokenRadialGradient
== pNode
->getType())
1253 setSvgGradientNodeStroke(static_cast< const SvgGradientNode
* >(pNode
));
1255 else if(SVGTokenPattern
== pNode
->getType())
1257 setSvgPatternNodeStroke(static_cast< const SvgPatternNode
* >(pNode
));
1263 case SVGTokenStrokeDasharray
:
1265 if(aContent
.getLength())
1267 static rtl::OUString
aStrNone(rtl::OUString::createFromAscii("none"));
1268 SvgNumberVector aVector
;
1270 if(aContent
.match(aStrNone
))
1272 // #121221# The special value 'none' needs to be handled
1273 // in the sense that *when* it is set, the parent shall not
1274 // be used. Before this was only dependent on the array being
1276 setStrokeDasharraySet(true);
1278 else if(readSvgNumberVector(aContent
, aVector
))
1280 setStrokeDasharray(aVector
);
1285 case SVGTokenStrokeDashoffset
:
1289 if(readSingleNumber(aContent
, aNum
))
1291 if(aNum
.isPositive())
1293 setStrokeDashOffset(aNum
);
1298 case SVGTokenStrokeLinecap
:
1300 if(aContent
.getLength())
1302 static OUString
aStrButt(OUString::createFromAscii("butt"));
1303 static OUString
aStrRound(OUString::createFromAscii("round"));
1304 static OUString
aStrSquare(OUString::createFromAscii("square"));
1306 if(aContent
.match(aStrButt
))
1308 setStrokeLinecap(StrokeLinecap_butt
);
1310 else if(aContent
.match(aStrRound
))
1312 setStrokeLinecap(StrokeLinecap_round
);
1314 else if(aContent
.match(aStrSquare
))
1316 setStrokeLinecap(StrokeLinecap_square
);
1321 case SVGTokenStrokeLinejoin
:
1323 if(aContent
.getLength())
1325 static OUString
aStrMiter(OUString::createFromAscii("miter"));
1326 static OUString
aStrRound(OUString::createFromAscii("round"));
1327 static OUString
aStrBevel(OUString::createFromAscii("bevel"));
1329 if(aContent
.match(aStrMiter
))
1331 setStrokeLinejoin(StrokeLinejoin_miter
);
1333 else if(aContent
.match(aStrRound
))
1335 setStrokeLinejoin(StrokeLinejoin_round
);
1337 else if(aContent
.match(aStrBevel
))
1339 setStrokeLinejoin(StrokeLinejoin_bevel
);
1344 case SVGTokenStrokeMiterlimit
:
1348 if(readSingleNumber(aContent
, aNum
))
1350 if(aNum
.isPositive())
1352 setStrokeMiterLimit(aNum
);
1357 case SVGTokenStrokeOpacity
:
1361 if(readSingleNumber(aContent
, aNum
))
1363 if(aNum
.isPositive())
1365 setStrokeOpacity(aNum
);
1370 case SVGTokenStrokeWidth
:
1374 if(readSingleNumber(aContent
, aNum
))
1376 if(aNum
.isPositive())
1378 setStrokeWidth(aNum
);
1383 case SVGTokenStopColor
:
1388 if(readSvgPaint(aContent
, aSvgPaint
, aURL
))
1390 setStopColor(aSvgPaint
);
1394 case SVGTokenStopOpacity
:
1398 if(readSingleNumber(aContent
, aNum
))
1400 if(aNum
.isPositive())
1402 setStopOpacity(aNum
);
1411 case SVGTokenFontFamily
:
1413 SvgStringVector aSvgStringVector
;
1415 if(readSvgStringVector(aContent
, aSvgStringVector
))
1417 setFontFamily(aSvgStringVector
);
1421 case SVGTokenFontSize
:
1425 if(readSingleNumber(aContent
, aNum
))
1431 case SVGTokenFontSizeAdjust
:
1435 case SVGTokenFontStretch
:
1437 if(aContent
.getLength())
1439 static OUString
aStrNormal(OUString::createFromAscii("normal"));
1440 static OUString
aStrWider(OUString::createFromAscii("wider"));
1441 static OUString
aStrNarrower(OUString::createFromAscii("narrower"));
1442 static OUString
aStrUltra_condensed(OUString::createFromAscii("ultra-condensed"));
1443 static OUString
aStrExtra_condensed(OUString::createFromAscii("extra-condensed"));
1444 static OUString
aStrCondensed(OUString::createFromAscii("condensed"));
1445 static OUString
aStrSemi_condensed(OUString::createFromAscii("semi-condensed"));
1446 static OUString
aStrSemi_expanded(OUString::createFromAscii("semi-expanded"));
1447 static OUString
aStrExpanded(OUString::createFromAscii("expanded"));
1448 static OUString
aStrExtra_expanded(OUString::createFromAscii("extra-expanded"));
1449 static OUString
aStrUltra_expanded(OUString::createFromAscii("ultra-expanded"));
1451 if(aContent
.match(aStrNormal
))
1453 setFontStretch(FontStretch_normal
);
1455 else if(aContent
.match(aStrWider
))
1457 setFontStretch(FontStretch_wider
);
1459 else if(aContent
.match(aStrNarrower
))
1461 setFontStretch(FontStretch_narrower
);
1463 else if(aContent
.match(aStrUltra_condensed
))
1465 setFontStretch(FontStretch_ultra_condensed
);
1467 else if(aContent
.match(aStrExtra_condensed
))
1469 setFontStretch(FontStretch_extra_condensed
);
1471 else if(aContent
.match(aStrCondensed
))
1473 setFontStretch(FontStretch_condensed
);
1475 else if(aContent
.match(aStrSemi_condensed
))
1477 setFontStretch(FontStretch_semi_condensed
);
1479 else if(aContent
.match(aStrSemi_expanded
))
1481 setFontStretch(FontStretch_semi_expanded
);
1483 else if(aContent
.match(aStrExpanded
))
1485 setFontStretch(FontStretch_expanded
);
1487 else if(aContent
.match(aStrExtra_expanded
))
1489 setFontStretch(FontStretch_extra_expanded
);
1491 else if(aContent
.match(aStrUltra_expanded
))
1493 setFontStretch(FontStretch_ultra_expanded
);
1498 case SVGTokenFontStyle
:
1500 if(aContent
.getLength())
1502 static OUString
aStrNormal(OUString::createFromAscii("normal"));
1503 static OUString
aStrItalic(OUString::createFromAscii("italic"));
1504 static OUString
aStrOblique(OUString::createFromAscii("oblique"));
1506 if(aContent
.match(aStrNormal
))
1508 setFontStyle(FontStyle_normal
);
1510 else if(aContent
.match(aStrItalic
))
1512 setFontStyle(FontStyle_italic
);
1514 else if(aContent
.match(aStrOblique
))
1516 setFontStyle(FontStyle_oblique
);
1521 case SVGTokenFontVariant
:
1523 if(aContent
.getLength())
1525 static OUString
aStrNormal(OUString::createFromAscii("normal"));
1526 static OUString
aStrSmallCaps(OUString::createFromAscii("small-caps"));
1528 if(aContent
.match(aStrNormal
))
1530 setFontVariant(FontVariant_normal
);
1532 else if(aContent
.match(aStrSmallCaps
))
1534 setFontVariant(FontVariant_small_caps
);
1539 case SVGTokenFontWeight
:
1541 if(aContent
.getLength())
1543 static OUString
aStrNormal(OUString::createFromAscii("normal"));
1544 static OUString
aStrBold(OUString::createFromAscii("bold"));
1545 static OUString
aStrBolder(OUString::createFromAscii("bolder"));
1546 static OUString
aStrLighter(OUString::createFromAscii("lighter"));
1547 static OUString
aStr100(OUString::createFromAscii("100"));
1548 static OUString
aStr200(OUString::createFromAscii("200"));
1549 static OUString
aStr300(OUString::createFromAscii("300"));
1550 static OUString
aStr400(OUString::createFromAscii("400"));
1551 static OUString
aStr500(OUString::createFromAscii("500"));
1552 static OUString
aStr600(OUString::createFromAscii("600"));
1553 static OUString
aStr700(OUString::createFromAscii("700"));
1554 static OUString
aStr800(OUString::createFromAscii("800"));
1555 static OUString
aStr900(OUString::createFromAscii("900"));
1557 if(aContent
.match(aStr100
))
1559 setFontWeight(FontWeight_100
);
1561 else if(aContent
.match(aStr200
))
1563 setFontWeight(FontWeight_200
);
1565 else if(aContent
.match(aStr300
))
1567 setFontWeight(FontWeight_300
);
1569 else if(aContent
.match(aStr400
) || aContent
.match(aStrNormal
))
1571 setFontWeight(FontWeight_400
);
1573 else if(aContent
.match(aStr500
))
1575 setFontWeight(FontWeight_500
);
1577 else if(aContent
.match(aStr600
))
1579 setFontWeight(FontWeight_600
);
1581 else if(aContent
.match(aStr700
) || aContent
.match(aStrBold
))
1583 setFontWeight(FontWeight_700
);
1585 else if(aContent
.match(aStr800
))
1587 setFontWeight(FontWeight_800
);
1589 else if(aContent
.match(aStr900
))
1591 setFontWeight(FontWeight_900
);
1593 else if(aContent
.match(aStrBolder
))
1595 setFontWeight(FontWeight_bolder
);
1597 else if(aContent
.match(aStrLighter
))
1599 setFontWeight(FontWeight_lighter
);
1604 case SVGTokenDirection
:
1608 case SVGTokenLetterSpacing
:
1612 case SVGTokenTextDecoration
:
1614 if(aContent
.getLength())
1616 static OUString
aStrNone(OUString::createFromAscii("none"));
1617 static OUString
aStrUnderline(OUString::createFromAscii("underline"));
1618 static OUString
aStrOverline(OUString::createFromAscii("overline"));
1619 static OUString
aStrLineThrough(OUString::createFromAscii("line-through"));
1620 static OUString
aStrBlink(OUString::createFromAscii("blink"));
1622 if(aContent
.match(aStrNone
))
1624 setTextDecoration(TextDecoration_none
);
1626 else if(aContent
.match(aStrUnderline
))
1628 setTextDecoration(TextDecoration_underline
);
1630 else if(aContent
.match(aStrOverline
))
1632 setTextDecoration(TextDecoration_overline
);
1634 else if(aContent
.match(aStrLineThrough
))
1636 setTextDecoration(TextDecoration_line_through
);
1638 else if(aContent
.match(aStrBlink
))
1640 setTextDecoration(TextDecoration_blink
);
1645 case SVGTokenUnicodeBidi
:
1649 case SVGTokenWordSpacing
:
1653 case SVGTokenTextAnchor
:
1655 if(aContent
.getLength())
1657 static OUString
aStrStart(OUString::createFromAscii("start"));
1658 static OUString
aStrMiddle(OUString::createFromAscii("middle"));
1659 static OUString
aStrEnd(OUString::createFromAscii("end"));
1661 if(aContent
.match(aStrStart
))
1663 setTextAnchor(TextAnchor_start
);
1665 else if(aContent
.match(aStrMiddle
))
1667 setTextAnchor(TextAnchor_middle
);
1669 else if(aContent
.match(aStrEnd
))
1671 setTextAnchor(TextAnchor_end
);
1676 case SVGTokenTextAlign
:
1678 if(aContent
.getLength())
1680 static OUString
aStrLeft(OUString::createFromAscii("left"));
1681 static OUString
aStrRight(OUString::createFromAscii("right"));
1682 static OUString
aStrCenter(OUString::createFromAscii("center"));
1683 static OUString
aStrJustify(OUString::createFromAscii("justify"));
1685 if(aContent
.match(aStrLeft
))
1687 setTextAlign(TextAlign_left
);
1689 else if(aContent
.match(aStrRight
))
1691 setTextAlign(TextAlign_right
);
1693 else if(aContent
.match(aStrCenter
))
1695 setTextAlign(TextAlign_center
);
1697 else if(aContent
.match(aStrJustify
))
1699 setTextAlign(TextAlign_justify
);
1709 if(readSvgPaint(aContent
, aSvgPaint
, aURL
))
1711 setColor(aSvgPaint
);
1715 case SVGTokenOpacity
:
1719 if(readSingleNumber(aContent
, aNum
))
1721 setOpacity(SvgNumber(basegfx::clamp(aNum
.getNumber(), 0.0, 1.0), aNum
.getUnit(), aNum
.isSet()));
1735 case SVGTokenClipPathProperty
:
1737 readLocalUrl(aContent
, maClipPathXLink
);
1742 readLocalUrl(aContent
, maMaskXLink
);
1745 case SVGTokenClipRule
:
1747 if(aContent
.getLength())
1749 if(aContent
.match(commonStrings::aStrNonzero
))
1751 maClipRule
= FillRule_nonzero
;
1753 else if(aContent
.match(commonStrings::aStrEvenOdd
))
1755 maClipRule
= FillRule_evenodd
;
1760 case SVGTokenMarker
:
1762 readLocalUrl(aContent
, maMarkerEndXLink
);
1763 maMarkerStartXLink
= maMarkerMidXLink
= maMarkerEndXLink
;
1766 case SVGTokenMarkerStart
:
1768 readLocalUrl(aContent
, maMarkerStartXLink
);
1771 case SVGTokenMarkerMid
:
1773 readLocalUrl(aContent
, maMarkerMidXLink
);
1776 case SVGTokenMarkerEnd
:
1778 readLocalUrl(aContent
, maMarkerEndXLink
);
1788 const basegfx::BColor
* SvgStyleAttributes::getFill() const
1790 if(mbIsClipPathContent
)
1792 static basegfx::BColor
aBlack(0.0, 0.0, 0.0);
1796 else if(maFill
.isSet())
1798 if(maFill
.isCurrent())
1802 else if(maFill
.isOn())
1804 return &maFill
.getBColor();
1809 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1811 if(pSvgStyleAttributes
)
1813 return pSvgStyleAttributes
->getFill();
1820 const basegfx::BColor
* SvgStyleAttributes::getStroke() const
1822 if(mbIsClipPathContent
)
1826 else if(maStroke
.isSet())
1828 if(maStroke
.isCurrent())
1832 else if(maStroke
.isOn())
1834 return &maStroke
.getBColor();
1839 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1841 if(pSvgStyleAttributes
)
1843 return pSvgStyleAttributes
->getStroke();
1850 const basegfx::BColor
& SvgStyleAttributes::getStopColor() const
1852 if(maStopColor
.isCurrent())
1858 return maStopColor
.getBColor();
1862 const SvgGradientNode
* SvgStyleAttributes::getSvgGradientNodeFill() const
1864 if(mbIsClipPathContent
)
1868 else if(mpSvgGradientNodeFill
)
1870 return mpSvgGradientNodeFill
;
1874 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1876 if(pSvgStyleAttributes
)
1878 return pSvgStyleAttributes
->getSvgGradientNodeFill();
1885 const SvgGradientNode
* SvgStyleAttributes::getSvgGradientNodeStroke() const
1887 if(mbIsClipPathContent
)
1891 else if(mpSvgGradientNodeStroke
)
1893 return mpSvgGradientNodeStroke
;
1897 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1899 if(pSvgStyleAttributes
)
1901 return pSvgStyleAttributes
->getSvgGradientNodeStroke();
1908 const SvgPatternNode
* SvgStyleAttributes::getSvgPatternNodeFill() const
1910 if(mbIsClipPathContent
)
1914 else if(mpSvgPatternNodeFill
)
1916 return mpSvgPatternNodeFill
;
1920 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1922 if(pSvgStyleAttributes
)
1924 return pSvgStyleAttributes
->getSvgPatternNodeFill();
1931 const SvgPatternNode
* SvgStyleAttributes::getSvgPatternNodeStroke() const
1933 if(mbIsClipPathContent
)
1937 else if(mpSvgPatternNodeStroke
)
1939 return mpSvgPatternNodeStroke
;
1943 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1945 if(pSvgStyleAttributes
)
1947 return pSvgStyleAttributes
->getSvgPatternNodeStroke();
1954 const SvgNumber
SvgStyleAttributes::getStrokeWidth() const
1956 if(mbIsClipPathContent
)
1958 return SvgNumber(0.0);
1960 else if(maStrokeWidth
.isSet())
1962 return maStrokeWidth
;
1965 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1967 if(pSvgStyleAttributes
)
1969 return pSvgStyleAttributes
->getStrokeWidth();
1973 return SvgNumber(1.0);
1976 const SvgNumber
SvgStyleAttributes::getStopOpacity() const
1978 if(maStopOpacity
.isSet())
1980 return maStopOpacity
;
1984 return SvgNumber(1.0);
1987 const SvgNumber
SvgStyleAttributes::getFillOpacity() const
1989 if(mbIsClipPathContent
)
1991 return SvgNumber(1.0);
1993 else if(maFillOpacity
.isSet())
1995 return maFillOpacity
;
1998 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2000 if(pSvgStyleAttributes
)
2002 return pSvgStyleAttributes
->getFillOpacity();
2006 return SvgNumber(1.0);
2009 FillRule
SvgStyleAttributes::getFillRule() const
2011 if(FillRule_notset
!= maFillRule
)
2016 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2018 if(pSvgStyleAttributes
)
2020 return pSvgStyleAttributes
->getFillRule();
2023 // default is NonZero
2024 return FillRule_nonzero
;
2027 const SvgNumberVector
& SvgStyleAttributes::getStrokeDasharray() const
2029 if(!maStrokeDasharray
.empty())
2031 return maStrokeDasharray
;
2033 else if(getStrokeDasharraySet())
2035 // #121221# is set to empty *by purpose*, do not visit parent styles
2036 return maStrokeDasharray
;
2039 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2041 if(pSvgStyleAttributes
)
2043 return pSvgStyleAttributes
->getStrokeDasharray();
2047 return maStrokeDasharray
;
2050 const SvgNumber
SvgStyleAttributes::getStrokeDashOffset() const
2052 if(maStrokeDashOffset
.isSet())
2054 return maStrokeDashOffset
;
2057 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2059 if(pSvgStyleAttributes
)
2061 return pSvgStyleAttributes
->getStrokeDashOffset();
2065 return SvgNumber(0.0);
2068 StrokeLinecap
SvgStyleAttributes::getStrokeLinecap() const
2070 if(maStrokeLinecap
!= StrokeLinecap_notset
)
2072 return maStrokeLinecap
;
2075 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2077 if(pSvgStyleAttributes
)
2079 return pSvgStyleAttributes
->getStrokeLinecap();
2082 // default is StrokeLinecap_butt
2083 return StrokeLinecap_butt
;
2086 StrokeLinejoin
SvgStyleAttributes::getStrokeLinejoin() const
2088 if(maStrokeLinejoin
!= StrokeLinejoin_notset
)
2090 return maStrokeLinejoin
;
2093 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2095 if(pSvgStyleAttributes
)
2097 return pSvgStyleAttributes
->getStrokeLinejoin();
2100 // default is StrokeLinejoin_butt
2101 return StrokeLinejoin_miter
;
2104 const SvgNumber
SvgStyleAttributes::getStrokeMiterLimit() const
2106 if(maStrokeMiterLimit
.isSet())
2108 return maStrokeMiterLimit
;
2111 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2113 if(pSvgStyleAttributes
)
2115 return pSvgStyleAttributes
->getStrokeMiterLimit();
2119 return SvgNumber(4.0);
2122 const SvgNumber
SvgStyleAttributes::getStrokeOpacity() const
2124 if(maStrokeOpacity
.isSet())
2126 return maStrokeOpacity
;
2129 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2131 if(pSvgStyleAttributes
)
2133 return pSvgStyleAttributes
->getStrokeOpacity();
2137 return SvgNumber(1.0);
2140 const SvgStringVector
& SvgStyleAttributes::getFontFamily() const
2142 if(!maFontFamily
.empty())
2144 return maFontFamily
;
2147 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2149 if(pSvgStyleAttributes
)
2151 return pSvgStyleAttributes
->getFontFamily();
2155 return maFontFamily
;
2158 const SvgNumber
SvgStyleAttributes::getFontSize() const
2160 if(maFontSize
.isSet())
2165 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2167 if(pSvgStyleAttributes
)
2169 return pSvgStyleAttributes
->getFontSize();
2172 // default is 'medium'
2173 return SvgNumber(12.0);
2176 FontStretch
SvgStyleAttributes::getFontStretch() const
2178 if(maFontStretch
!= FontStretch_notset
)
2180 if(FontStretch_wider
!= maFontStretch
&& FontStretch_narrower
!= maFontStretch
)
2182 return maFontStretch
;
2186 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2188 if(pSvgStyleAttributes
)
2190 FontStretch aInherited
= pSvgStyleAttributes
->getFontStretch();
2192 if(FontStretch_wider
== maFontStretch
)
2194 aInherited
= getWider(aInherited
);
2196 else if(FontStretch_narrower
== maFontStretch
)
2198 aInherited
= getNarrower(aInherited
);
2204 // default is FontStretch_normal
2205 return FontStretch_normal
;
2208 FontStyle
SvgStyleAttributes::getFontStyle() const
2210 if(maFontStyle
!= FontStyle_notset
)
2215 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2217 if(pSvgStyleAttributes
)
2219 return pSvgStyleAttributes
->getFontStyle();
2222 // default is FontStyle_normal
2223 return FontStyle_normal
;
2226 FontWeight
SvgStyleAttributes::getFontWeight() const
2228 if(maFontWeight
!= FontWeight_notset
)
2230 if(FontWeight_bolder
!= maFontWeight
&& FontWeight_lighter
!= maFontWeight
)
2232 return maFontWeight
;
2236 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2238 if(pSvgStyleAttributes
)
2240 FontWeight aInherited
= pSvgStyleAttributes
->getFontWeight();
2242 if(FontWeight_bolder
== maFontWeight
)
2244 aInherited
= getBolder(aInherited
);
2246 else if(FontWeight_lighter
== maFontWeight
)
2248 aInherited
= getLighter(aInherited
);
2254 // default is FontWeight_400 (FontWeight_normal)
2255 return FontWeight_400
;
2258 TextAlign
SvgStyleAttributes::getTextAlign() const
2260 if(maTextAlign
!= TextAlign_notset
)
2265 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2267 if(pSvgStyleAttributes
)
2269 return pSvgStyleAttributes
->getTextAlign();
2272 // default is TextAlign_left
2273 return TextAlign_left
;
2276 const SvgStyleAttributes
* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const
2278 if(maTextDecoration
!= TextDecoration_notset
)
2283 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2285 if(pSvgStyleAttributes
)
2287 return pSvgStyleAttributes
->getTextDecorationDefiningSvgStyleAttributes();
2294 TextDecoration
SvgStyleAttributes::getTextDecoration() const
2296 const SvgStyleAttributes
* pDefining
= getTextDecorationDefiningSvgStyleAttributes();
2300 return pDefining
->maTextDecoration
;
2304 // default is TextDecoration_none
2305 return TextDecoration_none
;
2309 TextAnchor
SvgStyleAttributes::getTextAnchor() const
2311 if(maTextAnchor
!= TextAnchor_notset
)
2313 return maTextAnchor
;
2316 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2318 if(pSvgStyleAttributes
)
2320 return pSvgStyleAttributes
->getTextAnchor();
2323 // default is TextAnchor_start
2324 return TextAnchor_start
;
2327 const basegfx::BColor
* SvgStyleAttributes::getColor() const
2331 if(maColor
.isCurrent())
2333 OSL_ENSURE(false, "Svg error: current color uses current color (!)");
2336 else if(maColor
.isOn())
2338 return &maColor
.getBColor();
2343 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2345 if(pSvgStyleAttributes
)
2347 return pSvgStyleAttributes
->getColor();
2354 const OUString
SvgStyleAttributes::getMarkerStartXLink() const
2356 if(maMarkerStartXLink
.getLength())
2358 return maMarkerStartXLink
;
2361 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2363 if(pSvgStyleAttributes
)
2365 return pSvgStyleAttributes
->getMarkerStartXLink();
2371 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerStartXLink() const
2373 if(!mpMarkerStartXLink
)
2375 const OUString
aMarker(getMarkerStartXLink());
2377 if(aMarker
.getLength())
2379 const_cast< SvgStyleAttributes
* >(this)->mpMarkerStartXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerStartXLink()));
2383 return mpMarkerStartXLink
;
2386 const OUString
SvgStyleAttributes::getMarkerMidXLink() const
2388 if(maMarkerMidXLink
.getLength())
2390 return maMarkerMidXLink
;
2393 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2395 if(pSvgStyleAttributes
)
2397 return pSvgStyleAttributes
->getMarkerMidXLink();
2403 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerMidXLink() const
2405 if(!mpMarkerMidXLink
)
2407 const OUString
aMarker(getMarkerMidXLink());
2409 if(aMarker
.getLength())
2411 const_cast< SvgStyleAttributes
* >(this)->mpMarkerMidXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerMidXLink()));
2415 return mpMarkerMidXLink
;
2418 const OUString
SvgStyleAttributes::getMarkerEndXLink() const
2420 if(maMarkerEndXLink
.getLength())
2422 return maMarkerEndXLink
;
2425 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2427 if(pSvgStyleAttributes
)
2429 return pSvgStyleAttributes
->getMarkerEndXLink();
2435 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerEndXLink() const
2437 if(!mpMarkerEndXLink
)
2439 const OUString
aMarker(getMarkerEndXLink());
2441 if(aMarker
.getLength())
2443 const_cast< SvgStyleAttributes
* >(this)->mpMarkerEndXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerEndXLink()));
2447 return mpMarkerEndXLink
;
2450 } // end of namespace svgreader
2451 } // end of namespace svgio
2453 //////////////////////////////////////////////////////////////////////////////
2456 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */