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/b2dpolypolygoncutter.hxx>
35 #include <basegfx/polygon/b2dpolypolygontools.hxx>
36 #include <svgio/svgreader/svgmarkernode.hxx>
37 #include <basegfx/curve/b2dcubicbezier.hxx>
38 #include <svgio/svgreader/svgpatternnode.hxx>
39 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
40 #include <basegfx/polygon/b2dpolygontools.hxx>
41 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
43 //////////////////////////////////////////////////////////////////////////////
49 basegfx::B2DLineJoin
StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin
)
51 if(StrokeLinejoin_round
== aStrokeLinejoin
)
53 return basegfx::B2DLINEJOIN_ROUND
;
55 else if(StrokeLinejoin_bevel
== aStrokeLinejoin
)
57 return basegfx::B2DLINEJOIN_BEVEL
;
60 return basegfx::B2DLINEJOIN_MITER
;
63 com::sun::star::drawing::LineCap
StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap
)
65 switch(aStrokeLinecap
)
67 default: /* StrokeLinecap_notset, StrokeLinecap_butt */
69 return com::sun::star::drawing::LineCap_BUTT
;
72 case StrokeLinecap_round
:
74 return com::sun::star::drawing::LineCap_ROUND
;
77 case StrokeLinecap_square
:
79 return com::sun::star::drawing::LineCap_SQUARE
;
85 FontStretch
getWider(FontStretch aSource
)
89 case FontStretch_ultra_condensed
: aSource
= FontStretch_extra_condensed
; break;
90 case FontStretch_extra_condensed
: aSource
= FontStretch_condensed
; break;
91 case FontStretch_condensed
: aSource
= FontStretch_semi_condensed
; break;
92 case FontStretch_semi_condensed
: aSource
= FontStretch_normal
; break;
93 case FontStretch_normal
: aSource
= FontStretch_semi_expanded
; break;
94 case FontStretch_semi_expanded
: aSource
= FontStretch_expanded
; break;
95 case FontStretch_expanded
: aSource
= FontStretch_extra_expanded
; break;
96 case FontStretch_extra_expanded
: aSource
= FontStretch_ultra_expanded
; break;
103 FontStretch
getNarrower(FontStretch aSource
)
107 case FontStretch_extra_condensed
: aSource
= FontStretch_ultra_condensed
; break;
108 case FontStretch_condensed
: aSource
= FontStretch_extra_condensed
; break;
109 case FontStretch_semi_condensed
: aSource
= FontStretch_condensed
; break;
110 case FontStretch_normal
: aSource
= FontStretch_semi_condensed
; break;
111 case FontStretch_semi_expanded
: aSource
= FontStretch_normal
; break;
112 case FontStretch_expanded
: aSource
= FontStretch_semi_expanded
; break;
113 case FontStretch_extra_expanded
: aSource
= FontStretch_expanded
; break;
114 case FontStretch_ultra_expanded
: aSource
= FontStretch_extra_expanded
; break;
121 FontWeight
getBolder(FontWeight aSource
)
125 case FontWeight_100
: aSource
= FontWeight_200
; break;
126 case FontWeight_200
: aSource
= FontWeight_300
; break;
127 case FontWeight_300
: aSource
= FontWeight_400
; break;
128 case FontWeight_400
: aSource
= FontWeight_500
; break;
129 case FontWeight_500
: aSource
= FontWeight_600
; break;
130 case FontWeight_600
: aSource
= FontWeight_700
; break;
131 case FontWeight_700
: aSource
= FontWeight_800
; break;
132 case FontWeight_800
: aSource
= FontWeight_900
; break;
139 FontWeight
getLighter(FontWeight aSource
)
143 case FontWeight_200
: aSource
= FontWeight_100
; break;
144 case FontWeight_300
: aSource
= FontWeight_200
; break;
145 case FontWeight_400
: aSource
= FontWeight_300
; break;
146 case FontWeight_500
: aSource
= FontWeight_400
; break;
147 case FontWeight_600
: aSource
= FontWeight_500
; break;
148 case FontWeight_700
: aSource
= FontWeight_600
; break;
149 case FontWeight_800
: aSource
= FontWeight_700
; break;
150 case FontWeight_900
: aSource
= FontWeight_800
; break;
157 ::FontWeight
getVclFontWeight(FontWeight aSource
)
159 ::FontWeight
nRetval(WEIGHT_NORMAL
);
163 case FontWeight_100
: nRetval
= WEIGHT_ULTRALIGHT
; break;
164 case FontWeight_200
: nRetval
= WEIGHT_LIGHT
; break;
165 case FontWeight_300
: nRetval
= WEIGHT_SEMILIGHT
; break;
166 case FontWeight_400
: nRetval
= WEIGHT_NORMAL
; break;
167 case FontWeight_500
: nRetval
= WEIGHT_MEDIUM
; break;
168 case FontWeight_600
: nRetval
= WEIGHT_SEMIBOLD
; break;
169 case FontWeight_700
: nRetval
= WEIGHT_BOLD
; break;
170 case FontWeight_800
: nRetval
= WEIGHT_ULTRABOLD
; break;
171 case FontWeight_900
: nRetval
= WEIGHT_BLACK
; break;
178 void SvgStyleAttributes::readStyle(const rtl::OUString
& rCandidate
)
180 const sal_Int32
nLen(rCandidate
.getLength());
185 const sal_Int32
nInitPos(nPos
);
186 skip_char(rCandidate
, sal_Unicode(' '), nPos
, nLen
);
187 rtl::OUStringBuffer aTokenName
;
188 copyString(rCandidate
, nPos
, aTokenName
, nLen
);
190 if(aTokenName
.getLength())
192 skip_char(rCandidate
, sal_Unicode(' '), sal_Unicode(':'), nPos
, nLen
);
193 rtl::OUStringBuffer aTokenValue
;
194 copyToLimiter(rCandidate
, sal_Unicode(';'), nPos
, aTokenValue
, nLen
);
195 skip_char(rCandidate
, sal_Unicode(' '), sal_Unicode(';'), nPos
, nLen
);
196 const rtl::OUString
aOUTokenName(aTokenName
.makeStringAndClear());
197 const rtl::OUString
aOUTokenValue(aTokenValue
.makeStringAndClear());
199 parseStyleAttribute(aOUTokenName
, StrToSVGToken(aOUTokenName
), aOUTokenValue
);
204 OSL_ENSURE(false, "Could not interpret on current position (!)");
210 void SvgStyleAttributes::checkForCssStyle(const rtl::OUString
& rClassStr
) const
212 if(!mpCssStyleParent
)
214 const SvgDocument
& rDocument
= mrOwner
.getDocument();
215 const SvgStyleAttributes
* pNew
= 0;
217 if(rDocument
.hasSvgStyleAttributesById())
219 if(mrOwner
.getClass())
221 rtl::OUString
aId(rtl::OUString::createFromAscii("."));
222 aId
= aId
+ *mrOwner
.getClass();
223 pNew
= rDocument
.findSvgStyleAttributesById(aId
);
225 if(!pNew
&& rClassStr
.getLength())
227 aId
= rClassStr
+ aId
;
229 pNew
= rDocument
.findSvgStyleAttributesById(aId
);
233 if(!pNew
&& mrOwner
.getId())
235 pNew
= rDocument
.findSvgStyleAttributesById(*mrOwner
.getId());
238 if(!pNew
&& rClassStr
.getLength())
240 pNew
= rDocument
.findSvgStyleAttributesById(rClassStr
);
245 // found css style, set as parent
246 const_cast< SvgStyleAttributes
* >(this)->mpCssStyleParent
= pNew
;
252 const SvgStyleAttributes
* SvgStyleAttributes::getParentStyle() const
256 return mpCssStyleParent
;
259 if(mrOwner
.getParent())
261 return mrOwner
.getParent()->getSvgStyleAttributes();
267 void SvgStyleAttributes::add_text(
268 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
269 drawinglayer::primitive2d::Primitive2DSequence
& rSource
) const
271 if(rSource
.hasElements())
273 // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D
274 // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill())
275 // set. When another fill is used and also evtl. stroke is set it gets necessary to
276 // dismantle to geometry and add needed primitives
277 const basegfx::BColor
* pFill
= getFill();
278 const SvgGradientNode
* pFillGradient
= getSvgGradientNodeFill();
279 const SvgPatternNode
* pFillPattern
= getSvgPatternNodeFill();
280 const basegfx::BColor
* pStroke
= getStroke();
281 const SvgGradientNode
* pStrokeGradient
= getSvgGradientNodeStroke();
282 const SvgPatternNode
* pStrokePattern
= getSvgPatternNodeStroke();
283 basegfx::B2DPolyPolygon aMergedArea
;
285 if(pFillGradient
|| pFillPattern
|| pStroke
|| pStrokeGradient
|| pStrokePattern
)
287 // text geometry is needed, create
288 // use neutral ViewInformation and create LineGeometryExtractor2D
289 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
290 drawinglayer::processor2d::TextAsPolygonExtractor2D
aExtractor(aViewInformation2D
);
293 aExtractor
.process(rSource
);
296 const drawinglayer::processor2d::TextAsPolygonDataNodeVector
& rResult
= aExtractor
.getTarget();
297 const sal_uInt32
nResultCount(rResult
.size());
298 basegfx::B2DPolyPolygonVector aTextFillVector
;
299 aTextFillVector
.reserve(nResultCount
);
301 for(sal_uInt32
a(0); a
< nResultCount
; a
++)
303 const drawinglayer::processor2d::TextAsPolygonDataNode
& rCandidate
= rResult
[a
];
305 if(rCandidate
.getIsFilled())
307 aTextFillVector
.push_back(rCandidate
.getB2DPolyPolygon());
311 if(!aTextFillVector
.empty())
313 aMergedArea
= basegfx::tools::mergeToSinglePolyPolygon(aTextFillVector
);
317 const bool bStrokeUsed(pStroke
|| pStrokeGradient
|| pStrokePattern
);
319 // add fill. Use geometry even for simple color fill when stroke
320 // is used, else text rendering and the geometry-based stroke will
321 // normally not really match optically due to divrese system text
323 if(aMergedArea
.count() && (pFillGradient
|| pFillPattern
|| bStrokeUsed
))
325 // create text fill content based on geometry
326 add_fill(aMergedArea
, rTarget
, aMergedArea
.getB2DRange());
330 // add the already prepared primitives for single color fill
331 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, rSource
);
335 if(aMergedArea
.count() && bStrokeUsed
)
337 // create text stroke content
338 add_stroke(aMergedArea
, rTarget
, aMergedArea
.getB2DRange());
343 void SvgStyleAttributes::add_fillGradient(
344 const basegfx::B2DPolyPolygon
& rPath
,
345 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
346 const SvgGradientNode
& rFillGradient
,
347 const basegfx::B2DRange
& rGeoRange
) const
349 // create fill content
350 drawinglayer::primitive2d::SvgGradientEntryVector aSvgGradientEntryVector
;
352 // get the color stops
353 rFillGradient
.collectGradientEntries(aSvgGradientEntryVector
);
355 if(!aSvgGradientEntryVector
.empty())
357 basegfx::B2DHomMatrix aGeoToUnit
;
359 if(rFillGradient
.getGradientTransform())
361 aGeoToUnit
= *rFillGradient
.getGradientTransform();
364 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
366 aGeoToUnit
.translate(-rGeoRange
.getMinX(), -rGeoRange
.getMinY());
367 aGeoToUnit
.scale(1.0 / rGeoRange
.getWidth(), 1.0 / rGeoRange
.getHeight());
370 if(SVGTokenLinearGradient
== rFillGradient
.getType())
372 basegfx::B2DPoint
aStart(0.0, 0.0);
373 basegfx::B2DPoint
aEnd(1.0, 0.0);
375 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
377 // all possible units
378 aStart
.setX(rFillGradient
.getX1().solve(mrOwner
, xcoordinate
));
379 aStart
.setY(rFillGradient
.getY1().solve(mrOwner
, ycoordinate
));
380 aEnd
.setX(rFillGradient
.getX2().solve(mrOwner
, xcoordinate
));
381 aEnd
.setY(rFillGradient
.getY2().solve(mrOwner
, ycoordinate
));
385 // fractions or percent relative to object bounds
386 const SvgNumber
X1(rFillGradient
.getX1());
387 const SvgNumber
Y1(rFillGradient
.getY1());
388 const SvgNumber
X2(rFillGradient
.getX2());
389 const SvgNumber
Y2(rFillGradient
.getY2());
391 aStart
.setX(Unit_percent
== X1
.getUnit() ? X1
.getNumber() * 0.01 : X1
.getNumber());
392 aStart
.setY(Unit_percent
== Y1
.getUnit() ? Y1
.getNumber() * 0.01 : Y1
.getNumber());
393 aEnd
.setX(Unit_percent
== X2
.getUnit() ? X2
.getNumber() * 0.01 : X2
.getNumber());
394 aEnd
.setY(Unit_percent
== Y2
.getUnit() ? Y2
.getNumber() * 0.01 : Y2
.getNumber());
397 if(!aGeoToUnit
.isIdentity())
399 aStart
*= aGeoToUnit
;
403 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
405 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
407 aSvgGradientEntryVector
,
410 rFillGradient
.getSpreadMethod()));
414 basegfx::B2DPoint
aStart(0.5, 0.5);
415 basegfx::B2DPoint aFocal
;
417 const SvgNumber
* pFx
= rFillGradient
.getFx();
418 const SvgNumber
* pFy
= rFillGradient
.getFy();
419 const bool bFocal(pFx
|| pFy
);
421 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
423 // all possible units
424 aStart
.setX(rFillGradient
.getCx().solve(mrOwner
, xcoordinate
));
425 aStart
.setY(rFillGradient
.getCy().solve(mrOwner
, ycoordinate
));
426 fRadius
= rFillGradient
.getR().solve(mrOwner
, length
);
430 aFocal
.setX(pFx
? pFx
->solve(mrOwner
, xcoordinate
) : aStart
.getX());
431 aFocal
.setY(pFy
? pFy
->solve(mrOwner
, ycoordinate
) : aStart
.getY());
436 // fractions or percent relative to object bounds
437 const SvgNumber
Cx(rFillGradient
.getCx());
438 const SvgNumber
Cy(rFillGradient
.getCy());
439 const SvgNumber
R(rFillGradient
.getR());
441 aStart
.setX(Unit_percent
== Cx
.getUnit() ? Cx
.getNumber() * 0.01 : Cx
.getNumber());
442 aStart
.setY(Unit_percent
== Cy
.getUnit() ? Cy
.getNumber() * 0.01 : Cy
.getNumber());
443 fRadius
= (Unit_percent
== R
.getUnit()) ? R
.getNumber() * 0.01 : R
.getNumber();
447 aFocal
.setX(pFx
? (Unit_percent
== pFx
->getUnit() ? pFx
->getNumber() * 0.01 : pFx
->getNumber()) : aStart
.getX());
448 aFocal
.setY(pFy
? (Unit_percent
== pFy
->getUnit() ? pFy
->getNumber() * 0.01 : pFy
->getNumber()) : aStart
.getY());
452 if(!aGeoToUnit
.isIdentity())
454 aStart
*= aGeoToUnit
;
455 fRadius
= (aGeoToUnit
* basegfx::B2DVector(fRadius
, 0.0)).getLength();
459 aFocal
*= aGeoToUnit
;
463 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
465 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
467 aSvgGradientEntryVector
,
470 rFillGradient
.getSpreadMethod(),
471 bFocal
? &aFocal
: 0));
476 void SvgStyleAttributes::add_fillPatternTransform(
477 const basegfx::B2DPolyPolygon
& rPath
,
478 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
479 const SvgPatternNode
& rFillPattern
,
480 const basegfx::B2DRange
& rGeoRange
) const
482 // prepare fill polyPolygon with given pattern, check for patternTransform
483 if(rFillPattern
.getPatternTransform() && !rFillPattern
.getPatternTransform()->isIdentity())
485 // PatternTransform is active; Handle by filling the inverse transformed
486 // path and back-transforming the result
487 basegfx::B2DPolyPolygon
aPath(rPath
);
488 basegfx::B2DHomMatrix
aInv(*rFillPattern
.getPatternTransform());
489 drawinglayer::primitive2d::Primitive2DSequence aNewTarget
;
492 aPath
.transform(aInv
);
493 add_fillPattern(aPath
, aNewTarget
, rFillPattern
, aPath
.getB2DRange());
495 if(aNewTarget
.hasElements())
497 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
499 new drawinglayer::primitive2d::TransformPrimitive2D(
500 *rFillPattern
.getPatternTransform(),
506 // no patternTransform, create fillPattern directly
507 add_fillPattern(rPath
, rTarget
, rFillPattern
, rGeoRange
);
511 void SvgStyleAttributes::add_fillPattern(
512 const basegfx::B2DPolyPolygon
& rPath
,
513 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
514 const SvgPatternNode
& rFillPattern
,
515 const basegfx::B2DRange
& rGeoRange
) const
517 // fill polyPolygon with given pattern
518 const drawinglayer::primitive2d::Primitive2DSequence
& rPrimitives
= rFillPattern
.getPatternPrimitives();
520 if(rPrimitives
.hasElements())
522 double fTargetWidth(rGeoRange
.getWidth());
523 double fTargetHeight(rGeoRange
.getHeight());
525 if(fTargetWidth
> 0.0 && fTargetHeight
> 0.0)
527 // get relative values from pattern
533 rFillPattern
.getValuesRelative(fX
, fY
, fW
, fH
, rGeoRange
, mrOwner
);
535 if(fW
> 0.0 && fH
> 0.0)
537 // build the reference range relative to the rGeoRange
538 const basegfx::B2DRange
aReferenceRange(fX
, fY
, fX
+ fW
, fY
+ fH
);
540 // find out how the content is mapped to the reference range
541 basegfx::B2DHomMatrix aMapPrimitivesToUnitRange
;
542 const basegfx::B2DRange
* pViewBox
= rFillPattern
.getViewBox();
546 // use viewBox/preserveAspectRatio
547 const SvgAspectRatio
& rRatio
= rFillPattern
.getSvgAspectRatio();
548 const basegfx::B2DRange
aUnitRange(0.0, 0.0, 1.0, 1.0);
552 // let mapping be created from SvgAspectRatio
553 aMapPrimitivesToUnitRange
= rRatio
.createMapping(aUnitRange
, *pViewBox
);
557 // choose default mapping
558 aMapPrimitivesToUnitRange
= rRatio
.createLinearMapping(aUnitRange
, *pViewBox
);
563 // use patternContentUnits
564 const SvgUnits
aPatternContentUnits(rFillPattern
.getPatternContentUnits() ? *rFillPattern
.getPatternContentUnits() : userSpaceOnUse
);
566 if(userSpaceOnUse
== aPatternContentUnits
)
568 // create relative mapping to unit coordinates
569 aMapPrimitivesToUnitRange
.scale(1.0 / (fW
* fTargetWidth
), 1.0 / (fH
* fTargetHeight
));
573 aMapPrimitivesToUnitRange
.scale(1.0 / fW
, 1.0 / fH
);
577 // apply aMapPrimitivesToUnitRange to content when used
578 drawinglayer::primitive2d::Primitive2DSequence
aPrimitives(rPrimitives
);
580 if(!aMapPrimitivesToUnitRange
.isIdentity())
582 const drawinglayer::primitive2d::Primitive2DReference
xRef(
583 new drawinglayer::primitive2d::TransformPrimitive2D(
584 aMapPrimitivesToUnitRange
,
587 aPrimitives
= drawinglayer::primitive2d::Primitive2DSequence(&xRef
, 1);
590 // embed in PatternFillPrimitive2D
591 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
593 new drawinglayer::primitive2d::PatternFillPrimitive2D(
602 void SvgStyleAttributes::add_fill(
603 const basegfx::B2DPolyPolygon
& rPath
,
604 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
605 const basegfx::B2DRange
& rGeoRange
) const
607 const basegfx::BColor
* pFill
= getFill();
608 const SvgGradientNode
* pFillGradient
= getSvgGradientNodeFill();
609 const SvgPatternNode
* pFillPattern
= getSvgPatternNodeFill();
611 if(pFill
|| pFillGradient
|| pFillPattern
)
613 const double fFillOpacity(getFillOpacity().solve(mrOwner
, length
));
615 if(basegfx::fTools::more(fFillOpacity
, 0.0))
617 drawinglayer::primitive2d::Primitive2DSequence aNewFill
;
621 // create fill content with SVG gradient primitive
622 add_fillGradient(rPath
, aNewFill
, *pFillGradient
, rGeoRange
);
624 else if(pFillPattern
)
626 // create fill content with SVG pattern primitive
627 add_fillPatternTransform(rPath
, aNewFill
, *pFillPattern
, rGeoRange
);
631 // create fill content
633 aNewFill
[0] = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
638 if(aNewFill
.hasElements())
640 if(basegfx::fTools::less(fFillOpacity
, 1.0))
642 // embed in UnifiedTransparencePrimitive2D
643 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
645 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
647 1.0 - fFillOpacity
));
652 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, aNewFill
);
659 void SvgStyleAttributes::add_stroke(
660 const basegfx::B2DPolyPolygon
& rPath
,
661 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
662 const basegfx::B2DRange
& rGeoRange
) const
664 const basegfx::BColor
* pStroke
= getStroke();
665 const SvgGradientNode
* pStrokeGradient
= getSvgGradientNodeStroke();
666 const SvgPatternNode
* pStrokePattern
= getSvgPatternNodeStroke();
668 if(pStroke
|| pStrokeGradient
|| pStrokePattern
)
670 drawinglayer::primitive2d::Primitive2DSequence aNewStroke
;
671 const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner
, length
));
673 if(basegfx::fTools::more(fStrokeOpacity
, 0.0))
675 // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all
676 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner
, length
) : 1.0);
678 if(basegfx::fTools::more(fStrokeWidth
, 0.0))
680 // get LineJoin, LineCap and stroke array
681 const basegfx::B2DLineJoin
aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin()));
682 const com::sun::star::drawing::LineCap
aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap()));
683 ::std::vector
< double > aDashArray
;
685 if(!getStrokeDasharray().empty())
687 aDashArray
= solveSvgNumberVector(getStrokeDasharray(), mrOwner
, length
);
690 // todo: Handle getStrokeDashOffset()
692 // prepare line attribute
693 drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive
;
694 const drawinglayer::attribute::LineAttribute
aLineAttribute(
695 pStroke
? *pStroke
: basegfx::BColor(0.0, 0.0, 0.0),
700 if(aDashArray
.empty())
702 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
708 const drawinglayer::attribute::StrokeAttribute
aStrokeAttribute(aDashArray
);
710 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
716 if(pStrokeGradient
|| pStrokePattern
)
718 // put primitive into Primitive2DReference and Primitive2DSequence
719 const drawinglayer::primitive2d::Primitive2DSequence
aSeq(&aNewLinePrimitive
, 1);
721 // use neutral ViewInformation and create LineGeometryExtractor2D
722 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
723 drawinglayer::processor2d::LineGeometryExtractor2D
aExtractor(aViewInformation2D
);
726 aExtractor
.process(aSeq
);
728 // check for fill rsults
729 const basegfx::B2DPolyPolygonVector
& rLineFillVector(aExtractor
.getExtractedLineFills());
731 if(!rLineFillVector
.empty())
733 const basegfx::B2DPolyPolygon
aMergedArea(
734 basegfx::tools::mergeToSinglePolyPolygon(
737 if(aMergedArea
.count())
741 // create fill content with SVG gradient primitive. Use original GeoRange,
742 // e.g. from circle without LineWidth
743 add_fillGradient(aMergedArea
, aNewStroke
, *pStrokeGradient
, rGeoRange
);
745 else // if(pStrokePattern)
747 // create fill content with SVG pattern primitive. Use GeoRange
748 // from the expanded data, e.g. circle with extended geo by half linewidth
749 add_fillPatternTransform(aMergedArea
, aNewStroke
, *pStrokePattern
, aMergedArea
.getB2DRange());
756 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aNewStroke
, aNewLinePrimitive
);
759 if(aNewStroke
.hasElements())
761 if(basegfx::fTools::less(fStrokeOpacity
, 1.0))
763 // embed in UnifiedTransparencePrimitive2D
764 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(
766 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
768 1.0 - fStrokeOpacity
));
773 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, aNewStroke
);
781 double get_markerRotation(
782 const SvgMarkerNode
& rMarker
,
783 const basegfx::B2DPolygon
& rPolygon
,
784 const sal_uInt32 nIndex
)
787 const sal_uInt32
nPointCount(rPolygon
.count());
791 if(rMarker
.getOrientAuto())
793 const bool bPrev(rPolygon
.isClosed() || nIndex
> 0);
794 basegfx::B2DCubicBezier aSegment
;
795 basegfx::B2DVector aPrev
;
796 basegfx::B2DVector aNext
;
800 rPolygon
.getBezierSegment((nIndex
- 1) % nPointCount
, aSegment
);
801 aPrev
= aSegment
.getTangent(1.0);
804 const bool bNext(rPolygon
.isClosed() || nIndex
+ 1 < nPointCount
);
808 rPolygon
.getBezierSegment(nIndex
% nPointCount
, aSegment
);
809 aNext
= aSegment
.getTangent(0.0);
814 fAngle
= atan2(aPrev
.getY() + aNext
.getY(), aPrev
.getX() + aNext
.getX());
818 fAngle
= atan2(aPrev
.getY(), aPrev
.getX());
822 fAngle
= atan2(aNext
.getY(), aNext
.getX());
827 fAngle
= rMarker
.getAngle();
834 bool SvgStyleAttributes::prepare_singleMarker(
835 drawinglayer::primitive2d::Primitive2DSequence
& rMarkerPrimitives
,
836 basegfx::B2DHomMatrix
& rMarkerTransform
,
837 basegfx::B2DRange
& rClipRange
,
838 const SvgMarkerNode
& rMarker
) const
840 // reset return values
841 rMarkerTransform
.identity();
844 // get marker primitive representation
845 rMarkerPrimitives
= rMarker
.getMarkerPrimitives();
847 if(rMarkerPrimitives
.hasElements())
849 basegfx::B2DRange
aPrimitiveRange(0.0, 0.0, 1.0, 1.0);
850 const basegfx::B2DRange
* pViewBox
= rMarker
.getViewBox();
854 aPrimitiveRange
= *pViewBox
;
857 if(aPrimitiveRange
.getWidth() > 0.0 && aPrimitiveRange
.getHeight() > 0.0)
859 double fTargetWidth(rMarker
.getMarkerWidth().isSet() ? rMarker
.getMarkerWidth().solve(mrOwner
, xcoordinate
) : 3.0);
860 double fTargetHeight(rMarker
.getMarkerHeight().isSet() ? rMarker
.getMarkerHeight().solve(mrOwner
, xcoordinate
) : 3.0);
861 const bool bStrokeWidth(SvgMarkerNode::strokeWidth
== rMarker
.getMarkerUnits());
862 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner
, length
) : 1.0);
866 // relative to strokeWidth
867 fTargetWidth
*= fStrokeWidth
;
868 fTargetHeight
*= fStrokeWidth
;
871 if(fTargetWidth
> 0.0 && fTargetHeight
> 0.0)
874 const basegfx::B2DRange
aTargetRange(0.0, 0.0, fTargetWidth
, fTargetHeight
);
875 const SvgAspectRatio
& rRatio
= rMarker
.getSvgAspectRatio();
879 // let mapping be created from SvgAspectRatio
880 rMarkerTransform
= rRatio
.createMapping(aTargetRange
, aPrimitiveRange
);
882 if(rRatio
.isMeetOrSlice())
885 rClipRange
= aPrimitiveRange
;
894 // adapt to strokewidth if needed
895 rMarkerTransform
.scale(fStrokeWidth
, fStrokeWidth
);
900 // choose default mapping
901 rMarkerTransform
= rRatio
.createLinearMapping(aTargetRange
, aPrimitiveRange
);
905 // get and apply reference point. Initially it's in marker local coordinate system
906 basegfx::B2DPoint
aRefPoint(
907 rMarker
.getRefX().isSet() ? rMarker
.getRefX().solve(mrOwner
, xcoordinate
) : 0.0,
908 rMarker
.getRefY().isSet() ? rMarker
.getRefY().solve(mrOwner
, ycoordinate
) : 0.0);
910 // apply MarkerTransform to have it in mapped coordinates
911 aRefPoint
*= rMarkerTransform
;
913 // apply by moving RepPoint to (0.0)
914 rMarkerTransform
.translate(-aRefPoint
.getX(), -aRefPoint
.getY());
924 void SvgStyleAttributes::add_singleMarker(
925 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
926 const drawinglayer::primitive2d::Primitive2DSequence
& rMarkerPrimitives
,
927 const basegfx::B2DHomMatrix
& rMarkerTransform
,
928 const basegfx::B2DRange
& rClipRange
,
929 const SvgMarkerNode
& rMarker
,
930 const basegfx::B2DPolygon
& rCandidate
,
931 const sal_uInt32 nIndex
) const
933 const sal_uInt32
nPointCount(rCandidate
.count());
937 // get and apply rotation
938 basegfx::B2DHomMatrix
aCombinedTransform(rMarkerTransform
);
939 aCombinedTransform
.rotate(get_markerRotation(rMarker
, rCandidate
, nIndex
));
941 // get and apply target position
942 const basegfx::B2DPoint
aPoint(rCandidate
.getB2DPoint(nIndex
% nPointCount
));
943 aCombinedTransform
.translate(aPoint
.getX(), aPoint
.getY());
946 drawinglayer::primitive2d::Primitive2DReference
xMarker(
947 new drawinglayer::primitive2d::TransformPrimitive2D(
951 if(!rClipRange
.isEmpty())
953 // marker needs to be clipped, it's bigger as the mapping
954 basegfx::B2DPolyPolygon
aClipPolygon(basegfx::tools::createPolygonFromRect(rClipRange
));
956 aClipPolygon
.transform(aCombinedTransform
);
957 xMarker
= new drawinglayer::primitive2d::MaskPrimitive2D(
959 drawinglayer::primitive2d::Primitive2DSequence(&xMarker
, 1));
963 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget
, xMarker
);
967 void SvgStyleAttributes::add_markers(
968 const basegfx::B2DPolyPolygon
& rPath
,
969 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
) const
971 // try to access linked markers
972 const SvgMarkerNode
* pStart
= accessMarkerStartXLink();
973 const SvgMarkerNode
* pMid
= accessMarkerMidXLink();
974 const SvgMarkerNode
* pEnd
= accessMarkerEndXLink();
976 if(pStart
|| pMid
|| pEnd
)
978 const sal_uInt32
nCount(rPath
.count());
980 for (sal_uInt32
a(0); a
< nCount
; a
++)
982 const basegfx::B2DPolygon
aCandidate(rPath
.getB2DPolygon(a
));
983 const sal_uInt32
nPointCount(aCandidate
.count());
987 const sal_uInt32
nMarkerCount(aCandidate
.isClosed() ? nPointCount
+ 1 : nPointCount
);
988 drawinglayer::primitive2d::Primitive2DSequence aMarkerPrimitives
;
989 basegfx::B2DHomMatrix aMarkerTransform
;
990 basegfx::B2DRange aClipRange
;
991 const SvgMarkerNode
* pPrepared
= 0;
995 if(prepare_singleMarker(aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pStart
))
998 add_singleMarker(rTarget
, aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pPrepared
, aCandidate
, 0);
1002 if(pMid
&& nMarkerCount
> 2)
1004 if(pMid
== pPrepared
|| prepare_singleMarker(aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pMid
))
1008 for(sal_uInt32
b(1); b
< nMarkerCount
- 1; b
++)
1010 add_singleMarker(rTarget
, aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pPrepared
, aCandidate
, b
);
1017 if(pEnd
== pPrepared
|| prepare_singleMarker(aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pEnd
))
1020 add_singleMarker(rTarget
, aMarkerPrimitives
, aMarkerTransform
, aClipRange
, *pPrepared
, aCandidate
, nMarkerCount
- 1);
1028 void SvgStyleAttributes::add_path(
1029 const basegfx::B2DPolyPolygon
& rPath
,
1030 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
) const
1032 const bool bIsLine(1 == rPath
.count()
1033 && !rPath
.areControlPointsUsed()
1034 && 2 == rPath
.getB2DPolygon(0).count());
1041 const basegfx::B2DRange
aGeoRange(rPath
.getB2DRange());
1043 if(aGeoRange
.isEmpty())
1048 if(!bIsLine
&& // not for lines
1049 (basegfx::fTools::equalZero(aGeoRange
.getWidth())
1050 || basegfx::fTools::equalZero(aGeoRange
.getHeight())))
1055 const double fOpacity(getOpacity().getNumber());
1057 if(basegfx::fTools::equalZero(fOpacity
))
1064 basegfx::B2DPolyPolygon
aPath(rPath
);
1065 const bool bNeedToCheckClipRule(SVGTokenPath
== mrOwner
.getType() || SVGTokenPolygon
== mrOwner
.getType());
1066 const bool bClipPathIsNonzero(!bIsLine
&& bNeedToCheckClipRule
&& mbIsClipPathContent
&& mbClipRule
);
1067 const bool bFillRuleIsNonzero(!bIsLine
&& bNeedToCheckClipRule
&& !mbIsClipPathContent
&& getFillRule());
1069 if(bClipPathIsNonzero
|| bFillRuleIsNonzero
)
1071 // nonzero is wanted, solve geometrically (see description on basegfx)
1072 aPath
= basegfx::tools::createNonzeroConform(aPath
);
1075 add_fill(aPath
, rTarget
, aGeoRange
);
1078 add_stroke(rPath
, rTarget
, aGeoRange
);
1080 // Svg supports markers for path, polygon, polyline and line
1081 if(SVGTokenPath
== mrOwner
.getType() || // path
1082 SVGTokenPolygon
== mrOwner
.getType() || // polygon, polyline
1083 SVGTokenLine
== mrOwner
.getType()) // line
1085 // try to add markers
1086 add_markers(rPath
, rTarget
);
1090 void SvgStyleAttributes::add_postProcess(
1091 drawinglayer::primitive2d::Primitive2DSequence
& rTarget
,
1092 const drawinglayer::primitive2d::Primitive2DSequence
& rSource
,
1093 const basegfx::B2DHomMatrix
* pTransform
) const
1095 if(rSource
.hasElements())
1097 const double fOpacity(getOpacity().getNumber());
1099 if(basegfx::fTools::equalZero(fOpacity
))
1104 drawinglayer::primitive2d::Primitive2DSequence
aSource(rSource
);
1106 if(basegfx::fTools::less(fOpacity
, 1.0))
1108 // embed in UnifiedTransparencePrimitive2D
1109 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1110 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1114 aSource
= drawinglayer::primitive2d::Primitive2DSequence(&xRef
, 1);
1117 if(getClipPathXLink().getLength())
1119 // try to access linked ClipPath
1120 const SvgClipPathNode
* mpClip
= dynamic_cast< const SvgClipPathNode
* >(mrOwner
.getDocument().findSvgNodeById(getClipPathXLink()));
1124 mpClip
->apply(aSource
);
1128 if(aSource
.hasElements()) // test again, applied clipPath may have lead to empty geometry
1130 if(getMaskXLink().getLength())
1132 // try to access linked Mask
1133 const SvgMaskNode
* mpMask
= dynamic_cast< const SvgMaskNode
* >(mrOwner
.getDocument().findSvgNodeById(getMaskXLink()));
1137 mpMask
->apply(aSource
);
1141 if(aSource
.hasElements()) // test again, applied mask may have lead to empty geometry
1145 // create embedding group element with transformation
1146 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1147 new drawinglayer::primitive2d::TransformPrimitive2D(
1151 aSource
= drawinglayer::primitive2d::Primitive2DSequence(&xRef
, 1);
1154 // append to current target
1155 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, aSource
);
1161 SvgStyleAttributes::SvgStyleAttributes(SvgNode
& rOwner
)
1163 mpCssStyleParent(0),
1166 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
1169 mpSvgGradientNodeFill(0),
1170 mpSvgGradientNodeStroke(0),
1171 mpSvgPatternNodeFill(0),
1172 mpSvgPatternNodeStroke(0),
1174 maStrokeDasharray(),
1175 maStrokeDashOffset(),
1176 maStrokeLinecap(StrokeLinecap_notset
),
1177 maStrokeLinejoin(StrokeLinejoin_notset
),
1178 maStrokeMiterLimit(),
1182 maFontStretch(FontStretch_notset
),
1183 maFontStyle(FontStyle_notset
),
1184 maFontVariant(FontVariant_notset
),
1185 maFontWeight(FontWeight_notset
),
1186 maTextAlign(TextAlign_notset
),
1187 maTextDecoration(TextDecoration_notset
),
1188 maTextAnchor(TextAnchor_notset
),
1193 maMarkerStartXLink(),
1194 mpMarkerStartXLink(0),
1196 mpMarkerMidXLink(0),
1198 mpMarkerEndXLink(0),
1200 maFillRuleSet(false),
1201 mbIsClipPathContent(SVGTokenClipPathNode
== mrOwner
.getType()),
1204 if(!mbIsClipPathContent
)
1206 const SvgStyleAttributes
* pParentStyle
= getParentStyle();
1210 mbIsClipPathContent
= pParentStyle
->mbIsClipPathContent
;
1215 SvgStyleAttributes::~SvgStyleAttributes()
1219 void SvgStyleAttributes::parseStyleAttribute(const rtl::OUString
& /*rTokenName*/, SVGToken aSVGToken
, const rtl::OUString
& aContent
)
1228 if(readSvgPaint(aContent
, aSvgPaint
, aURL
))
1232 else if(aURL
.getLength())
1234 const SvgNode
* pNode
= mrOwner
.getDocument().findSvgNodeById(aURL
);
1238 if(SVGTokenLinearGradient
== pNode
->getType() || SVGTokenRadialGradient
== pNode
->getType())
1240 setSvgGradientNodeFill(static_cast< const SvgGradientNode
* >(pNode
));
1242 else if(SVGTokenPattern
== pNode
->getType())
1244 setSvgPatternNodeFill(static_cast< const SvgPatternNode
* >(pNode
));
1250 case SVGTokenFillOpacity
:
1254 if(readSingleNumber(aContent
, aNum
))
1256 if(aNum
.isPositive())
1258 setFillOpacity(aNum
);
1263 case SVGTokenFillRule
:
1265 if(aContent
.getLength())
1267 if(aContent
.match(commonStrings::aStrNonzero
))
1270 maFillRuleSet
= true;
1272 else if(aContent
.match(commonStrings::aStrEvenOdd
))
1275 maFillRuleSet
= true;
1280 case SVGTokenStroke
:
1285 if(readSvgPaint(aContent
, aSvgPaint
, aURL
))
1287 setStroke(aSvgPaint
);
1289 else if(aURL
.getLength())
1291 const SvgNode
* pNode
= mrOwner
.getDocument().findSvgNodeById(aURL
);
1295 if(SVGTokenLinearGradient
== pNode
->getType() || SVGTokenRadialGradient
== pNode
->getType())
1297 setSvgGradientNodeStroke(static_cast< const SvgGradientNode
* >(pNode
));
1299 else if(SVGTokenPattern
== pNode
->getType())
1301 setSvgPatternNodeStroke(static_cast< const SvgPatternNode
* >(pNode
));
1307 case SVGTokenStrokeDasharray
:
1309 if(aContent
.getLength())
1311 SvgNumberVector aVector
;
1313 if(readSvgNumberVector(aContent
, aVector
))
1315 setStrokeDasharray(aVector
);
1320 case SVGTokenStrokeDashoffset
:
1324 if(readSingleNumber(aContent
, aNum
))
1326 if(aNum
.isPositive())
1328 setStrokeDashOffset(aNum
);
1333 case SVGTokenStrokeLinecap
:
1335 if(aContent
.getLength())
1337 static rtl::OUString
aStrButt(rtl::OUString::createFromAscii("butt"));
1338 static rtl::OUString
aStrRound(rtl::OUString::createFromAscii("round"));
1339 static rtl::OUString
aStrSquare(rtl::OUString::createFromAscii("square"));
1341 if(aContent
.match(aStrButt
))
1343 setStrokeLinecap(StrokeLinecap_butt
);
1345 else if(aContent
.match(aStrRound
))
1347 setStrokeLinecap(StrokeLinecap_round
);
1349 else if(aContent
.match(aStrSquare
))
1351 setStrokeLinecap(StrokeLinecap_square
);
1356 case SVGTokenStrokeLinejoin
:
1358 if(aContent
.getLength())
1360 static rtl::OUString
aStrMiter(rtl::OUString::createFromAscii("miter"));
1361 static rtl::OUString
aStrRound(rtl::OUString::createFromAscii("round"));
1362 static rtl::OUString
aStrBevel(rtl::OUString::createFromAscii("bevel"));
1364 if(aContent
.match(aStrMiter
))
1366 setStrokeLinejoin(StrokeLinejoin_miter
);
1368 else if(aContent
.match(aStrRound
))
1370 setStrokeLinejoin(StrokeLinejoin_round
);
1372 else if(aContent
.match(aStrBevel
))
1374 setStrokeLinejoin(StrokeLinejoin_bevel
);
1379 case SVGTokenStrokeMiterlimit
:
1383 if(readSingleNumber(aContent
, aNum
))
1385 if(aNum
.isPositive())
1387 setStrokeMiterLimit(aNum
);
1392 case SVGTokenStrokeOpacity
:
1396 if(readSingleNumber(aContent
, aNum
))
1398 if(aNum
.isPositive())
1400 setStrokeOpacity(aNum
);
1405 case SVGTokenStrokeWidth
:
1409 if(readSingleNumber(aContent
, aNum
))
1411 if(aNum
.isPositive())
1413 setStrokeWidth(aNum
);
1418 case SVGTokenStopColor
:
1423 if(readSvgPaint(aContent
, aSvgPaint
, aURL
))
1425 setStopColor(aSvgPaint
);
1429 case SVGTokenStopOpacity
:
1433 if(readSingleNumber(aContent
, aNum
))
1435 if(aNum
.isPositive())
1437 setStopOpacity(aNum
);
1446 case SVGTokenFontFamily
:
1448 SvgStringVector aSvgStringVector
;
1450 if(readSvgStringVector(aContent
, aSvgStringVector
))
1452 setFontFamily(aSvgStringVector
);
1456 case SVGTokenFontSize
:
1460 if(readSingleNumber(aContent
, aNum
))
1466 case SVGTokenFontSizeAdjust
:
1470 case SVGTokenFontStretch
:
1472 if(aContent
.getLength())
1474 static rtl::OUString
aStrNormal(rtl::OUString::createFromAscii("normal"));
1475 static rtl::OUString
aStrWider(rtl::OUString::createFromAscii("wider"));
1476 static rtl::OUString
aStrNarrower(rtl::OUString::createFromAscii("narrower"));
1477 static rtl::OUString
aStrUltra_condensed(rtl::OUString::createFromAscii("ultra-condensed"));
1478 static rtl::OUString
aStrExtra_condensed(rtl::OUString::createFromAscii("extra-condensed"));
1479 static rtl::OUString
aStrCondensed(rtl::OUString::createFromAscii("condensed"));
1480 static rtl::OUString
aStrSemi_condensed(rtl::OUString::createFromAscii("semi-condensed"));
1481 static rtl::OUString
aStrSemi_expanded(rtl::OUString::createFromAscii("semi-expanded"));
1482 static rtl::OUString
aStrExpanded(rtl::OUString::createFromAscii("expanded"));
1483 static rtl::OUString
aStrExtra_expanded(rtl::OUString::createFromAscii("extra-expanded"));
1484 static rtl::OUString
aStrUltra_expanded(rtl::OUString::createFromAscii("ultra-expanded"));
1486 if(aContent
.match(aStrNormal
))
1488 setFontStretch(FontStretch_normal
);
1490 else if(aContent
.match(aStrWider
))
1492 setFontStretch(FontStretch_wider
);
1494 else if(aContent
.match(aStrNarrower
))
1496 setFontStretch(FontStretch_narrower
);
1498 else if(aContent
.match(aStrUltra_condensed
))
1500 setFontStretch(FontStretch_ultra_condensed
);
1502 else if(aContent
.match(aStrExtra_condensed
))
1504 setFontStretch(FontStretch_extra_condensed
);
1506 else if(aContent
.match(aStrCondensed
))
1508 setFontStretch(FontStretch_condensed
);
1510 else if(aContent
.match(aStrSemi_condensed
))
1512 setFontStretch(FontStretch_semi_condensed
);
1514 else if(aContent
.match(aStrSemi_expanded
))
1516 setFontStretch(FontStretch_semi_expanded
);
1518 else if(aContent
.match(aStrExpanded
))
1520 setFontStretch(FontStretch_expanded
);
1522 else if(aContent
.match(aStrExtra_expanded
))
1524 setFontStretch(FontStretch_extra_expanded
);
1526 else if(aContent
.match(aStrUltra_expanded
))
1528 setFontStretch(FontStretch_ultra_expanded
);
1533 case SVGTokenFontStyle
:
1535 if(aContent
.getLength())
1537 static rtl::OUString
aStrNormal(rtl::OUString::createFromAscii("normal"));
1538 static rtl::OUString
aStrItalic(rtl::OUString::createFromAscii("italic"));
1539 static rtl::OUString
aStrOblique(rtl::OUString::createFromAscii("oblique"));
1541 if(aContent
.match(aStrNormal
))
1543 setFontStyle(FontStyle_normal
);
1545 else if(aContent
.match(aStrItalic
))
1547 setFontStyle(FontStyle_italic
);
1549 else if(aContent
.match(aStrOblique
))
1551 setFontStyle(FontStyle_oblique
);
1556 case SVGTokenFontVariant
:
1558 if(aContent
.getLength())
1560 static rtl::OUString
aStrNormal(rtl::OUString::createFromAscii("normal"));
1561 static rtl::OUString
aStrSmallCaps(rtl::OUString::createFromAscii("small-caps"));
1563 if(aContent
.match(aStrNormal
))
1565 setFontVariant(FontVariant_normal
);
1567 else if(aContent
.match(aStrSmallCaps
))
1569 setFontVariant(FontVariant_small_caps
);
1574 case SVGTokenFontWeight
:
1576 if(aContent
.getLength())
1578 static rtl::OUString
aStrNormal(rtl::OUString::createFromAscii("normal"));
1579 static rtl::OUString
aStrBold(rtl::OUString::createFromAscii("bold"));
1580 static rtl::OUString
aStrBolder(rtl::OUString::createFromAscii("bolder"));
1581 static rtl::OUString
aStrLighter(rtl::OUString::createFromAscii("lighter"));
1582 static rtl::OUString
aStr100(rtl::OUString::createFromAscii("100"));
1583 static rtl::OUString
aStr200(rtl::OUString::createFromAscii("200"));
1584 static rtl::OUString
aStr300(rtl::OUString::createFromAscii("300"));
1585 static rtl::OUString
aStr400(rtl::OUString::createFromAscii("400"));
1586 static rtl::OUString
aStr500(rtl::OUString::createFromAscii("500"));
1587 static rtl::OUString
aStr600(rtl::OUString::createFromAscii("600"));
1588 static rtl::OUString
aStr700(rtl::OUString::createFromAscii("700"));
1589 static rtl::OUString
aStr800(rtl::OUString::createFromAscii("800"));
1590 static rtl::OUString
aStr900(rtl::OUString::createFromAscii("900"));
1592 if(aContent
.match(aStr100
))
1594 setFontWeight(FontWeight_100
);
1596 else if(aContent
.match(aStr200
))
1598 setFontWeight(FontWeight_200
);
1600 else if(aContent
.match(aStr300
))
1602 setFontWeight(FontWeight_300
);
1604 else if(aContent
.match(aStr400
) || aContent
.match(aStrNormal
))
1606 setFontWeight(FontWeight_400
);
1608 else if(aContent
.match(aStr500
))
1610 setFontWeight(FontWeight_500
);
1612 else if(aContent
.match(aStr600
))
1614 setFontWeight(FontWeight_600
);
1616 else if(aContent
.match(aStr700
) || aContent
.match(aStrBold
))
1618 setFontWeight(FontWeight_700
);
1620 else if(aContent
.match(aStr800
))
1622 setFontWeight(FontWeight_800
);
1624 else if(aContent
.match(aStr900
))
1626 setFontWeight(FontWeight_900
);
1628 else if(aContent
.match(aStrBolder
))
1630 setFontWeight(FontWeight_bolder
);
1632 else if(aContent
.match(aStrLighter
))
1634 setFontWeight(FontWeight_lighter
);
1639 case SVGTokenDirection
:
1643 case SVGTokenLetterSpacing
:
1647 case SVGTokenTextDecoration
:
1649 if(aContent
.getLength())
1651 static rtl::OUString
aStrNone(rtl::OUString::createFromAscii("none"));
1652 static rtl::OUString
aStrUnderline(rtl::OUString::createFromAscii("underline"));
1653 static rtl::OUString
aStrOverline(rtl::OUString::createFromAscii("overline"));
1654 static rtl::OUString
aStrLineThrough(rtl::OUString::createFromAscii("line-through"));
1655 static rtl::OUString
aStrBlink(rtl::OUString::createFromAscii("blink"));
1657 if(aContent
.match(aStrNone
))
1659 setTextDecoration(TextDecoration_none
);
1661 else if(aContent
.match(aStrUnderline
))
1663 setTextDecoration(TextDecoration_underline
);
1665 else if(aContent
.match(aStrOverline
))
1667 setTextDecoration(TextDecoration_overline
);
1669 else if(aContent
.match(aStrLineThrough
))
1671 setTextDecoration(TextDecoration_line_through
);
1673 else if(aContent
.match(aStrBlink
))
1675 setTextDecoration(TextDecoration_blink
);
1680 case SVGTokenUnicodeBidi
:
1684 case SVGTokenWordSpacing
:
1688 case SVGTokenTextAnchor
:
1690 if(aContent
.getLength())
1692 static rtl::OUString
aStrStart(rtl::OUString::createFromAscii("start"));
1693 static rtl::OUString
aStrMiddle(rtl::OUString::createFromAscii("middle"));
1694 static rtl::OUString
aStrEnd(rtl::OUString::createFromAscii("end"));
1696 if(aContent
.match(aStrStart
))
1698 setTextAnchor(TextAnchor_start
);
1700 else if(aContent
.match(aStrMiddle
))
1702 setTextAnchor(TextAnchor_middle
);
1704 else if(aContent
.match(aStrEnd
))
1706 setTextAnchor(TextAnchor_end
);
1711 case SVGTokenTextAlign
:
1713 if(aContent
.getLength())
1715 static rtl::OUString
aStrLeft(rtl::OUString::createFromAscii("left"));
1716 static rtl::OUString
aStrRight(rtl::OUString::createFromAscii("right"));
1717 static rtl::OUString
aStrCenter(rtl::OUString::createFromAscii("center"));
1718 static rtl::OUString
aStrJustify(rtl::OUString::createFromAscii("justify"));
1720 if(aContent
.match(aStrLeft
))
1722 setTextAlign(TextAlign_left
);
1724 else if(aContent
.match(aStrRight
))
1726 setTextAlign(TextAlign_right
);
1728 else if(aContent
.match(aStrCenter
))
1730 setTextAlign(TextAlign_center
);
1732 else if(aContent
.match(aStrJustify
))
1734 setTextAlign(TextAlign_justify
);
1744 if(readSvgPaint(aContent
, aSvgPaint
, aURL
))
1746 setColor(aSvgPaint
);
1750 case SVGTokenOpacity
:
1754 if(readSingleNumber(aContent
, aNum
))
1756 setOpacity(SvgNumber(basegfx::clamp(aNum
.getNumber(), 0.0, 1.0), aNum
.getUnit(), aNum
.isSet()));
1760 case SVGTokenClipPathProperty
:
1762 readLocalUrl(aContent
, maClipPathXLink
);
1767 readLocalUrl(aContent
, maMaskXLink
);
1770 case SVGTokenClipRule
:
1772 if(aContent
.getLength())
1774 if(aContent
.match(commonStrings::aStrNonzero
))
1778 else if(aContent
.match(commonStrings::aStrEvenOdd
))
1785 case SVGTokenMarker
:
1787 readLocalUrl(aContent
, maMarkerEndXLink
);
1788 maMarkerStartXLink
= maMarkerMidXLink
= maMarkerEndXLink
;
1791 case SVGTokenMarkerStart
:
1793 readLocalUrl(aContent
, maMarkerStartXLink
);
1796 case SVGTokenMarkerMid
:
1798 readLocalUrl(aContent
, maMarkerMidXLink
);
1801 case SVGTokenMarkerEnd
:
1803 readLocalUrl(aContent
, maMarkerEndXLink
);
1813 const basegfx::BColor
* SvgStyleAttributes::getFill() const
1815 if(mbIsClipPathContent
)
1817 static basegfx::BColor
aBlack(0.0, 0.0, 0.0);
1821 else if(maFill
.isSet())
1823 if(maFill
.isCurrent())
1827 else if(maFill
.isOn())
1829 return &maFill
.getBColor();
1834 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1836 if(pSvgStyleAttributes
)
1838 return pSvgStyleAttributes
->getFill();
1845 const basegfx::BColor
* SvgStyleAttributes::getStroke() const
1847 if(mbIsClipPathContent
)
1851 else if(maStroke
.isSet())
1853 if(maStroke
.isCurrent())
1857 else if(maStroke
.isOn())
1859 return &maStroke
.getBColor();
1864 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1866 if(pSvgStyleAttributes
)
1868 return pSvgStyleAttributes
->getStroke();
1875 const basegfx::BColor
& SvgStyleAttributes::getStopColor() const
1877 if(maStopColor
.isCurrent())
1883 return maStopColor
.getBColor();
1887 const SvgGradientNode
* SvgStyleAttributes::getSvgGradientNodeFill() const
1889 if(mbIsClipPathContent
)
1893 else if(mpSvgGradientNodeFill
)
1895 return mpSvgGradientNodeFill
;
1899 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1901 if(pSvgStyleAttributes
)
1903 return pSvgStyleAttributes
->getSvgGradientNodeFill();
1910 const SvgGradientNode
* SvgStyleAttributes::getSvgGradientNodeStroke() const
1912 if(mbIsClipPathContent
)
1916 else if(mpSvgGradientNodeStroke
)
1918 return mpSvgGradientNodeStroke
;
1922 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1924 if(pSvgStyleAttributes
)
1926 return pSvgStyleAttributes
->getSvgGradientNodeStroke();
1933 const SvgPatternNode
* SvgStyleAttributes::getSvgPatternNodeFill() const
1935 if(mbIsClipPathContent
)
1939 else if(mpSvgPatternNodeFill
)
1941 return mpSvgPatternNodeFill
;
1945 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1947 if(pSvgStyleAttributes
)
1949 return pSvgStyleAttributes
->getSvgPatternNodeFill();
1956 const SvgPatternNode
* SvgStyleAttributes::getSvgPatternNodeStroke() const
1958 if(mbIsClipPathContent
)
1962 else if(mpSvgPatternNodeStroke
)
1964 return mpSvgPatternNodeStroke
;
1968 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1970 if(pSvgStyleAttributes
)
1972 return pSvgStyleAttributes
->getSvgPatternNodeStroke();
1979 const SvgNumber
SvgStyleAttributes::getStrokeWidth() const
1981 if(mbIsClipPathContent
)
1983 return SvgNumber(0.0);
1985 else if(maStrokeWidth
.isSet())
1987 return maStrokeWidth
;
1990 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
1992 if(pSvgStyleAttributes
)
1994 return pSvgStyleAttributes
->getStrokeWidth();
1998 return SvgNumber(1.0);
2001 const SvgNumber
SvgStyleAttributes::getStopOpacity() const
2003 if(maStopOpacity
.isSet())
2005 return maStopOpacity
;
2009 return SvgNumber(1.0);
2012 const SvgNumber
SvgStyleAttributes::getFillOpacity() const
2014 if(mbIsClipPathContent
)
2016 return SvgNumber(1.0);
2018 else if(maFillOpacity
.isSet())
2020 return maFillOpacity
;
2023 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2025 if(pSvgStyleAttributes
)
2027 return pSvgStyleAttributes
->getFillOpacity();
2031 return SvgNumber(1.0);
2034 bool SvgStyleAttributes::getFillRule() const
2041 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2043 if(pSvgStyleAttributes
)
2045 return pSvgStyleAttributes
->getFillRule();
2048 // default is NonZero
2052 const SvgNumberVector
& SvgStyleAttributes::getStrokeDasharray() const
2054 if(!maStrokeDasharray
.empty())
2056 return maStrokeDasharray
;
2059 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2061 if(pSvgStyleAttributes
)
2063 return pSvgStyleAttributes
->getStrokeDasharray();
2067 return maStrokeDasharray
;
2070 const SvgNumber
SvgStyleAttributes::getStrokeDashOffset() const
2072 if(maStrokeDashOffset
.isSet())
2074 return maStrokeDashOffset
;
2077 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2079 if(pSvgStyleAttributes
)
2081 return pSvgStyleAttributes
->getStrokeDashOffset();
2085 return SvgNumber(0.0);
2088 StrokeLinecap
SvgStyleAttributes::getStrokeLinecap() const
2090 if(maStrokeLinecap
!= StrokeLinecap_notset
)
2092 return maStrokeLinecap
;
2095 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2097 if(pSvgStyleAttributes
)
2099 return pSvgStyleAttributes
->getStrokeLinecap();
2102 // default is StrokeLinecap_butt
2103 return StrokeLinecap_butt
;
2106 StrokeLinejoin
SvgStyleAttributes::getStrokeLinejoin() const
2108 if(maStrokeLinejoin
!= StrokeLinejoin_notset
)
2110 return maStrokeLinejoin
;
2113 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2115 if(pSvgStyleAttributes
)
2117 return pSvgStyleAttributes
->getStrokeLinejoin();
2120 // default is StrokeLinejoin_butt
2121 return StrokeLinejoin_miter
;
2124 const SvgNumber
SvgStyleAttributes::getStrokeMiterLimit() const
2126 if(maStrokeMiterLimit
.isSet())
2128 return maStrokeMiterLimit
;
2131 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2133 if(pSvgStyleAttributes
)
2135 return pSvgStyleAttributes
->getStrokeMiterLimit();
2139 return SvgNumber(4.0);
2142 const SvgNumber
SvgStyleAttributes::getStrokeOpacity() const
2144 if(maStrokeOpacity
.isSet())
2146 return maStrokeOpacity
;
2149 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2151 if(pSvgStyleAttributes
)
2153 return pSvgStyleAttributes
->getStrokeOpacity();
2157 return SvgNumber(1.0);
2160 const SvgStringVector
& SvgStyleAttributes::getFontFamily() const
2162 if(!maFontFamily
.empty())
2164 return maFontFamily
;
2167 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2169 if(pSvgStyleAttributes
)
2171 return pSvgStyleAttributes
->getFontFamily();
2175 return maFontFamily
;
2178 const SvgNumber
SvgStyleAttributes::getFontSize() const
2180 if(maFontSize
.isSet())
2185 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2187 if(pSvgStyleAttributes
)
2189 return pSvgStyleAttributes
->getFontSize();
2192 // default is 'medium'
2193 return SvgNumber(12.0);
2196 FontStretch
SvgStyleAttributes::getFontStretch() const
2198 if(maFontStretch
!= FontStretch_notset
)
2200 if(FontStretch_wider
!= maFontStretch
&& FontStretch_narrower
!= maFontStretch
)
2202 return maFontStretch
;
2206 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2208 if(pSvgStyleAttributes
)
2210 FontStretch aInherited
= pSvgStyleAttributes
->getFontStretch();
2212 if(FontStretch_wider
== maFontStretch
)
2214 aInherited
= getWider(aInherited
);
2216 else if(FontStretch_narrower
== maFontStretch
)
2218 aInherited
= getNarrower(aInherited
);
2224 // default is FontStretch_normal
2225 return FontStretch_normal
;
2228 FontStyle
SvgStyleAttributes::getFontStyle() const
2230 if(maFontStyle
!= FontStyle_notset
)
2235 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2237 if(pSvgStyleAttributes
)
2239 return pSvgStyleAttributes
->getFontStyle();
2242 // default is FontStyle_normal
2243 return FontStyle_normal
;
2246 FontWeight
SvgStyleAttributes::getFontWeight() const
2248 if(maFontWeight
!= FontWeight_notset
)
2250 if(FontWeight_bolder
!= maFontWeight
&& FontWeight_lighter
!= maFontWeight
)
2252 return maFontWeight
;
2256 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2258 if(pSvgStyleAttributes
)
2260 FontWeight aInherited
= pSvgStyleAttributes
->getFontWeight();
2262 if(FontWeight_bolder
== maFontWeight
)
2264 aInherited
= getBolder(aInherited
);
2266 else if(FontWeight_lighter
== maFontWeight
)
2268 aInherited
= getLighter(aInherited
);
2274 // default is FontWeight_400 (FontWeight_normal)
2275 return FontWeight_400
;
2278 TextAlign
SvgStyleAttributes::getTextAlign() const
2280 if(maTextAlign
!= TextAlign_notset
)
2285 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2287 if(pSvgStyleAttributes
)
2289 return pSvgStyleAttributes
->getTextAlign();
2292 // default is TextAlign_left
2293 return TextAlign_left
;
2296 const SvgStyleAttributes
* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const
2298 if(maTextDecoration
!= TextDecoration_notset
)
2303 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2305 if(pSvgStyleAttributes
)
2307 return pSvgStyleAttributes
->getTextDecorationDefiningSvgStyleAttributes();
2314 TextDecoration
SvgStyleAttributes::getTextDecoration() const
2316 const SvgStyleAttributes
* pDefining
= getTextDecorationDefiningSvgStyleAttributes();
2320 return pDefining
->maTextDecoration
;
2324 // default is TextDecoration_none
2325 return TextDecoration_none
;
2329 TextAnchor
SvgStyleAttributes::getTextAnchor() const
2331 if(maTextAnchor
!= TextAnchor_notset
)
2333 return maTextAnchor
;
2336 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2338 if(pSvgStyleAttributes
)
2340 return pSvgStyleAttributes
->getTextAnchor();
2343 // default is TextAnchor_start
2344 return TextAnchor_start
;
2347 const basegfx::BColor
* SvgStyleAttributes::getColor() const
2351 if(maColor
.isCurrent())
2353 OSL_ENSURE(false, "Svg error: current color uses current color (!)");
2356 else if(maColor
.isOn())
2358 return &maColor
.getBColor();
2363 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2365 if(pSvgStyleAttributes
)
2367 return pSvgStyleAttributes
->getColor();
2374 const rtl::OUString
SvgStyleAttributes::getMarkerStartXLink() const
2376 if(maMarkerStartXLink
.getLength())
2378 return maMarkerStartXLink
;
2381 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2383 if(pSvgStyleAttributes
)
2385 return pSvgStyleAttributes
->getMarkerStartXLink();
2388 return rtl::OUString();
2391 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerStartXLink() const
2393 if(!mpMarkerStartXLink
)
2395 const rtl::OUString
aMarker(getMarkerStartXLink());
2397 if(aMarker
.getLength())
2399 const_cast< SvgStyleAttributes
* >(this)->mpMarkerStartXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerStartXLink()));
2403 return mpMarkerStartXLink
;
2406 const rtl::OUString
SvgStyleAttributes::getMarkerMidXLink() const
2408 if(maMarkerMidXLink
.getLength())
2410 return maMarkerMidXLink
;
2413 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2415 if(pSvgStyleAttributes
)
2417 return pSvgStyleAttributes
->getMarkerMidXLink();
2420 return rtl::OUString();
2423 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerMidXLink() const
2425 if(!mpMarkerMidXLink
)
2427 const rtl::OUString
aMarker(getMarkerMidXLink());
2429 if(aMarker
.getLength())
2431 const_cast< SvgStyleAttributes
* >(this)->mpMarkerMidXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerMidXLink()));
2435 return mpMarkerMidXLink
;
2438 const rtl::OUString
SvgStyleAttributes::getMarkerEndXLink() const
2440 if(maMarkerEndXLink
.getLength())
2442 return maMarkerEndXLink
;
2445 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2447 if(pSvgStyleAttributes
)
2449 return pSvgStyleAttributes
->getMarkerEndXLink();
2452 return rtl::OUString();
2455 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerEndXLink() const
2457 if(!mpMarkerEndXLink
)
2459 const rtl::OUString
aMarker(getMarkerEndXLink());
2461 if(aMarker
.getLength())
2463 const_cast< SvgStyleAttributes
* >(this)->mpMarkerEndXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerEndXLink()));
2467 return mpMarkerEndXLink
;
2470 } // end of namespace svgreader
2471 } // end of namespace svgio
2473 //////////////////////////////////////////////////////////////////////////////
2476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */