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 <sal/config.h>
22 #include <o3tl/clamp.hxx>
23 #include <svgstyleattributes.hxx>
24 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
25 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
26 #include <svgnode.hxx>
27 #include <svgdocument.hxx>
28 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
29 #include <svggradientnode.hxx>
30 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
31 #include <basegfx/vector/b2enums.hxx>
32 #include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
33 #include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
34 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
35 #include <svgclippathnode.hxx>
36 #include <svgmasknode.hxx>
37 #include <basegfx/polygon/b2dpolypolygontools.hxx>
38 #include <svgmarkernode.hxx>
39 #include <basegfx/curve/b2dcubicbezier.hxx>
40 #include <svgpatternnode.hxx>
41 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
42 #include <basegfx/polygon/b2dpolygontools.hxx>
43 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
44 #include <drawinglayer/primitive2d/pagehierarchyprimitive2d.hxx>
46 const int nStyleDepthLimit
= 1024;
52 static basegfx::B2DLineJoin
StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin
)
54 if(StrokeLinejoin_round
== aStrokeLinejoin
)
56 return basegfx::B2DLineJoin::Round
;
58 else if(StrokeLinejoin_bevel
== aStrokeLinejoin
)
60 return basegfx::B2DLineJoin::Bevel
;
63 return basegfx::B2DLineJoin::Miter
;
66 static css::drawing::LineCap
StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap
)
68 switch(aStrokeLinecap
)
70 default: /* StrokeLinecap_notset, StrokeLinecap_butt */
72 return css::drawing::LineCap_BUTT
;
74 case StrokeLinecap_round
:
76 return css::drawing::LineCap_ROUND
;
78 case StrokeLinecap_square
:
80 return css::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::readCssStyle(const OUString
& rCandidate
)
180 const sal_Int32
nLen(rCandidate
.getLength());
186 OUStringBuffer aTokenName
;
187 skip_char(rCandidate
, u
' ', nPos
, nLen
);
188 copyString(rCandidate
, nPos
, aTokenName
, nLen
);
190 if (aTokenName
.isEmpty())
192 // if no TokenName advance one by force to avoid death loop, continue
193 OSL_ENSURE(false, "Could not interpret on current position, advancing one byte (!)");
199 OUStringBuffer aTokenValue
;
200 skip_char(rCandidate
, u
' ', u
':', nPos
, nLen
);
201 copyToLimiter(rCandidate
, u
';', nPos
, aTokenValue
, nLen
);
202 skip_char(rCandidate
, u
' ', u
';', nPos
, nLen
);
204 if (aTokenValue
.isEmpty())
206 // no value - continue
210 // generate OUStrings
211 const OUString
aOUTokenName(aTokenName
.makeStringAndClear());
212 OUString
aOUTokenValue(aTokenValue
.makeStringAndClear());
214 // check for '!important' CssStyle mark, currently not supported
215 // but needs to be extracted for correct parsing
216 OUString
aTokenImportant("!important");
217 const sal_Int32
nIndexTokenImportant(aOUTokenValue
.indexOf(aTokenImportant
));
219 if(-1 != nIndexTokenImportant
)
221 // if there currently just remove it and remove spaces to have the value only
222 OUString aNewOUTokenValue
;
224 if(nIndexTokenImportant
> 0)
226 // copy content before token
227 aNewOUTokenValue
+= aOUTokenValue
.copy(0, nIndexTokenImportant
);
230 if(aOUTokenValue
.getLength() > nIndexTokenImportant
+ aTokenImportant
.getLength())
232 // copy content after token
233 aNewOUTokenValue
+= aOUTokenValue
.copy(nIndexTokenImportant
+ aTokenImportant
.getLength());
237 aOUTokenValue
= aNewOUTokenValue
.trim();
240 // valid token-value pair, parse it
241 parseStyleAttribute(StrToSVGToken(aOUTokenName
, true), aOUTokenValue
, true);
245 const SvgStyleAttributes
* SvgStyleAttributes::getParentStyle() const
247 if(getCssStyleParent())
249 return getCssStyleParent();
252 if(mrOwner
.supportsParentStyle() && mrOwner
.getParent())
254 return mrOwner
.getParent()->getSvgStyleAttributes();
260 void SvgStyleAttributes::add_text(
261 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
262 drawinglayer::primitive2d::Primitive2DContainer
const & rSource
) const
266 // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D
267 // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill())
268 // set. When another fill is used and also evtl. stroke is set it gets necessary to
269 // dismantle to geometry and add needed primitives
270 const basegfx::BColor
* pFill
= getFill();
271 const SvgGradientNode
* pFillGradient
= getSvgGradientNodeFill();
272 const SvgPatternNode
* pFillPattern
= getSvgPatternNodeFill();
273 const basegfx::BColor
* pStroke
= getStroke();
274 const SvgGradientNode
* pStrokeGradient
= getSvgGradientNodeStroke();
275 const SvgPatternNode
* pStrokePattern
= getSvgPatternNodeStroke();
276 basegfx::B2DPolyPolygon aMergedArea
;
278 if(pFillGradient
|| pFillPattern
|| pStroke
|| pStrokeGradient
|| pStrokePattern
)
280 // text geometry is needed, create
281 // use neutral ViewInformation and create LineGeometryExtractor2D
282 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
283 drawinglayer::processor2d::TextAsPolygonExtractor2D
aExtractor(aViewInformation2D
);
286 aExtractor
.process(rSource
);
289 const drawinglayer::processor2d::TextAsPolygonDataNodeVector
& rResult
= aExtractor
.getTarget();
290 const sal_uInt32
nResultCount(rResult
.size());
291 basegfx::B2DPolyPolygonVector aTextFillVector
;
292 aTextFillVector
.reserve(nResultCount
);
294 for(sal_uInt32
a(0); a
< nResultCount
; a
++)
296 const drawinglayer::processor2d::TextAsPolygonDataNode
& rCandidate
= rResult
[a
];
298 if(rCandidate
.getIsFilled())
300 aTextFillVector
.push_back(rCandidate
.getB2DPolyPolygon());
304 if(!aTextFillVector
.empty())
306 aMergedArea
= basegfx::utils::mergeToSinglePolyPolygon(aTextFillVector
);
310 const bool bStrokeUsed(pStroke
|| pStrokeGradient
|| pStrokePattern
);
312 // add fill. Use geometry even for simple color fill when stroke
313 // is used, else text rendering and the geometry-based stroke will
314 // normally not really match optically due to diverse system text
316 if(aMergedArea
.count() && (pFillGradient
|| pFillPattern
|| bStrokeUsed
))
318 // create text fill content based on geometry
319 add_fill(aMergedArea
, rTarget
, aMergedArea
.getB2DRange());
323 // add the already prepared primitives for single color fill
324 rTarget
.append(rSource
);
328 if(aMergedArea
.count() && bStrokeUsed
)
330 // create text stroke content
331 add_stroke(aMergedArea
, rTarget
, aMergedArea
.getB2DRange());
336 void SvgStyleAttributes::add_fillGradient(
337 const basegfx::B2DPolyPolygon
& rPath
,
338 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
339 const SvgGradientNode
& rFillGradient
,
340 const basegfx::B2DRange
& rGeoRange
) const
342 // create fill content
343 drawinglayer::primitive2d::SvgGradientEntryVector aSvgGradientEntryVector
;
345 // get the color stops
346 rFillGradient
.collectGradientEntries(aSvgGradientEntryVector
);
348 if(!aSvgGradientEntryVector
.empty())
350 basegfx::B2DHomMatrix aGeoToUnit
;
351 basegfx::B2DHomMatrix aGradientTransform
;
353 if(rFillGradient
.getGradientTransform())
355 aGradientTransform
= *rFillGradient
.getGradientTransform();
358 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
360 aGeoToUnit
.translate(-rGeoRange
.getMinX(), -rGeoRange
.getMinY());
361 aGeoToUnit
.scale(1.0 / rGeoRange
.getWidth(), 1.0 / rGeoRange
.getHeight());
364 if(SVGTokenLinearGradient
== rFillGradient
.getType())
366 basegfx::B2DPoint
aStart(0.0, 0.0);
367 basegfx::B2DPoint
aEnd(1.0, 0.0);
369 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
371 // all possible units
372 aStart
.setX(rFillGradient
.getX1().solve(mrOwner
, xcoordinate
));
373 aStart
.setY(rFillGradient
.getY1().solve(mrOwner
, ycoordinate
));
374 aEnd
.setX(rFillGradient
.getX2().solve(mrOwner
, xcoordinate
));
375 aEnd
.setY(rFillGradient
.getY2().solve(mrOwner
, ycoordinate
));
379 // fractions or percent relative to object bounds
380 const SvgNumber
X1(rFillGradient
.getX1());
381 const SvgNumber
Y1(rFillGradient
.getY1());
382 const SvgNumber
X2(rFillGradient
.getX2());
383 const SvgNumber
Y2(rFillGradient
.getY2());
385 aStart
.setX(Unit_percent
== X1
.getUnit() ? X1
.getNumber() * 0.01 : X1
.getNumber());
386 aStart
.setY(Unit_percent
== Y1
.getUnit() ? Y1
.getNumber() * 0.01 : Y1
.getNumber());
387 aEnd
.setX(Unit_percent
== X2
.getUnit() ? X2
.getNumber() * 0.01 : X2
.getNumber());
388 aEnd
.setY(Unit_percent
== Y2
.getUnit() ? Y2
.getNumber() * 0.01 : Y2
.getNumber());
391 if(!aGeoToUnit
.isIdentity())
393 aStart
*= aGeoToUnit
;
398 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
401 aSvgGradientEntryVector
,
404 userSpaceOnUse
!= rFillGradient
.getGradientUnits(),
405 rFillGradient
.getSpreadMethod()));
409 basegfx::B2DPoint
aStart(0.5, 0.5);
410 basegfx::B2DPoint aFocal
;
412 const SvgNumber
* pFx
= rFillGradient
.getFx();
413 const SvgNumber
* pFy
= rFillGradient
.getFy();
414 const bool bFocal(pFx
|| pFy
);
416 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
418 // all possible units
419 aStart
.setX(rFillGradient
.getCx().solve(mrOwner
, xcoordinate
));
420 aStart
.setY(rFillGradient
.getCy().solve(mrOwner
, ycoordinate
));
421 fRadius
= rFillGradient
.getR().solve(mrOwner
);
425 aFocal
.setX(pFx
? pFx
->solve(mrOwner
, xcoordinate
) : aStart
.getX());
426 aFocal
.setY(pFy
? pFy
->solve(mrOwner
, ycoordinate
) : aStart
.getY());
431 // fractions or percent relative to object bounds
432 const SvgNumber
Cx(rFillGradient
.getCx());
433 const SvgNumber
Cy(rFillGradient
.getCy());
434 const SvgNumber
R(rFillGradient
.getR());
436 aStart
.setX(Unit_percent
== Cx
.getUnit() ? Cx
.getNumber() * 0.01 : Cx
.getNumber());
437 aStart
.setY(Unit_percent
== Cy
.getUnit() ? Cy
.getNumber() * 0.01 : Cy
.getNumber());
438 fRadius
= (Unit_percent
== R
.getUnit()) ? R
.getNumber() * 0.01 : R
.getNumber();
442 aFocal
.setX(pFx
? (Unit_percent
== pFx
->getUnit() ? pFx
->getNumber() * 0.01 : pFx
->getNumber()) : aStart
.getX());
443 aFocal
.setY(pFy
? (Unit_percent
== pFy
->getUnit() ? pFy
->getNumber() * 0.01 : pFy
->getNumber()) : aStart
.getY());
447 if(!aGeoToUnit
.isIdentity())
449 aStart
*= aGeoToUnit
;
450 fRadius
= (aGeoToUnit
* basegfx::B2DVector(fRadius
, 0.0)).getLength();
454 aFocal
*= aGeoToUnit
;
459 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
462 aSvgGradientEntryVector
,
465 userSpaceOnUse
!= rFillGradient
.getGradientUnits(),
466 rFillGradient
.getSpreadMethod(),
467 bFocal
? &aFocal
: nullptr));
472 void SvgStyleAttributes::add_fillPatternTransform(
473 const basegfx::B2DPolyPolygon
& rPath
,
474 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
475 const SvgPatternNode
& rFillPattern
,
476 const basegfx::B2DRange
& rGeoRange
) const
478 // prepare fill polyPolygon with given pattern, check for patternTransform
479 if(rFillPattern
.getPatternTransform() && !rFillPattern
.getPatternTransform()->isIdentity())
481 // PatternTransform is active; Handle by filling the inverse transformed
482 // path and back-transforming the result
483 basegfx::B2DPolyPolygon
aPath(rPath
);
484 basegfx::B2DHomMatrix
aInv(*rFillPattern
.getPatternTransform());
485 drawinglayer::primitive2d::Primitive2DContainer aNewTarget
;
488 aPath
.transform(aInv
);
489 add_fillPattern(aPath
, aNewTarget
, rFillPattern
, aPath
.getB2DRange());
491 if(!aNewTarget
.empty())
494 new drawinglayer::primitive2d::TransformPrimitive2D(
495 *rFillPattern
.getPatternTransform(),
501 // no patternTransform, create fillPattern directly
502 add_fillPattern(rPath
, rTarget
, rFillPattern
, rGeoRange
);
506 void SvgStyleAttributes::add_fillPattern(
507 const basegfx::B2DPolyPolygon
& rPath
,
508 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
509 const SvgPatternNode
& rFillPattern
,
510 const basegfx::B2DRange
& rGeoRange
) const
512 // fill polyPolygon with given pattern
513 const drawinglayer::primitive2d::Primitive2DContainer
& rPrimitives
= rFillPattern
.getPatternPrimitives();
515 if(!rPrimitives
.empty())
517 double fTargetWidth(rGeoRange
.getWidth());
518 double fTargetHeight(rGeoRange
.getHeight());
520 if(fTargetWidth
> 0.0 && fTargetHeight
> 0.0)
522 // get relative values from pattern
528 rFillPattern
.getValuesRelative(fX
, fY
, fW
, fH
, rGeoRange
, mrOwner
);
530 if(fW
> 0.0 && fH
> 0.0)
532 // build the reference range relative to the rGeoRange
533 const basegfx::B2DRange
aReferenceRange(fX
, fY
, fX
+ fW
, fY
+ fH
);
535 // find out how the content is mapped to the reference range
536 basegfx::B2DHomMatrix aMapPrimitivesToUnitRange
;
537 const basegfx::B2DRange
* pViewBox
= rFillPattern
.getViewBox();
541 // use viewBox/preserveAspectRatio
542 const SvgAspectRatio
& rRatio
= rFillPattern
.getSvgAspectRatio();
543 const basegfx::B2DRange
aUnitRange(0.0, 0.0, 1.0, 1.0);
547 // let mapping be created from SvgAspectRatio
548 aMapPrimitivesToUnitRange
= rRatio
.createMapping(aUnitRange
, *pViewBox
);
552 // choose default mapping
553 aMapPrimitivesToUnitRange
= SvgAspectRatio::createLinearMapping(aUnitRange
, *pViewBox
);
558 // use patternContentUnits
559 const SvgUnits
aPatternContentUnits(rFillPattern
.getPatternContentUnits() ? *rFillPattern
.getPatternContentUnits() : userSpaceOnUse
);
561 if(userSpaceOnUse
== aPatternContentUnits
)
563 // create relative mapping to unit coordinates
564 aMapPrimitivesToUnitRange
.scale(1.0 / (fW
* fTargetWidth
), 1.0 / (fH
* fTargetHeight
));
568 aMapPrimitivesToUnitRange
.scale(1.0 / fW
, 1.0 / fH
);
572 // apply aMapPrimitivesToUnitRange to content when used
573 drawinglayer::primitive2d::Primitive2DContainer
aPrimitives(rPrimitives
);
575 if(!aMapPrimitivesToUnitRange
.isIdentity())
577 const drawinglayer::primitive2d::Primitive2DReference
xRef(
578 new drawinglayer::primitive2d::TransformPrimitive2D(
579 aMapPrimitivesToUnitRange
,
582 aPrimitives
= drawinglayer::primitive2d::Primitive2DContainer
{ xRef
};
585 // embed in PatternFillPrimitive2D
587 new drawinglayer::primitive2d::PatternFillPrimitive2D(
596 void SvgStyleAttributes::add_fill(
597 const basegfx::B2DPolyPolygon
& rPath
,
598 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
599 const basegfx::B2DRange
& rGeoRange
) const
601 const basegfx::BColor
* pFill
= getFill();
602 const SvgGradientNode
* pFillGradient
= getSvgGradientNodeFill();
603 const SvgPatternNode
* pFillPattern
= getSvgPatternNodeFill();
605 if(pFill
|| pFillGradient
|| pFillPattern
)
607 const double fFillOpacity(getFillOpacity().solve(mrOwner
));
609 if(basegfx::fTools::more(fFillOpacity
, 0.0))
611 drawinglayer::primitive2d::Primitive2DContainer aNewFill
;
615 // create fill content with SVG gradient primitive
616 add_fillGradient(rPath
, aNewFill
, *pFillGradient
, rGeoRange
);
618 else if(pFillPattern
)
620 // create fill content with SVG pattern primitive
621 add_fillPatternTransform(rPath
, aNewFill
, *pFillPattern
, rGeoRange
);
625 // create fill content
627 aNewFill
[0] = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
632 if(!aNewFill
.empty())
634 if(basegfx::fTools::less(fFillOpacity
, 1.0))
636 // embed in UnifiedTransparencePrimitive2D
638 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
640 1.0 - fFillOpacity
));
645 rTarget
.append(aNewFill
);
652 void SvgStyleAttributes::add_stroke(
653 const basegfx::B2DPolyPolygon
& rPath
,
654 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
655 const basegfx::B2DRange
& rGeoRange
) const
657 const basegfx::BColor
* pStroke
= getStroke();
658 const SvgGradientNode
* pStrokeGradient
= getSvgGradientNodeStroke();
659 const SvgPatternNode
* pStrokePattern
= getSvgPatternNodeStroke();
661 if(pStroke
|| pStrokeGradient
|| pStrokePattern
)
663 drawinglayer::primitive2d::Primitive2DContainer aNewStroke
;
664 const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner
));
666 if(basegfx::fTools::more(fStrokeOpacity
, 0.0))
668 // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all
669 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner
) : 1.0);
671 if(basegfx::fTools::more(fStrokeWidth
, 0.0))
673 drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive
;
675 // if we have a line with two identical points it is not really a line,
676 // but used by SVG sometimes to paint a single dot.In that case, create
677 // the geometry for a single dot
678 if(1 == rPath
.count())
680 const basegfx::B2DPolygon
& aSingle(rPath
.getB2DPolygon(0));
682 if(2 == aSingle
.count() && aSingle
.getB2DPoint(0).equal(aSingle
.getB2DPoint(1)))
684 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
685 basegfx::B2DPolyPolygon(
686 basegfx::utils::createPolygonFromCircle(
687 aSingle
.getB2DPoint(0),
688 fStrokeWidth
* (1.44 * 0.5))),
689 pStroke
? *pStroke
: basegfx::BColor(0.0, 0.0, 0.0));
693 if(!aNewLinePrimitive
.is())
695 // get LineJoin, LineCap and stroke array
696 const basegfx::B2DLineJoin
aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin()));
697 const css::drawing::LineCap
aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap()));
698 ::std::vector
< double > aDashArray
;
700 if(!getStrokeDasharray().empty())
702 aDashArray
= solveSvgNumberVector(getStrokeDasharray(), mrOwner
);
705 // convert svg:stroke-miterlimit to LineAttrute:mfMiterMinimumAngle
706 // The default needs to be set explicitly, because svg default <> Draw default
707 double fMiterMinimumAngle
;
708 if (getStrokeMiterLimit().isSet())
710 fMiterMinimumAngle
= 2.0 * asin(1.0/getStrokeMiterLimit().getNumber());
714 fMiterMinimumAngle
= 2.0 * asin(0.25); // 1.0/default 4.0
717 // todo: Handle getStrokeDashOffset()
719 // prepare line attribute
720 const drawinglayer::attribute::LineAttribute
aLineAttribute(
721 pStroke
? *pStroke
: basegfx::BColor(0.0, 0.0, 0.0),
727 if(aDashArray
.empty())
729 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
735 const drawinglayer::attribute::StrokeAttribute
aStrokeAttribute(aDashArray
);
737 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
744 if(pStrokeGradient
|| pStrokePattern
)
746 // put primitive into Primitive2DReference and Primitive2DSequence
747 const drawinglayer::primitive2d::Primitive2DContainer aSeq
{ aNewLinePrimitive
};
749 // use neutral ViewInformation and create LineGeometryExtractor2D
750 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
751 drawinglayer::processor2d::LineGeometryExtractor2D
aExtractor(aViewInformation2D
);
754 aExtractor
.process(aSeq
);
756 // check for fill rsults
757 const basegfx::B2DPolyPolygonVector
& rLineFillVector(aExtractor
.getExtractedLineFills());
759 if(!rLineFillVector
.empty())
761 const basegfx::B2DPolyPolygon
aMergedArea(
762 basegfx::utils::mergeToSinglePolyPolygon(
765 if(aMergedArea
.count())
769 // create fill content with SVG gradient primitive. Use original GeoRange,
770 // e.g. from circle without LineWidth
771 add_fillGradient(aMergedArea
, aNewStroke
, *pStrokeGradient
, rGeoRange
);
773 else // if(pStrokePattern)
775 // create fill content with SVG pattern primitive. Use GeoRange
776 // from the expanded data, e.g. circle with extended geo by half linewidth
777 add_fillPatternTransform(aMergedArea
, aNewStroke
, *pStrokePattern
, aMergedArea
.getB2DRange());
784 aNewStroke
.push_back(aNewLinePrimitive
);
787 if(!aNewStroke
.empty())
789 if(basegfx::fTools::less(fStrokeOpacity
, 1.0))
791 // embed in UnifiedTransparencePrimitive2D
793 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
795 1.0 - fStrokeOpacity
));
800 rTarget
.append(aNewStroke
);
808 bool SvgStyleAttributes::prepare_singleMarker(
809 drawinglayer::primitive2d::Primitive2DContainer
& rMarkerPrimitives
,
810 basegfx::B2DHomMatrix
& rMarkerTransform
,
811 basegfx::B2DRange
& rClipRange
,
812 const SvgMarkerNode
& rMarker
) const
814 // reset return values
815 rMarkerTransform
.identity();
818 // get marker primitive representation
819 rMarkerPrimitives
= rMarker
.getMarkerPrimitives();
821 if(!rMarkerPrimitives
.empty())
823 basegfx::B2DRange
aPrimitiveRange(0.0, 0.0, 1.0, 1.0);
824 const basegfx::B2DRange
* pViewBox
= rMarker
.getViewBox();
828 aPrimitiveRange
= *pViewBox
;
831 if(aPrimitiveRange
.getWidth() > 0.0 && aPrimitiveRange
.getHeight() > 0.0)
833 double fTargetWidth(rMarker
.getMarkerWidth().isSet() ? rMarker
.getMarkerWidth().solve(mrOwner
, xcoordinate
) : 3.0);
834 double fTargetHeight(rMarker
.getMarkerHeight().isSet() ? rMarker
.getMarkerHeight().solve(mrOwner
, xcoordinate
) : 3.0);
835 const bool bStrokeWidth(SvgMarkerNode::MarkerUnits::strokeWidth
== rMarker
.getMarkerUnits());
836 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner
) : 1.0);
840 // relative to strokeWidth
841 fTargetWidth
*= fStrokeWidth
;
842 fTargetHeight
*= fStrokeWidth
;
845 if(fTargetWidth
> 0.0 && fTargetHeight
> 0.0)
848 const basegfx::B2DRange
aTargetRange(0.0, 0.0, fTargetWidth
, fTargetHeight
);
849 const SvgAspectRatio
& rRatio
= rMarker
.getSvgAspectRatio();
853 // let mapping be created from SvgAspectRatio
854 rMarkerTransform
= rRatio
.createMapping(aTargetRange
, aPrimitiveRange
);
856 if(rRatio
.isMeetOrSlice())
859 rClipRange
= aPrimitiveRange
;
868 // adapt to strokewidth if needed
869 rMarkerTransform
.scale(fStrokeWidth
, fStrokeWidth
);
874 // choose default mapping
875 rMarkerTransform
= SvgAspectRatio::createLinearMapping(aTargetRange
, aPrimitiveRange
);
879 // get and apply reference point. Initially it's in marker local coordinate system
880 basegfx::B2DPoint
aRefPoint(
881 rMarker
.getRefX().isSet() ? rMarker
.getRefX().solve(mrOwner
, xcoordinate
) : 0.0,
882 rMarker
.getRefY().isSet() ? rMarker
.getRefY().solve(mrOwner
, ycoordinate
) : 0.0);
884 // apply MarkerTransform to have it in mapped coordinates
885 aRefPoint
*= rMarkerTransform
;
887 // apply by moving RepPoint to (0.0)
888 rMarkerTransform
.translate(-aRefPoint
.getX(), -aRefPoint
.getY());
898 void SvgStyleAttributes::add_markers(
899 const basegfx::B2DPolyPolygon
& rPath
,
900 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
901 const basegfx::utils::PointIndexSet
* pHelpPointIndices
) const
903 // try to access linked markers
904 const SvgMarkerNode
* pStart
= accessMarkerStartXLink();
905 const SvgMarkerNode
* pMid
= accessMarkerMidXLink();
906 const SvgMarkerNode
* pEnd
= accessMarkerEndXLink();
908 if(pStart
|| pMid
|| pEnd
)
910 const sal_uInt32
nSubPathCount(rPath
.count());
914 // remember prepared marker; pStart, pMid and pEnd may all be equal when
915 // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end',
916 // see 'case SVGTokenMarker' in this file; thus in this case only one common
917 // marker in primitive form will be prepared
918 const SvgMarkerNode
* pPrepared
= nullptr;
920 // values for the prepared marker, results of prepare_singleMarker
921 drawinglayer::primitive2d::Primitive2DContainer aPreparedMarkerPrimitives
;
922 basegfx::B2DHomMatrix aPreparedMarkerTransform
;
923 basegfx::B2DRange aPreparedMarkerClipRange
;
925 for (sal_uInt32
a(0); a
< nSubPathCount
; a
++)
927 // iterate over sub-paths
928 const basegfx::B2DPolygon
& aSubPolygonPath(rPath
.getB2DPolygon(a
));
929 const sal_uInt32
nSubPolygonPointCount(aSubPolygonPath
.count());
930 const bool bSubPolygonPathIsClosed(aSubPolygonPath
.isClosed());
932 if(nSubPolygonPointCount
)
934 // for each sub-path, create one marker per point (when closed, two markers
935 // need to pe created for the 1st point)
936 const sal_uInt32
nTargetMarkerCount(bSubPolygonPathIsClosed
? nSubPolygonPointCount
+ 1 : nSubPolygonPointCount
);
938 for (sal_uInt32
b(0); b
< nTargetMarkerCount
; b
++)
940 const bool bIsFirstMarker(!a
&& !b
);
941 const bool bIsLastMarker(nSubPathCount
- 1 == a
&& nTargetMarkerCount
- 1 == b
);
942 const SvgMarkerNode
* pNeeded
= nullptr;
946 // 1st point in 1st sub-polygon, use pStart
949 else if(bIsLastMarker
)
951 // last point in last sub-polygon, use pEnd
956 // anything in-between, use pMid
960 if(pHelpPointIndices
&& !pHelpPointIndices
->empty())
962 const basegfx::utils::PointIndexSet::const_iterator
aFound(
963 pHelpPointIndices
->find(basegfx::utils::PointIndex(a
, b
)));
965 if(aFound
!= pHelpPointIndices
->end())
967 // this point is a pure helper point; do not create a marker for it
974 // no marker needs to be created for this point
978 if(pPrepared
!= pNeeded
)
980 // if needed marker is not yet prepared, do it now
981 if(prepare_singleMarker(aPreparedMarkerPrimitives
, aPreparedMarkerTransform
, aPreparedMarkerClipRange
, *pNeeded
))
987 // error: could not prepare given marker
988 OSL_ENSURE(false, "OOps, could not prepare given marker as primitives (!)");
994 // prepare complete transform
995 basegfx::B2DHomMatrix
aCombinedTransform(aPreparedMarkerTransform
);
998 if(pPrepared
->getOrientAuto())
1000 const sal_uInt32
nPointIndex(b
% nSubPolygonPointCount
);
1002 // get entering and leaving tangents; this will search backward/forward
1003 // in the polygon to find tangents unequal to zero, skipping empty edges
1004 // see basegfx descriptions)
1005 // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker
1006 // and entering tangent for end marker. To achieve this (if wanted) it is possible
1007 // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker.
1008 // This is not done here, see comment 14 in task #1232379#
1009 // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute
1010 basegfx::B2DVector
aEntering(
1011 basegfx::utils::getTangentEnteringPoint(
1014 basegfx::B2DVector
aLeaving(
1015 basegfx::utils::getTangentLeavingPoint(
1018 const bool bEntering(!aEntering
.equalZero());
1019 const bool bLeaving(!aLeaving
.equalZero());
1021 if(bEntering
|| bLeaving
)
1023 basegfx::B2DVector
aSum(0.0, 0.0);
1027 aSum
+= aEntering
.normalize();
1032 aSum
+= aLeaving
.normalize();
1035 if(!aSum
.equalZero())
1037 const double fAngle(atan2(aSum
.getY(), aSum
.getX()));
1040 aCombinedTransform
.rotate(fAngle
);
1047 aCombinedTransform
.rotate(pPrepared
->getAngle());
1050 // get and apply target position
1051 const basegfx::B2DPoint
aPoint(aSubPolygonPath
.getB2DPoint(b
% nSubPolygonPointCount
));
1053 aCombinedTransform
.translate(aPoint
.getX(), aPoint
.getY());
1056 drawinglayer::primitive2d::Primitive2DReference
xMarker(
1057 new drawinglayer::primitive2d::TransformPrimitive2D(
1059 aPreparedMarkerPrimitives
));
1061 if(!aPreparedMarkerClipRange
.isEmpty())
1063 // marker needs to be clipped, it's bigger as the mapping
1064 basegfx::B2DPolyPolygon
aClipPolygon(basegfx::utils::createPolygonFromRect(aPreparedMarkerClipRange
));
1066 aClipPolygon
.transform(aCombinedTransform
);
1067 xMarker
= new drawinglayer::primitive2d::MaskPrimitive2D(
1069 drawinglayer::primitive2d::Primitive2DContainer
{ xMarker
});
1073 rTarget
.push_back(xMarker
);
1081 void SvgStyleAttributes::add_path(
1082 const basegfx::B2DPolyPolygon
& rPath
,
1083 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
1084 const basegfx::utils::PointIndexSet
* pHelpPointIndices
) const
1088 // no geometry at all
1092 const basegfx::B2DRange
aGeoRange(rPath
.getB2DRange());
1094 if(aGeoRange
.isEmpty())
1096 // no geometry range
1100 const double fOpacity(getOpacity().solve(mrOwner
));
1102 if(basegfx::fTools::equalZero(fOpacity
))
1108 // check if it's a line
1109 const bool bNoWidth(basegfx::fTools::equalZero(aGeoRange
.getWidth()));
1110 const bool bNoHeight(basegfx::fTools::equalZero(aGeoRange
.getHeight()));
1111 const bool bIsTwoPointLine(1 == rPath
.count()
1112 && !rPath
.areControlPointsUsed()
1113 && 2 == rPath
.getB2DPolygon(0).count());
1114 const bool bIsLine(bIsTwoPointLine
|| bNoWidth
|| bNoHeight
);
1119 basegfx::B2DPolyPolygon
aPath(rPath
);
1120 const bool bNeedToCheckClipRule(SVGTokenPath
== mrOwner
.getType() || SVGTokenPolygon
== mrOwner
.getType());
1121 const bool bClipPathIsNonzero(bNeedToCheckClipRule
&& mbIsClipPathContent
&& FillRule_nonzero
== maClipRule
);
1122 const bool bFillRuleIsNonzero(bNeedToCheckClipRule
&& !mbIsClipPathContent
&& FillRule_nonzero
== getFillRule());
1124 if(bClipPathIsNonzero
|| bFillRuleIsNonzero
)
1126 if(getFill() || getSvgGradientNodeFill() || getSvgPatternNodeFill()) {
1127 // nonzero is wanted, solve geometrically (see description on basegfx)
1128 // basegfx::utils::createNonzeroConform() is expensive for huge paths
1129 // and is only needed if path will be filled later on
1130 aPath
= basegfx::utils::createNonzeroConform(aPath
);
1134 add_fill(aPath
, rTarget
, aGeoRange
);
1138 add_stroke(rPath
, rTarget
, aGeoRange
);
1140 // Svg supports markers for path, polygon, polyline and line
1141 if(SVGTokenPath
== mrOwner
.getType() || // path
1142 SVGTokenPolygon
== mrOwner
.getType() || // polygon, polyline
1143 SVGTokenLine
== mrOwner
.getType()) // line
1145 // try to add markers
1146 add_markers(rPath
, rTarget
, pHelpPointIndices
);
1150 void SvgStyleAttributes::add_postProcess(
1151 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
1152 const drawinglayer::primitive2d::Primitive2DContainer
& rSource
,
1153 const basegfx::B2DHomMatrix
* pTransform
) const
1155 if(!rSource
.empty())
1157 const double fOpacity(getOpacity().solve(mrOwner
));
1159 if(basegfx::fTools::equalZero(fOpacity
))
1164 drawinglayer::primitive2d::Primitive2DContainer
aSource(rSource
);
1166 if(basegfx::fTools::less(fOpacity
, 1.0))
1168 // embed in UnifiedTransparencePrimitive2D
1169 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1170 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1174 aSource
= drawinglayer::primitive2d::Primitive2DContainer
{ xRef
};
1179 // create embedding group element with transformation. This applies the given
1180 // transformation to the graphical content, but *not* to mask and/or clip (as needed)
1181 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1182 new drawinglayer::primitive2d::TransformPrimitive2D(
1186 aSource
= drawinglayer::primitive2d::Primitive2DContainer
{ xRef
};
1189 const SvgClipPathNode
* pClip
= accessClipPathXLink();
1192 // #i124852# transform may be needed when userSpaceOnUse
1193 pClip
->apply(aSource
, pTransform
);
1194 pClip
= pClip
->getSvgStyleAttributes()->accessClipPathXLink();
1197 if(!aSource
.empty()) // test again, applied clipPath may have lead to empty geometry
1199 const SvgMaskNode
* pMask
= accessMaskXLink();
1202 // #i124852# transform may be needed when userSpaceOnUse
1203 pMask
->apply(aSource
, pTransform
);
1207 // This is part of the SVG import of self-written SVGs from
1208 // Draw/Impress containing multiple Slides/Pages. To be able
1209 // to later 'break' these to multiple Pages if wanted, embed
1210 // each Page-Content in a identifiable Primitive Grouping
1212 // This is the case when the current Node is a GroupNode, has
1213 // class="Page" set, has a parent that also is a GroupNode
1214 // at which class="Slide" is set.
1215 // Multiple Slides/Pages are possible for Draw and Impress.
1216 if(SVGTokenG
== mrOwner
.getType() && mrOwner
.getClass())
1218 const OUString
aOwnerClass(*mrOwner
.getClass());
1220 if("Page" == aOwnerClass
)
1222 const SvgNode
* pParent(mrOwner
.getParent());
1224 if(nullptr != pParent
&& SVGTokenG
== pParent
->getType() && pParent
->getClass())
1226 const OUString
aParentClass(*pParent
->getClass());
1228 if("Slide" == aParentClass
)
1230 // embed to grouping primitive to identify the
1231 // Slide/Page information
1232 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1233 new drawinglayer::primitive2d::PageHierarchyPrimitive2D(
1236 aSource
= drawinglayer::primitive2d::Primitive2DContainer
{ xRef
};
1242 if(!aSource
.empty()) // test again, applied mask may have lead to empty geometry
1244 // append to current target
1245 rTarget
.append(aSource
);
1250 SvgStyleAttributes::SvgStyleAttributes(SvgNode
& rOwner
)
1252 mpCssStyleParent(nullptr),
1255 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
1258 mpSvgGradientNodeFill(nullptr),
1259 mpSvgGradientNodeStroke(nullptr),
1260 mpSvgPatternNodeFill(nullptr),
1261 mpSvgPatternNodeStroke(nullptr),
1263 maStrokeDasharray(),
1264 maStrokeDashOffset(),
1265 maStrokeLinecap(StrokeLinecap_notset
),
1266 maStrokeLinejoin(StrokeLinejoin_notset
),
1267 maStrokeMiterLimit(),
1272 maFontStretch(FontStretch_notset
),
1273 maFontStyle(FontStyle_notset
),
1274 maFontWeight(FontWeight_notset
),
1275 maTextAlign(TextAlign_notset
),
1276 maTextDecoration(TextDecoration_notset
),
1277 maTextAnchor(TextAnchor_notset
),
1280 maVisibility(Visibility_notset
),
1284 mpClipPathXLink(nullptr),
1286 mpMaskXLink(nullptr),
1287 maMarkerStartXLink(),
1288 mpMarkerStartXLink(nullptr),
1290 mpMarkerMidXLink(nullptr),
1292 mpMarkerEndXLink(nullptr),
1293 maFillRule(FillRule_notset
),
1294 maClipRule(FillRule_nonzero
),
1295 maBaselineShift(BaselineShift_Baseline
),
1296 maBaselineShiftNumber(0),
1297 maResolvingParent(30, 0),
1298 mbIsClipPathContent(SVGTokenClipPathNode
== mrOwner
.getType()),
1299 mbStrokeDasharraySet(false)
1301 const SvgStyleAttributes
* pParentStyle
= getParentStyle();
1302 if(!mbIsClipPathContent
)
1306 mbIsClipPathContent
= pParentStyle
->mbIsClipPathContent
;
1311 SvgStyleAttributes::~SvgStyleAttributes()
1315 void SvgStyleAttributes::parseStyleAttribute(
1317 const OUString
& aContent
,
1318 bool bCaseIndependent
)
1328 if(readSvgPaint(aContent
, aSvgPaint
, aURL
, bCaseIndependent
, aOpacity
))
1331 if(aOpacity
.isSet())
1333 setOpacity(SvgNumber(o3tl::clamp(aOpacity
.getNumber(), 0.0, 1.0)));
1336 else if(!aURL
.isEmpty())
1338 const SvgNode
* pNode
= mrOwner
.getDocument().findSvgNodeById(aURL
);
1342 if(SVGTokenLinearGradient
== pNode
->getType() || SVGTokenRadialGradient
== pNode
->getType())
1344 mpSvgGradientNodeFill
= static_cast< const SvgGradientNode
* >(pNode
);
1346 else if(SVGTokenPattern
== pNode
->getType())
1348 mpSvgPatternNodeFill
= static_cast< const SvgPatternNode
* >(pNode
);
1354 case SVGTokenFillOpacity
:
1358 if(readSingleNumber(aContent
, aNum
))
1360 maFillOpacity
= SvgNumber(o3tl::clamp(aNum
.getNumber(), 0.0, 1.0), aNum
.getUnit(), aNum
.isSet());
1364 case SVGTokenFillRule
:
1366 if(!aContent
.isEmpty())
1368 if(aContent
.match(commonStrings::aStrNonzero
))
1370 maFillRule
= FillRule_nonzero
;
1372 else if(aContent
.match(commonStrings::aStrEvenOdd
))
1374 maFillRule
= FillRule_evenodd
;
1379 case SVGTokenStroke
:
1385 if(readSvgPaint(aContent
, aSvgPaint
, aURL
, bCaseIndependent
, aOpacity
))
1387 maStroke
= aSvgPaint
;
1388 if(aOpacity
.isSet())
1390 setOpacity(SvgNumber(o3tl::clamp(aOpacity
.getNumber(), 0.0, 1.0)));
1393 else if(!aURL
.isEmpty())
1395 const SvgNode
* pNode
= mrOwner
.getDocument().findSvgNodeById(aURL
);
1399 if(SVGTokenLinearGradient
== pNode
->getType() || SVGTokenRadialGradient
== pNode
->getType())
1401 mpSvgGradientNodeStroke
= static_cast< const SvgGradientNode
* >(pNode
);
1403 else if(SVGTokenPattern
== pNode
->getType())
1405 mpSvgPatternNodeStroke
= static_cast< const SvgPatternNode
* >(pNode
);
1411 case SVGTokenStrokeDasharray
:
1413 if(!aContent
.isEmpty())
1415 SvgNumberVector aVector
;
1417 if(aContent
.startsWith("none"))
1419 // #121221# The special value 'none' needs to be handled
1420 // in the sense that *when* it is set, the parent shall not
1421 // be used. Before this was only dependent on the array being
1423 mbStrokeDasharraySet
= true;
1425 else if(readSvgNumberVector(aContent
, aVector
))
1427 maStrokeDasharray
= aVector
;
1432 case SVGTokenStrokeDashoffset
:
1436 if(readSingleNumber(aContent
, aNum
))
1438 if(aNum
.isPositive())
1440 maStrokeDashOffset
= aNum
;
1445 case SVGTokenStrokeLinecap
:
1447 if(!aContent
.isEmpty())
1449 if(aContent
.startsWith("butt"))
1451 setStrokeLinecap(StrokeLinecap_butt
);
1453 else if(aContent
.startsWith("round"))
1455 setStrokeLinecap(StrokeLinecap_round
);
1457 else if(aContent
.startsWith("square"))
1459 setStrokeLinecap(StrokeLinecap_square
);
1464 case SVGTokenStrokeLinejoin
:
1466 if(!aContent
.isEmpty())
1468 if(aContent
.startsWith("miter"))
1470 setStrokeLinejoin(StrokeLinejoin_miter
);
1472 else if(aContent
.startsWith("round"))
1474 setStrokeLinejoin(StrokeLinejoin_round
);
1476 else if(aContent
.startsWith("bevel"))
1478 setStrokeLinejoin(StrokeLinejoin_bevel
);
1483 case SVGTokenStrokeMiterlimit
:
1487 if(readSingleNumber(aContent
, aNum
))
1489 if(basegfx::fTools::moreOrEqual(aNum
.getNumber(), 1.0))
1490 { //readSingleNumber sets Unit_px as default, if unit is missing. Correct it here.
1491 maStrokeMiterLimit
= SvgNumber(aNum
.getNumber(), Unit_none
);
1496 case SVGTokenStrokeOpacity
:
1501 if(readSingleNumber(aContent
, aNum
))
1503 maStrokeOpacity
= SvgNumber(o3tl::clamp(aNum
.getNumber(), 0.0, 1.0), aNum
.getUnit(), aNum
.isSet());
1507 case SVGTokenStrokeWidth
:
1511 if(readSingleNumber(aContent
, aNum
))
1513 if(aNum
.isPositive())
1515 maStrokeWidth
= aNum
;
1520 case SVGTokenStopColor
:
1526 if(readSvgPaint(aContent
, aSvgPaint
, aURL
, bCaseIndependent
, aOpacity
))
1528 maStopColor
= aSvgPaint
;
1529 if(aOpacity
.isSet())
1531 setOpacity(SvgNumber(o3tl::clamp(aOpacity
.getNumber(), 0.0, 1.0)));
1536 case SVGTokenStopOpacity
:
1540 if(readSingleNumber(aContent
, aNum
))
1542 if(aNum
.isPositive())
1544 maStopOpacity
= aNum
;
1553 case SVGTokenFontFamily
:
1555 SvgStringVector aSvgStringVector
;
1557 if(readSvgStringVector(aContent
, aSvgStringVector
))
1559 maFontFamily
= aSvgStringVector
;
1563 case SVGTokenFontSize
:
1565 if(!aContent
.isEmpty())
1567 if(aContent
.startsWith("xx-small"))
1569 setFontSize(FontSize_xx_small
);
1571 else if(aContent
.startsWith("x-small"))
1573 setFontSize(FontSize_x_small
);
1575 else if(aContent
.startsWith("small"))
1577 setFontSize(FontSize_small
);
1579 else if(aContent
.startsWith("smaller"))
1581 setFontSize(FontSize_smaller
);
1583 else if(aContent
.startsWith("medium"))
1585 setFontSize(FontSize_medium
);
1587 else if(aContent
.startsWith("larger"))
1589 setFontSize(FontSize_larger
);
1591 else if(aContent
.startsWith("large"))
1593 setFontSize(FontSize_large
);
1595 else if(aContent
.startsWith("x-large"))
1597 setFontSize(FontSize_x_large
);
1599 else if(aContent
.startsWith("xx-large"))
1601 setFontSize(FontSize_xx_large
);
1603 else if(aContent
.startsWith("initial"))
1605 setFontSize(FontSize_initial
);
1611 if(readSingleNumber(aContent
, aNum
))
1613 maFontSizeNumber
= aNum
;
1619 case SVGTokenFontSizeAdjust
:
1623 case SVGTokenFontStretch
:
1625 if(!aContent
.isEmpty())
1627 if(aContent
.startsWith("normal"))
1629 setFontStretch(FontStretch_normal
);
1631 else if(aContent
.startsWith("wider"))
1633 setFontStretch(FontStretch_wider
);
1635 else if(aContent
.startsWith("narrower"))
1637 setFontStretch(FontStretch_narrower
);
1639 else if(aContent
.startsWith("ultra-condensed"))
1641 setFontStretch(FontStretch_ultra_condensed
);
1643 else if(aContent
.startsWith("extra-condensed"))
1645 setFontStretch(FontStretch_extra_condensed
);
1647 else if(aContent
.startsWith("condensed"))
1649 setFontStretch(FontStretch_condensed
);
1651 else if(aContent
.startsWith("semi-condensed"))
1653 setFontStretch(FontStretch_semi_condensed
);
1655 else if(aContent
.startsWith("semi-expanded"))
1657 setFontStretch(FontStretch_semi_expanded
);
1659 else if(aContent
.startsWith("expanded"))
1661 setFontStretch(FontStretch_expanded
);
1663 else if(aContent
.startsWith("extra-expanded"))
1665 setFontStretch(FontStretch_extra_expanded
);
1667 else if(aContent
.startsWith("ultra-expanded"))
1669 setFontStretch(FontStretch_ultra_expanded
);
1674 case SVGTokenFontStyle
:
1676 if(!aContent
.isEmpty())
1678 if(aContent
.startsWith("normal"))
1680 setFontStyle(FontStyle_normal
);
1682 else if(aContent
.startsWith("italic"))
1684 setFontStyle(FontStyle_italic
);
1686 else if(aContent
.startsWith("oblique"))
1688 setFontStyle(FontStyle_oblique
);
1693 case SVGTokenFontVariant
:
1697 case SVGTokenFontWeight
:
1699 if(!aContent
.isEmpty())
1701 if(aContent
.startsWith("100"))
1703 setFontWeight(FontWeight_100
);
1705 else if(aContent
.startsWith("200"))
1707 setFontWeight(FontWeight_200
);
1709 else if(aContent
.startsWith("300"))
1711 setFontWeight(FontWeight_300
);
1713 else if(aContent
.startsWith("400") || aContent
.startsWith("normal"))
1715 setFontWeight(FontWeight_400
);
1717 else if(aContent
.startsWith("500"))
1719 setFontWeight(FontWeight_500
);
1721 else if(aContent
.startsWith("600"))
1723 setFontWeight(FontWeight_600
);
1725 else if(aContent
.startsWith("700") || aContent
.startsWith("bold"))
1727 setFontWeight(FontWeight_700
);
1729 else if(aContent
.startsWith("800"))
1731 setFontWeight(FontWeight_800
);
1733 else if(aContent
.startsWith("900"))
1735 setFontWeight(FontWeight_900
);
1737 else if(aContent
.startsWith("bolder"))
1739 setFontWeight(FontWeight_bolder
);
1741 else if(aContent
.startsWith("lighter"))
1743 setFontWeight(FontWeight_lighter
);
1748 case SVGTokenDirection
:
1752 case SVGTokenLetterSpacing
:
1756 case SVGTokenTextDecoration
:
1758 if(!aContent
.isEmpty())
1760 if(aContent
.startsWith("none"))
1762 setTextDecoration(TextDecoration_none
);
1764 else if(aContent
.startsWith("underline"))
1766 setTextDecoration(TextDecoration_underline
);
1768 else if(aContent
.startsWith("overline"))
1770 setTextDecoration(TextDecoration_overline
);
1772 else if(aContent
.startsWith("line-through"))
1774 setTextDecoration(TextDecoration_line_through
);
1776 else if(aContent
.startsWith("blink"))
1778 setTextDecoration(TextDecoration_blink
);
1783 case SVGTokenUnicodeBidi
:
1787 case SVGTokenWordSpacing
:
1791 case SVGTokenTextAnchor
:
1793 if(!aContent
.isEmpty())
1795 if(aContent
.startsWith("start"))
1797 setTextAnchor(TextAnchor_start
);
1799 else if(aContent
.startsWith("middle"))
1801 setTextAnchor(TextAnchor_middle
);
1803 else if(aContent
.startsWith("end"))
1805 setTextAnchor(TextAnchor_end
);
1810 case SVGTokenTextAlign
:
1812 if(!aContent
.isEmpty())
1814 if(aContent
.startsWith("left"))
1816 setTextAlign(TextAlign_left
);
1818 else if(aContent
.startsWith("right"))
1820 setTextAlign(TextAlign_right
);
1822 else if(aContent
.startsWith("center"))
1824 setTextAlign(TextAlign_center
);
1826 else if(aContent
.startsWith("justify"))
1828 setTextAlign(TextAlign_justify
);
1839 if(readSvgPaint(aContent
, aSvgPaint
, aURL
, bCaseIndependent
, aOpacity
))
1841 maColor
= aSvgPaint
;
1842 if(aOpacity
.isSet())
1844 setOpacity(SvgNumber(o3tl::clamp(aOpacity
.getNumber(), 0.0, 1.0)));
1849 case SVGTokenOpacity
:
1853 if(readSingleNumber(aContent
, aNum
))
1855 setOpacity(SvgNumber(o3tl::clamp(aNum
.getNumber(), 0.0, 1.0), aNum
.getUnit(), aNum
.isSet()));
1859 case SVGTokenVisibility
:
1861 if(!aContent
.isEmpty())
1863 if(aContent
.startsWith("visible"))
1865 setVisibility(Visibility_visible
);
1867 else if(aContent
.startsWith("hidden"))
1869 setVisibility(Visibility_hidden
);
1871 else if(aContent
.startsWith("collapse"))
1873 setVisibility(Visibility_collapse
);
1875 else if(aContent
.startsWith("inherit"))
1877 setVisibility(Visibility_inherit
);
1892 case SVGTokenClipPathProperty
:
1894 readLocalUrl(aContent
, maClipPathXLink
);
1899 readLocalUrl(aContent
, maMaskXLink
);
1902 case SVGTokenClipRule
:
1904 if(!aContent
.isEmpty())
1906 if(aContent
.match(commonStrings::aStrNonzero
))
1908 maClipRule
= FillRule_nonzero
;
1910 else if(aContent
.match(commonStrings::aStrEvenOdd
))
1912 maClipRule
= FillRule_evenodd
;
1917 case SVGTokenMarker
:
1919 if(bCaseIndependent
)
1921 readLocalUrl(aContent
, maMarkerEndXLink
);
1922 maMarkerStartXLink
= maMarkerMidXLink
= maMarkerEndXLink
;
1926 case SVGTokenMarkerStart
:
1928 readLocalUrl(aContent
, maMarkerStartXLink
);
1931 case SVGTokenMarkerMid
:
1933 readLocalUrl(aContent
, maMarkerMidXLink
);
1936 case SVGTokenMarkerEnd
:
1938 readLocalUrl(aContent
, maMarkerEndXLink
);
1941 case SVGTokenDisplay
:
1943 // There may be display:none statements inside of style defines, e.g. the following line:
1944 // style="display:none"
1945 // taken from a svg example; this needs to be parsed and set at the owning node. Do not call
1946 // mrOwner.parseAttribute(...) here, this would lead to a recursion
1947 if(!aContent
.isEmpty())
1949 mrOwner
.setDisplay(getDisplayFromContent(aContent
));
1953 case SVGTokenBaselineShift
:
1955 if(!aContent
.isEmpty())
1959 if(aContent
.startsWith("sub"))
1961 setBaselineShift(BaselineShift_Sub
);
1963 else if(aContent
.startsWith("super"))
1965 setBaselineShift(BaselineShift_Super
);
1967 else if(readSingleNumber(aContent
, aNum
))
1969 maBaselineShiftNumber
= aNum
;
1971 if(Unit_percent
== aNum
.getUnit())
1973 setBaselineShift(BaselineShift_Percentage
);
1977 setBaselineShift(BaselineShift_Length
);
1982 // no BaselineShift or inherit (which is automatically)
1983 setBaselineShift(BaselineShift_Baseline
);
1995 // #i125258# ask if fill is a direct hard attribute (no hierarchy)
1996 bool SvgStyleAttributes::isFillSet() const
1998 if(mbIsClipPathContent
)
2002 else if(maFill
.isSet())
2010 const basegfx::BColor
* SvgStyleAttributes::getCurrentColor() const
2012 static basegfx::BColor
aBlack(0.0, 0.0, 0.0);
2013 const basegfx::BColor
*aColor
= getColor();
2020 const basegfx::BColor
* SvgStyleAttributes::getFill() const
2024 if(maFill
.isCurrent())
2026 return getCurrentColor();
2028 else if(maFill
.isOn())
2030 return &maFill
.getBColor();
2033 else if (!mpSvgGradientNodeFill
&& !mpSvgPatternNodeFill
)
2035 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2037 if (pSvgStyleAttributes
&& maResolvingParent
[0] < nStyleDepthLimit
)
2039 ++maResolvingParent
[0];
2040 const basegfx::BColor
* pFill
= pSvgStyleAttributes
->getFill();
2041 --maResolvingParent
[0];
2043 if(mbIsClipPathContent
)
2051 static basegfx::BColor
aBlack(0.0, 0.0, 0.0);
2065 const basegfx::BColor
* SvgStyleAttributes::getStroke() const
2067 if(maStroke
.isSet())
2069 if(maStroke
.isCurrent())
2071 return getCurrentColor();
2073 else if(maStroke
.isOn())
2075 return &maStroke
.getBColor();
2078 else if (!mpSvgGradientNodeStroke
&& !mpSvgPatternNodeStroke
)
2080 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2082 if (pSvgStyleAttributes
&& maResolvingParent
[1] < nStyleDepthLimit
)
2084 ++maResolvingParent
[1];
2085 auto ret
= pSvgStyleAttributes
->getStroke();
2086 --maResolvingParent
[1];
2094 const basegfx::BColor
& SvgStyleAttributes::getStopColor() const
2096 if(maStopColor
.isCurrent())
2098 return *getCurrentColor();
2102 return maStopColor
.getBColor();
2106 const SvgGradientNode
* SvgStyleAttributes::getSvgGradientNodeFill() const
2108 if(mpSvgGradientNodeFill
)
2110 return mpSvgGradientNodeFill
;
2112 else if (!maFill
.isSet() && !mpSvgPatternNodeFill
)
2114 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2116 if (pSvgStyleAttributes
&& maResolvingParent
[2] < nStyleDepthLimit
)
2118 ++maResolvingParent
[2];
2119 auto ret
= pSvgStyleAttributes
->getSvgGradientNodeFill();
2120 --maResolvingParent
[2];
2128 const SvgGradientNode
* SvgStyleAttributes::getSvgGradientNodeStroke() const
2130 if(mpSvgGradientNodeStroke
)
2132 return mpSvgGradientNodeStroke
;
2134 else if (!maStroke
.isSet() && !mpSvgPatternNodeStroke
)
2136 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2138 if (pSvgStyleAttributes
&& maResolvingParent
[3] < nStyleDepthLimit
)
2140 ++maResolvingParent
[3];
2141 auto ret
= pSvgStyleAttributes
->getSvgGradientNodeStroke();
2142 --maResolvingParent
[3];
2150 const SvgPatternNode
* SvgStyleAttributes::getSvgPatternNodeFill() const
2152 if(mpSvgPatternNodeFill
)
2154 return mpSvgPatternNodeFill
;
2156 else if (!maFill
.isSet() && !mpSvgGradientNodeFill
)
2158 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2160 if (pSvgStyleAttributes
&& maResolvingParent
[4] < nStyleDepthLimit
)
2162 ++maResolvingParent
[4];
2163 auto ret
= pSvgStyleAttributes
->getSvgPatternNodeFill();
2164 --maResolvingParent
[4];
2172 const SvgPatternNode
* SvgStyleAttributes::getSvgPatternNodeStroke() const
2174 if(mpSvgPatternNodeStroke
)
2176 return mpSvgPatternNodeStroke
;
2178 else if (!maStroke
.isSet() && !mpSvgGradientNodeStroke
)
2180 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2182 if (pSvgStyleAttributes
&& maResolvingParent
[5] < nStyleDepthLimit
)
2184 ++maResolvingParent
[5];
2185 auto ret
= pSvgStyleAttributes
->getSvgPatternNodeStroke();
2186 --maResolvingParent
[5];
2194 SvgNumber
SvgStyleAttributes::getStrokeWidth() const
2196 if(maStrokeWidth
.isSet())
2198 return maStrokeWidth
;
2201 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2203 if (pSvgStyleAttributes
&& maResolvingParent
[6] < nStyleDepthLimit
)
2205 ++maResolvingParent
[6];
2206 auto ret
= pSvgStyleAttributes
->getStrokeWidth();
2207 --maResolvingParent
[6];
2211 if(mbIsClipPathContent
)
2213 return SvgNumber(0.0);
2217 return SvgNumber(1.0);
2220 SvgNumber
SvgStyleAttributes::getStopOpacity() const
2222 if(maStopOpacity
.isSet())
2224 return maStopOpacity
;
2228 return SvgNumber(1.0);
2231 SvgNumber
SvgStyleAttributes::getFillOpacity() const
2233 if(maFillOpacity
.isSet())
2235 return maFillOpacity
;
2238 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2240 if (pSvgStyleAttributes
&& maResolvingParent
[7] < nStyleDepthLimit
)
2242 ++maResolvingParent
[7];
2243 auto ret
= pSvgStyleAttributes
->getFillOpacity();
2244 --maResolvingParent
[7];
2249 return SvgNumber(1.0);
2252 SvgNumber
SvgStyleAttributes::getOpacity() const
2254 if(maOpacity
.isSet())
2259 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2261 if (pSvgStyleAttributes
&& maResolvingParent
[8] < nStyleDepthLimit
)
2263 ++maResolvingParent
[8];
2264 auto ret
= pSvgStyleAttributes
->getOpacity();
2265 --maResolvingParent
[8];
2270 return SvgNumber(1.0);
2273 Visibility
SvgStyleAttributes::getVisibility() const
2275 if(Visibility_notset
== maVisibility
|| Visibility_inherit
== maVisibility
)
2277 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2279 if (pSvgStyleAttributes
&& maResolvingParent
[9] < nStyleDepthLimit
)
2281 ++maResolvingParent
[9];
2282 auto ret
= pSvgStyleAttributes
->getVisibility();
2283 --maResolvingParent
[9];
2286 //default is Visible
2287 return Visibility_visible
;
2290 // Visibility correction/exception for self-exported SVGs:
2291 // When Impress exports single or multi-page SVGs, it puts the
2292 // single slides into <g visibility="hidden">. Not sure why
2293 // this happens, but this leads (correctly) to empty imported
2295 // Thus, if Visibility_hidden is active and owner is a SVGTokenG
2296 // and it's parent is also a SVGTokenG and it has a Class 'SlideGroup'
2297 // set, check if we are an Impress export.
2298 // We are an Impress export if an SVG-Node titled 'ooo:meta_slides'
2300 // All together gives:
2301 if(Visibility_hidden
== maVisibility
2302 && SVGTokenG
== mrOwner
.getType()
2303 && nullptr != mrOwner
.getDocument().findSvgNodeById("ooo:meta_slides"))
2305 const SvgNode
* pParent(mrOwner
.getParent());
2307 if(nullptr != pParent
&& SVGTokenG
== pParent
->getType() && pParent
->getClass())
2309 const OUString
aClass(*pParent
->getClass());
2311 if("SlideGroup" == aClass
)
2313 // if we detect this exception,
2314 // override Visibility_hidden -> Visibility_visible
2315 return Visibility_visible
;
2320 return maVisibility
;
2323 FillRule
SvgStyleAttributes::getFillRule() const
2325 if(FillRule_notset
!= maFillRule
)
2330 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2332 if (pSvgStyleAttributes
&& maResolvingParent
[10] < nStyleDepthLimit
)
2334 ++maResolvingParent
[10];
2335 auto ret
= pSvgStyleAttributes
->getFillRule();
2336 --maResolvingParent
[10];
2340 // default is NonZero
2341 return FillRule_nonzero
;
2344 const SvgNumberVector
& SvgStyleAttributes::getStrokeDasharray() const
2346 if(!maStrokeDasharray
.empty())
2348 return maStrokeDasharray
;
2350 else if(mbStrokeDasharraySet
)
2352 // #121221# is set to empty *by purpose*, do not visit parent styles
2353 return maStrokeDasharray
;
2356 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2358 if (pSvgStyleAttributes
&& maResolvingParent
[11] < nStyleDepthLimit
)
2360 ++maResolvingParent
[11];
2361 const SvgNumberVector
& ret
= pSvgStyleAttributes
->getStrokeDasharray();
2362 --maResolvingParent
[11];
2367 return maStrokeDasharray
;
2370 SvgNumber
SvgStyleAttributes::getStrokeDashOffset() const
2372 if(maStrokeDashOffset
.isSet())
2374 return maStrokeDashOffset
;
2377 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2379 if (pSvgStyleAttributes
&& maResolvingParent
[12] < nStyleDepthLimit
)
2381 ++maResolvingParent
[12];
2382 auto ret
= pSvgStyleAttributes
->getStrokeDashOffset();
2383 --maResolvingParent
[12];
2388 return SvgNumber(0.0);
2391 StrokeLinecap
SvgStyleAttributes::getStrokeLinecap() const
2393 if(maStrokeLinecap
!= StrokeLinecap_notset
)
2395 return maStrokeLinecap
;
2398 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2400 if (pSvgStyleAttributes
&& maResolvingParent
[13] < nStyleDepthLimit
)
2402 ++maResolvingParent
[13];
2403 auto ret
= pSvgStyleAttributes
->getStrokeLinecap();
2404 --maResolvingParent
[13];
2408 // default is StrokeLinecap_butt
2409 return StrokeLinecap_butt
;
2412 StrokeLinejoin
SvgStyleAttributes::getStrokeLinejoin() const
2414 if(maStrokeLinejoin
!= StrokeLinejoin_notset
)
2416 return maStrokeLinejoin
;
2419 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2421 if (pSvgStyleAttributes
&& maResolvingParent
[14] < nStyleDepthLimit
)
2423 ++maResolvingParent
[14];
2424 auto ret
= pSvgStyleAttributes
->getStrokeLinejoin();
2425 --maResolvingParent
[14];
2429 // default is StrokeLinejoin_butt
2430 return StrokeLinejoin_miter
;
2433 SvgNumber
SvgStyleAttributes::getStrokeMiterLimit() const
2435 if(maStrokeMiterLimit
.isSet())
2437 return maStrokeMiterLimit
;
2440 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2442 if (pSvgStyleAttributes
&& maResolvingParent
[15] < nStyleDepthLimit
)
2444 ++maResolvingParent
[15];
2445 auto ret
= pSvgStyleAttributes
->getStrokeMiterLimit();
2446 --maResolvingParent
[15];
2451 return SvgNumber(4.0, Unit_none
);
2454 SvgNumber
SvgStyleAttributes::getStrokeOpacity() const
2456 if(maStrokeOpacity
.isSet())
2458 return maStrokeOpacity
;
2461 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2463 if (pSvgStyleAttributes
&& maResolvingParent
[16] < nStyleDepthLimit
)
2465 ++maResolvingParent
[16];
2466 auto ret
= pSvgStyleAttributes
->getStrokeOpacity();
2467 --maResolvingParent
[16];
2472 return SvgNumber(1.0);
2475 const SvgStringVector
& SvgStyleAttributes::getFontFamily() const
2477 if(!maFontFamily
.empty() && !maFontFamily
[0].startsWith("inherit"))
2479 return maFontFamily
;
2482 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2484 if (pSvgStyleAttributes
&& maResolvingParent
[17] < nStyleDepthLimit
)
2486 ++maResolvingParent
[17];
2487 const SvgStringVector
& ret
= pSvgStyleAttributes
->getFontFamily();
2488 --maResolvingParent
[17];
2493 return maFontFamily
;
2496 SvgNumber
SvgStyleAttributes::getFontSizeNumber() const
2498 // default size is 'medium' or 16px, which is equal to the default PPI used in svgio ( 96.0 )
2499 // converted to pixels
2500 const double aDefaultSize
= F_SVG_PIXEL_PER_INCH
/ 6.0;
2502 if(maFontSizeNumber
.isSet())
2504 if(!maFontSizeNumber
.isPositive())
2505 return aDefaultSize
;
2507 // #122524# Handle Unit_percent realtive to parent FontSize (see SVG1.1
2508 // spec 10.10 Font selection properties \91font-size\92, lastline (click 'normative
2509 // definition of the property')
2510 if(Unit_percent
== maFontSizeNumber
.getUnit())
2512 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2514 if(pSvgStyleAttributes
)
2516 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getFontSizeNumber();
2519 aParentNumber
.getNumber() * maFontSizeNumber
.getNumber() * 0.01,
2520 aParentNumber
.getUnit(),
2523 // if there's no parent style, set the font size based on the default size
2526 maFontSizeNumber
.getNumber() * aDefaultSize
/ 100.0, Unit_px
, true);
2528 else if((Unit_em
== maFontSizeNumber
.getUnit()) || (Unit_ex
== maFontSizeNumber
.getUnit()))
2530 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2532 if(pSvgStyleAttributes
)
2534 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getFontSizeNumber();
2537 aParentNumber
.getNumber() * maFontSizeNumber
.getNumber(),
2538 aParentNumber
.getUnit(),
2543 return maFontSizeNumber
;
2546 //In CSS2, the suggested scaling factor between adjacent indexes is 1.2
2549 case FontSize_notset
:
2551 case FontSize_xx_small
:
2553 return SvgNumber(aDefaultSize
/ 1.728);
2555 case FontSize_x_small
:
2557 return SvgNumber(aDefaultSize
/ 1.44);
2559 case FontSize_small
:
2561 return SvgNumber(aDefaultSize
/ 1.2);
2563 case FontSize_smaller
:
2565 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2566 if(pSvgStyleAttributes
)
2568 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getFontSizeNumber();
2569 return SvgNumber(aParentNumber
.getNumber() / 1.2, aParentNumber
.getUnit());
2573 case FontSize_medium
:
2574 case FontSize_initial
:
2576 return SvgNumber(aDefaultSize
);
2578 case FontSize_large
:
2580 return SvgNumber(aDefaultSize
* 1.2);
2582 case FontSize_larger
:
2584 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2585 if(pSvgStyleAttributes
)
2587 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getFontSizeNumber();
2588 return SvgNumber(aParentNumber
.getNumber() * 1.2, aParentNumber
.getUnit());
2592 case FontSize_x_large
:
2594 return SvgNumber(aDefaultSize
* 1.44);
2596 case FontSize_xx_large
:
2598 return SvgNumber(aDefaultSize
* 1.728);
2602 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2604 if(pSvgStyleAttributes
)
2606 return pSvgStyleAttributes
->getFontSizeNumber();
2609 return SvgNumber(aDefaultSize
);
2612 FontStretch
SvgStyleAttributes::getFontStretch() const
2614 if(maFontStretch
!= FontStretch_notset
)
2616 if(FontStretch_wider
!= maFontStretch
&& FontStretch_narrower
!= maFontStretch
)
2618 return maFontStretch
;
2622 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2624 if (pSvgStyleAttributes
&& maResolvingParent
[18] < nStyleDepthLimit
)
2626 ++maResolvingParent
[18];
2627 FontStretch aInherited
= pSvgStyleAttributes
->getFontStretch();
2628 --maResolvingParent
[18];
2630 if(FontStretch_wider
== maFontStretch
)
2632 aInherited
= getWider(aInherited
);
2634 else if(FontStretch_narrower
== maFontStretch
)
2636 aInherited
= getNarrower(aInherited
);
2642 // default is FontStretch_normal
2643 return FontStretch_normal
;
2646 FontStyle
SvgStyleAttributes::getFontStyle() const
2648 if(maFontStyle
!= FontStyle_notset
)
2653 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2655 if (pSvgStyleAttributes
&& maResolvingParent
[19] < nStyleDepthLimit
)
2657 ++maResolvingParent
[19];
2658 auto ret
= pSvgStyleAttributes
->getFontStyle();
2659 --maResolvingParent
[19];
2663 // default is FontStyle_normal
2664 return FontStyle_normal
;
2667 FontWeight
SvgStyleAttributes::getFontWeight() const
2669 if(maFontWeight
!= FontWeight_notset
)
2671 if(FontWeight_bolder
!= maFontWeight
&& FontWeight_lighter
!= maFontWeight
)
2673 return maFontWeight
;
2677 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2679 if (pSvgStyleAttributes
&& maResolvingParent
[20] < nStyleDepthLimit
)
2681 ++maResolvingParent
[20];
2682 FontWeight aInherited
= pSvgStyleAttributes
->getFontWeight();
2683 --maResolvingParent
[20];
2685 if(FontWeight_bolder
== maFontWeight
)
2687 aInherited
= getBolder(aInherited
);
2689 else if(FontWeight_lighter
== maFontWeight
)
2691 aInherited
= getLighter(aInherited
);
2697 // default is FontWeight_400 (FontWeight_normal)
2698 return FontWeight_400
;
2701 TextAlign
SvgStyleAttributes::getTextAlign() const
2703 if(maTextAlign
!= TextAlign_notset
)
2708 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2710 if (pSvgStyleAttributes
&& maResolvingParent
[21] < nStyleDepthLimit
)
2712 ++maResolvingParent
[21];
2713 auto ret
= pSvgStyleAttributes
->getTextAlign();
2714 --maResolvingParent
[21];
2718 // default is TextAlign_left
2719 return TextAlign_left
;
2722 const SvgStyleAttributes
* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const
2724 if(maTextDecoration
!= TextDecoration_notset
)
2729 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2731 if (pSvgStyleAttributes
&& maResolvingParent
[22] < nStyleDepthLimit
)
2733 ++maResolvingParent
[22];
2734 auto ret
= pSvgStyleAttributes
->getTextDecorationDefiningSvgStyleAttributes();
2735 --maResolvingParent
[22];
2743 TextDecoration
SvgStyleAttributes::getTextDecoration() const
2745 const SvgStyleAttributes
* pDefining
= getTextDecorationDefiningSvgStyleAttributes();
2749 return pDefining
->maTextDecoration
;
2753 // default is TextDecoration_none
2754 return TextDecoration_none
;
2758 TextAnchor
SvgStyleAttributes::getTextAnchor() const
2760 if(maTextAnchor
!= TextAnchor_notset
)
2762 return maTextAnchor
;
2765 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2767 if (pSvgStyleAttributes
&& maResolvingParent
[23] < nStyleDepthLimit
)
2769 ++maResolvingParent
[23];
2770 auto ret
= pSvgStyleAttributes
->getTextAnchor();
2771 --maResolvingParent
[23];
2775 // default is TextAnchor_start
2776 return TextAnchor_start
;
2779 const basegfx::BColor
* SvgStyleAttributes::getColor() const
2783 if(maColor
.isCurrent())
2785 OSL_ENSURE(false, "Svg error: current color uses current color (!)");
2788 else if(maColor
.isOn())
2790 return &maColor
.getBColor();
2795 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2797 if (pSvgStyleAttributes
&& maResolvingParent
[24] < nStyleDepthLimit
)
2799 ++maResolvingParent
[24];
2800 auto ret
= pSvgStyleAttributes
->getColor();
2801 --maResolvingParent
[24];
2809 OUString
const & SvgStyleAttributes::getClipPathXLink() const
2811 return maClipPathXLink
;
2814 const SvgClipPathNode
* SvgStyleAttributes::accessClipPathXLink() const
2816 if(!mpClipPathXLink
)
2818 const OUString
aClipPath(getClipPathXLink());
2820 if(!aClipPath
.isEmpty())
2822 const_cast< SvgStyleAttributes
* >(this)->mpClipPathXLink
= dynamic_cast< const SvgClipPathNode
* >(mrOwner
.getDocument().findSvgNodeById(aClipPath
));
2826 return mpClipPathXLink
;
2829 OUString
SvgStyleAttributes::getMaskXLink() const
2831 if(!maMaskXLink
.isEmpty())
2836 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2838 if (pSvgStyleAttributes
&& !pSvgStyleAttributes
->maMaskXLink
.isEmpty() && maResolvingParent
[25] < nStyleDepthLimit
)
2840 ++maResolvingParent
[25];
2841 auto ret
= pSvgStyleAttributes
->getMaskXLink();
2842 --maResolvingParent
[25];
2849 const SvgMaskNode
* SvgStyleAttributes::accessMaskXLink() const
2853 const OUString
aMask(getMaskXLink());
2855 if(!aMask
.isEmpty())
2857 const_cast< SvgStyleAttributes
* >(this)->mpMaskXLink
= dynamic_cast< const SvgMaskNode
* >(mrOwner
.getDocument().findSvgNodeById(aMask
));
2864 OUString
SvgStyleAttributes::getMarkerStartXLink() const
2866 if(!maMarkerStartXLink
.isEmpty())
2868 return maMarkerStartXLink
;
2871 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2873 if (pSvgStyleAttributes
&& maResolvingParent
[26] < nStyleDepthLimit
)
2875 ++maResolvingParent
[26];
2876 auto ret
= pSvgStyleAttributes
->getMarkerStartXLink();
2877 --maResolvingParent
[26];
2884 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerStartXLink() const
2886 if(!mpMarkerStartXLink
)
2888 const OUString
aMarker(getMarkerStartXLink());
2890 if(!aMarker
.isEmpty())
2892 const_cast< SvgStyleAttributes
* >(this)->mpMarkerStartXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerStartXLink()));
2896 return mpMarkerStartXLink
;
2899 OUString
SvgStyleAttributes::getMarkerMidXLink() const
2901 if(!maMarkerMidXLink
.isEmpty())
2903 return maMarkerMidXLink
;
2906 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2908 if (pSvgStyleAttributes
&& maResolvingParent
[27] < nStyleDepthLimit
)
2910 ++maResolvingParent
[27];
2911 auto ret
= pSvgStyleAttributes
->getMarkerMidXLink();
2912 --maResolvingParent
[27];
2919 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerMidXLink() const
2921 if(!mpMarkerMidXLink
)
2923 const OUString
aMarker(getMarkerMidXLink());
2925 if(!aMarker
.isEmpty())
2927 const_cast< SvgStyleAttributes
* >(this)->mpMarkerMidXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerMidXLink()));
2931 return mpMarkerMidXLink
;
2934 OUString
SvgStyleAttributes::getMarkerEndXLink() const
2936 if(!maMarkerEndXLink
.isEmpty())
2938 return maMarkerEndXLink
;
2941 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2943 if (pSvgStyleAttributes
&& maResolvingParent
[28] < nStyleDepthLimit
)
2945 ++maResolvingParent
[28];
2946 auto ret
= pSvgStyleAttributes
->getMarkerEndXLink();
2947 --maResolvingParent
[28];
2954 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerEndXLink() const
2956 if(!mpMarkerEndXLink
)
2958 const OUString
aMarker(getMarkerEndXLink());
2960 if(!aMarker
.isEmpty())
2962 const_cast< SvgStyleAttributes
* >(this)->mpMarkerEndXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerEndXLink()));
2966 return mpMarkerEndXLink
;
2969 SvgNumber
SvgStyleAttributes::getBaselineShiftNumber() const
2971 // #122524# Handle Unit_percent realtive to parent BaselineShift
2972 if(Unit_percent
== maBaselineShiftNumber
.getUnit())
2974 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2976 if (pSvgStyleAttributes
&& maResolvingParent
[29] < nStyleDepthLimit
)
2978 ++maResolvingParent
[29];
2979 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getBaselineShiftNumber();
2980 --maResolvingParent
[29];
2983 aParentNumber
.getNumber() * maBaselineShiftNumber
.getNumber() * 0.01,
2984 aParentNumber
.getUnit(),
2989 return maBaselineShiftNumber
;
2991 } // end of namespace svgreader
2992 } // end of namespace svgio
2994 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */