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 <svgstyleattributes.hxx>
21 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
22 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
23 #include <svgnode.hxx>
24 #include <svgdocument.hxx>
25 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
26 #include <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 <svgclippathnode.hxx>
33 #include <svgmasknode.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <svgmarkernode.hxx>
36 #include <basegfx/curve/b2dcubicbezier.hxx>
37 #include <svgpatternnode.hxx>
38 #include <drawinglayer/primitive2d/patternfillprimitive2d.hxx>
39 #include <basegfx/polygon/b2dpolygontools.hxx>
40 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/pagehierarchyprimitive2d.hxx>
47 basegfx::B2DLineJoin
StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin
)
49 if(StrokeLinejoin_round
== aStrokeLinejoin
)
51 return basegfx::B2DLineJoin::Round
;
53 else if(StrokeLinejoin_bevel
== aStrokeLinejoin
)
55 return basegfx::B2DLineJoin::Bevel
;
58 return basegfx::B2DLineJoin::Miter
;
61 css::drawing::LineCap
StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap
)
63 switch(aStrokeLinecap
)
65 default: /* StrokeLinecap_notset, StrokeLinecap_butt */
67 return css::drawing::LineCap_BUTT
;
69 case StrokeLinecap_round
:
71 return css::drawing::LineCap_ROUND
;
73 case StrokeLinecap_square
:
75 return css::drawing::LineCap_SQUARE
;
80 FontStretch
getWider(FontStretch aSource
)
84 case FontStretch_ultra_condensed
: aSource
= FontStretch_extra_condensed
; break;
85 case FontStretch_extra_condensed
: aSource
= FontStretch_condensed
; break;
86 case FontStretch_condensed
: aSource
= FontStretch_semi_condensed
; break;
87 case FontStretch_semi_condensed
: aSource
= FontStretch_normal
; break;
88 case FontStretch_normal
: aSource
= FontStretch_semi_expanded
; break;
89 case FontStretch_semi_expanded
: aSource
= FontStretch_expanded
; break;
90 case FontStretch_expanded
: aSource
= FontStretch_extra_expanded
; break;
91 case FontStretch_extra_expanded
: aSource
= FontStretch_ultra_expanded
; break;
98 FontStretch
getNarrower(FontStretch aSource
)
102 case FontStretch_extra_condensed
: aSource
= FontStretch_ultra_condensed
; break;
103 case FontStretch_condensed
: aSource
= FontStretch_extra_condensed
; break;
104 case FontStretch_semi_condensed
: aSource
= FontStretch_condensed
; break;
105 case FontStretch_normal
: aSource
= FontStretch_semi_condensed
; break;
106 case FontStretch_semi_expanded
: aSource
= FontStretch_normal
; break;
107 case FontStretch_expanded
: aSource
= FontStretch_semi_expanded
; break;
108 case FontStretch_extra_expanded
: aSource
= FontStretch_expanded
; break;
109 case FontStretch_ultra_expanded
: aSource
= FontStretch_extra_expanded
; break;
116 FontWeight
getBolder(FontWeight aSource
)
120 case FontWeight_100
: aSource
= FontWeight_200
; break;
121 case FontWeight_200
: aSource
= FontWeight_300
; break;
122 case FontWeight_300
: aSource
= FontWeight_400
; break;
123 case FontWeight_400
: aSource
= FontWeight_500
; break;
124 case FontWeight_500
: aSource
= FontWeight_600
; break;
125 case FontWeight_600
: aSource
= FontWeight_700
; break;
126 case FontWeight_700
: aSource
= FontWeight_800
; break;
127 case FontWeight_800
: aSource
= FontWeight_900
; break;
134 FontWeight
getLighter(FontWeight aSource
)
138 case FontWeight_200
: aSource
= FontWeight_100
; break;
139 case FontWeight_300
: aSource
= FontWeight_200
; break;
140 case FontWeight_400
: aSource
= FontWeight_300
; break;
141 case FontWeight_500
: aSource
= FontWeight_400
; break;
142 case FontWeight_600
: aSource
= FontWeight_500
; break;
143 case FontWeight_700
: aSource
= FontWeight_600
; break;
144 case FontWeight_800
: aSource
= FontWeight_700
; break;
145 case FontWeight_900
: aSource
= FontWeight_800
; break;
152 ::FontWeight
getVclFontWeight(FontWeight aSource
)
154 ::FontWeight
nRetval(WEIGHT_NORMAL
);
158 case FontWeight_100
: nRetval
= WEIGHT_ULTRALIGHT
; break;
159 case FontWeight_200
: nRetval
= WEIGHT_LIGHT
; break;
160 case FontWeight_300
: nRetval
= WEIGHT_SEMILIGHT
; break;
161 case FontWeight_400
: nRetval
= WEIGHT_NORMAL
; break;
162 case FontWeight_500
: nRetval
= WEIGHT_MEDIUM
; break;
163 case FontWeight_600
: nRetval
= WEIGHT_SEMIBOLD
; break;
164 case FontWeight_700
: nRetval
= WEIGHT_BOLD
; break;
165 case FontWeight_800
: nRetval
= WEIGHT_ULTRABOLD
; break;
166 case FontWeight_900
: nRetval
= WEIGHT_BLACK
; break;
173 void SvgStyleAttributes::readCssStyle(const OUString
& rCandidate
)
175 const sal_Int32
nLen(rCandidate
.getLength());
181 OUStringBuffer aTokenName
;
182 skip_char(rCandidate
, u
' ', nPos
, nLen
);
183 copyString(rCandidate
, nPos
, aTokenName
, nLen
);
185 if (aTokenName
.isEmpty())
187 // if no TokenName advance one by force to avoid death loop, continue
188 OSL_ENSURE(false, "Could not interpret on current position, advancing one byte (!)");
194 OUStringBuffer aTokenValue
;
195 skip_char(rCandidate
, u
' ', u
':', nPos
, nLen
);
196 copyToLimiter(rCandidate
, u
';', nPos
, aTokenValue
, nLen
);
197 skip_char(rCandidate
, u
' ', u
';', nPos
, nLen
);
199 if (aTokenValue
.isEmpty())
201 // no value - continue
205 // generate OUStrings
206 const OUString
aOUTokenName(aTokenName
.makeStringAndClear());
207 OUString
aOUTokenValue(aTokenValue
.makeStringAndClear());
209 // check for '!important' CssStyle mark, currently not supported
210 // but needs to be extracted for correct parsing
211 OUString
aTokenImportant("!important");
212 const sal_Int32
nIndexTokenImportant(aOUTokenValue
.indexOf(aTokenImportant
));
214 if(-1 != nIndexTokenImportant
)
216 // if there currently just remove it and remove spaces to have the value only
217 OUString aNewOUTokenValue
;
219 if(nIndexTokenImportant
> 0)
221 // copy content before token
222 aNewOUTokenValue
+= aOUTokenValue
.copy(0, nIndexTokenImportant
);
225 if(aOUTokenValue
.getLength() > nIndexTokenImportant
+ aTokenImportant
.getLength())
227 // copy content after token
228 aNewOUTokenValue
+= aOUTokenValue
.copy(nIndexTokenImportant
+ aTokenImportant
.getLength());
232 aOUTokenValue
= aNewOUTokenValue
.trim();
235 // valid token-value pair, parse it
236 parseStyleAttribute(StrToSVGToken(aOUTokenName
, true), aOUTokenValue
, true);
240 const SvgStyleAttributes
* SvgStyleAttributes::getParentStyle() const
242 if(getCssStyleParent())
244 return getCssStyleParent();
247 if(mrOwner
.supportsParentStyle() && mrOwner
.getParent())
249 return mrOwner
.getParent()->getSvgStyleAttributes();
255 void SvgStyleAttributes::add_text(
256 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
257 drawinglayer::primitive2d::Primitive2DContainer
const & rSource
) const
261 // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D
262 // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill())
263 // set. When another fill is used and also evtl. stroke is set it gets necessary to
264 // dismantle to geometry and add needed primitives
265 const basegfx::BColor
* pFill
= getFill();
266 const SvgGradientNode
* pFillGradient
= getSvgGradientNodeFill();
267 const SvgPatternNode
* pFillPattern
= getSvgPatternNodeFill();
268 const basegfx::BColor
* pStroke
= getStroke();
269 const SvgGradientNode
* pStrokeGradient
= getSvgGradientNodeStroke();
270 const SvgPatternNode
* pStrokePattern
= getSvgPatternNodeStroke();
271 basegfx::B2DPolyPolygon aMergedArea
;
273 if(pFillGradient
|| pFillPattern
|| pStroke
|| pStrokeGradient
|| pStrokePattern
)
275 // text geometry is needed, create
276 // use neutral ViewInformation and create LineGeometryExtractor2D
277 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
278 drawinglayer::processor2d::TextAsPolygonExtractor2D
aExtractor(aViewInformation2D
);
281 aExtractor
.process(rSource
);
284 const drawinglayer::processor2d::TextAsPolygonDataNodeVector
& rResult
= aExtractor
.getTarget();
285 const sal_uInt32
nResultCount(rResult
.size());
286 basegfx::B2DPolyPolygonVector aTextFillVector
;
287 aTextFillVector
.reserve(nResultCount
);
289 for(sal_uInt32
a(0); a
< nResultCount
; a
++)
291 const drawinglayer::processor2d::TextAsPolygonDataNode
& rCandidate
= rResult
[a
];
293 if(rCandidate
.getIsFilled())
295 aTextFillVector
.push_back(rCandidate
.getB2DPolyPolygon());
299 if(!aTextFillVector
.empty())
301 aMergedArea
= basegfx::utils::mergeToSinglePolyPolygon(aTextFillVector
);
305 const bool bStrokeUsed(pStroke
|| pStrokeGradient
|| pStrokePattern
);
307 // add fill. Use geometry even for simple color fill when stroke
308 // is used, else text rendering and the geometry-based stroke will
309 // normally not really match optically due to diverse system text
311 if(aMergedArea
.count() && (pFillGradient
|| pFillPattern
|| bStrokeUsed
))
313 // create text fill content based on geometry
314 add_fill(aMergedArea
, rTarget
, aMergedArea
.getB2DRange());
318 // add the already prepared primitives for single color fill
319 rTarget
.append(rSource
);
323 if(aMergedArea
.count() && bStrokeUsed
)
325 // create text stroke content
326 add_stroke(aMergedArea
, rTarget
, aMergedArea
.getB2DRange());
331 void SvgStyleAttributes::add_fillGradient(
332 const basegfx::B2DPolyPolygon
& rPath
,
333 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
334 const SvgGradientNode
& rFillGradient
,
335 const basegfx::B2DRange
& rGeoRange
) const
337 // create fill content
338 drawinglayer::primitive2d::SvgGradientEntryVector aSvgGradientEntryVector
;
340 // get the color stops
341 rFillGradient
.collectGradientEntries(aSvgGradientEntryVector
);
343 if(!aSvgGradientEntryVector
.empty())
345 basegfx::B2DHomMatrix aGeoToUnit
;
346 basegfx::B2DHomMatrix aGradientTransform
;
348 if(rFillGradient
.getGradientTransform())
350 aGradientTransform
= *rFillGradient
.getGradientTransform();
353 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
355 aGeoToUnit
.translate(-rGeoRange
.getMinX(), -rGeoRange
.getMinY());
356 aGeoToUnit
.scale(1.0 / rGeoRange
.getWidth(), 1.0 / rGeoRange
.getHeight());
359 if(SVGTokenLinearGradient
== rFillGradient
.getType())
361 basegfx::B2DPoint
aStart(0.0, 0.0);
362 basegfx::B2DPoint
aEnd(1.0, 0.0);
364 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
366 // all possible units
367 aStart
.setX(rFillGradient
.getX1().solve(mrOwner
, xcoordinate
));
368 aStart
.setY(rFillGradient
.getY1().solve(mrOwner
, ycoordinate
));
369 aEnd
.setX(rFillGradient
.getX2().solve(mrOwner
, xcoordinate
));
370 aEnd
.setY(rFillGradient
.getY2().solve(mrOwner
, ycoordinate
));
374 // fractions or percent relative to object bounds
375 const SvgNumber
X1(rFillGradient
.getX1());
376 const SvgNumber
Y1(rFillGradient
.getY1());
377 const SvgNumber
X2(rFillGradient
.getX2());
378 const SvgNumber
Y2(rFillGradient
.getY2());
380 aStart
.setX(Unit_percent
== X1
.getUnit() ? X1
.getNumber() * 0.01 : X1
.getNumber());
381 aStart
.setY(Unit_percent
== Y1
.getUnit() ? Y1
.getNumber() * 0.01 : Y1
.getNumber());
382 aEnd
.setX(Unit_percent
== X2
.getUnit() ? X2
.getNumber() * 0.01 : X2
.getNumber());
383 aEnd
.setY(Unit_percent
== Y2
.getUnit() ? Y2
.getNumber() * 0.01 : Y2
.getNumber());
386 if(!aGeoToUnit
.isIdentity())
388 aStart
*= aGeoToUnit
;
393 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
396 aSvgGradientEntryVector
,
399 userSpaceOnUse
!= rFillGradient
.getGradientUnits(),
400 rFillGradient
.getSpreadMethod()));
404 basegfx::B2DPoint
aStart(0.5, 0.5);
405 basegfx::B2DPoint aFocal
;
407 const SvgNumber
* pFx
= rFillGradient
.getFx();
408 const SvgNumber
* pFy
= rFillGradient
.getFy();
409 const bool bFocal(pFx
|| pFy
);
411 if(userSpaceOnUse
== rFillGradient
.getGradientUnits())
413 // all possible units
414 aStart
.setX(rFillGradient
.getCx().solve(mrOwner
, xcoordinate
));
415 aStart
.setY(rFillGradient
.getCy().solve(mrOwner
, ycoordinate
));
416 fRadius
= rFillGradient
.getR().solve(mrOwner
);
420 aFocal
.setX(pFx
? pFx
->solve(mrOwner
, xcoordinate
) : aStart
.getX());
421 aFocal
.setY(pFy
? pFy
->solve(mrOwner
, ycoordinate
) : aStart
.getY());
426 // fractions or percent relative to object bounds
427 const SvgNumber
Cx(rFillGradient
.getCx());
428 const SvgNumber
Cy(rFillGradient
.getCy());
429 const SvgNumber
R(rFillGradient
.getR());
431 aStart
.setX(Unit_percent
== Cx
.getUnit() ? Cx
.getNumber() * 0.01 : Cx
.getNumber());
432 aStart
.setY(Unit_percent
== Cy
.getUnit() ? Cy
.getNumber() * 0.01 : Cy
.getNumber());
433 fRadius
= (Unit_percent
== R
.getUnit()) ? R
.getNumber() * 0.01 : R
.getNumber();
437 aFocal
.setX(pFx
? (Unit_percent
== pFx
->getUnit() ? pFx
->getNumber() * 0.01 : pFx
->getNumber()) : aStart
.getX());
438 aFocal
.setY(pFy
? (Unit_percent
== pFy
->getUnit() ? pFy
->getNumber() * 0.01 : pFy
->getNumber()) : aStart
.getY());
442 if(!aGeoToUnit
.isIdentity())
444 aStart
*= aGeoToUnit
;
445 fRadius
= (aGeoToUnit
* basegfx::B2DVector(fRadius
, 0.0)).getLength();
449 aFocal
*= aGeoToUnit
;
454 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
457 aSvgGradientEntryVector
,
460 userSpaceOnUse
!= rFillGradient
.getGradientUnits(),
461 rFillGradient
.getSpreadMethod(),
462 bFocal
? &aFocal
: nullptr));
467 void SvgStyleAttributes::add_fillPatternTransform(
468 const basegfx::B2DPolyPolygon
& rPath
,
469 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
470 const SvgPatternNode
& rFillPattern
,
471 const basegfx::B2DRange
& rGeoRange
) const
473 // prepare fill polyPolygon with given pattern, check for patternTransform
474 if(rFillPattern
.getPatternTransform() && !rFillPattern
.getPatternTransform()->isIdentity())
476 // PatternTransform is active; Handle by filling the inverse transformed
477 // path and back-transforming the result
478 basegfx::B2DPolyPolygon
aPath(rPath
);
479 basegfx::B2DHomMatrix
aInv(*rFillPattern
.getPatternTransform());
480 drawinglayer::primitive2d::Primitive2DContainer aNewTarget
;
483 aPath
.transform(aInv
);
484 add_fillPattern(aPath
, aNewTarget
, rFillPattern
, aPath
.getB2DRange());
486 if(!aNewTarget
.empty())
489 new drawinglayer::primitive2d::TransformPrimitive2D(
490 *rFillPattern
.getPatternTransform(),
496 // no patternTransform, create fillPattern directly
497 add_fillPattern(rPath
, rTarget
, rFillPattern
, rGeoRange
);
501 void SvgStyleAttributes::add_fillPattern(
502 const basegfx::B2DPolyPolygon
& rPath
,
503 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
504 const SvgPatternNode
& rFillPattern
,
505 const basegfx::B2DRange
& rGeoRange
) const
507 // fill polyPolygon with given pattern
508 const drawinglayer::primitive2d::Primitive2DContainer
& rPrimitives
= rFillPattern
.getPatternPrimitives();
510 if(!rPrimitives
.empty())
512 double fTargetWidth(rGeoRange
.getWidth());
513 double fTargetHeight(rGeoRange
.getHeight());
515 if(fTargetWidth
> 0.0 && fTargetHeight
> 0.0)
517 // get relative values from pattern
523 rFillPattern
.getValuesRelative(fX
, fY
, fW
, fH
, rGeoRange
, mrOwner
);
525 if(fW
> 0.0 && fH
> 0.0)
527 // build the reference range relative to the rGeoRange
528 const basegfx::B2DRange
aReferenceRange(fX
, fY
, fX
+ fW
, fY
+ fH
);
530 // find out how the content is mapped to the reference range
531 basegfx::B2DHomMatrix aMapPrimitivesToUnitRange
;
532 const basegfx::B2DRange
* pViewBox
= rFillPattern
.getViewBox();
536 // use viewBox/preserveAspectRatio
537 const SvgAspectRatio
& rRatio
= rFillPattern
.getSvgAspectRatio();
538 const basegfx::B2DRange
aUnitRange(0.0, 0.0, 1.0, 1.0);
542 // let mapping be created from SvgAspectRatio
543 aMapPrimitivesToUnitRange
= rRatio
.createMapping(aUnitRange
, *pViewBox
);
547 // choose default mapping
548 aMapPrimitivesToUnitRange
= SvgAspectRatio::createLinearMapping(aUnitRange
, *pViewBox
);
553 // use patternContentUnits
554 const SvgUnits
aPatternContentUnits(rFillPattern
.getPatternContentUnits() ? *rFillPattern
.getPatternContentUnits() : userSpaceOnUse
);
556 if(userSpaceOnUse
== aPatternContentUnits
)
558 // create relative mapping to unit coordinates
559 aMapPrimitivesToUnitRange
.scale(1.0 / (fW
* fTargetWidth
), 1.0 / (fH
* fTargetHeight
));
563 aMapPrimitivesToUnitRange
.scale(1.0 / fW
, 1.0 / fH
);
567 // apply aMapPrimitivesToUnitRange to content when used
568 drawinglayer::primitive2d::Primitive2DContainer
aPrimitives(rPrimitives
);
570 if(!aMapPrimitivesToUnitRange
.isIdentity())
572 const drawinglayer::primitive2d::Primitive2DReference
xRef(
573 new drawinglayer::primitive2d::TransformPrimitive2D(
574 aMapPrimitivesToUnitRange
,
577 aPrimitives
= drawinglayer::primitive2d::Primitive2DContainer
{ xRef
};
580 // embed in PatternFillPrimitive2D
582 new drawinglayer::primitive2d::PatternFillPrimitive2D(
591 void SvgStyleAttributes::add_fill(
592 const basegfx::B2DPolyPolygon
& rPath
,
593 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
594 const basegfx::B2DRange
& rGeoRange
) const
596 const basegfx::BColor
* pFill
= getFill();
597 const SvgGradientNode
* pFillGradient
= getSvgGradientNodeFill();
598 const SvgPatternNode
* pFillPattern
= getSvgPatternNodeFill();
600 if(pFill
|| pFillGradient
|| pFillPattern
)
602 const double fFillOpacity(getFillOpacity().solve(mrOwner
));
604 if(basegfx::fTools::more(fFillOpacity
, 0.0))
606 drawinglayer::primitive2d::Primitive2DContainer aNewFill
;
610 // create fill content with SVG gradient primitive
611 add_fillGradient(rPath
, aNewFill
, *pFillGradient
, rGeoRange
);
613 else if(pFillPattern
)
615 // create fill content with SVG pattern primitive
616 add_fillPatternTransform(rPath
, aNewFill
, *pFillPattern
, rGeoRange
);
620 // create fill content
622 aNewFill
[0] = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
627 if(!aNewFill
.empty())
629 if(basegfx::fTools::less(fFillOpacity
, 1.0))
631 // embed in UnifiedTransparencePrimitive2D
633 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
635 1.0 - fFillOpacity
));
640 rTarget
.append(aNewFill
);
647 void SvgStyleAttributes::add_stroke(
648 const basegfx::B2DPolyPolygon
& rPath
,
649 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
650 const basegfx::B2DRange
& rGeoRange
) const
652 const basegfx::BColor
* pStroke
= getStroke();
653 const SvgGradientNode
* pStrokeGradient
= getSvgGradientNodeStroke();
654 const SvgPatternNode
* pStrokePattern
= getSvgPatternNodeStroke();
656 if(pStroke
|| pStrokeGradient
|| pStrokePattern
)
658 drawinglayer::primitive2d::Primitive2DContainer aNewStroke
;
659 const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner
));
661 if(basegfx::fTools::more(fStrokeOpacity
, 0.0))
663 // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all
664 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner
) : 1.0);
666 if(basegfx::fTools::more(fStrokeWidth
, 0.0))
668 drawinglayer::primitive2d::Primitive2DReference aNewLinePrimitive
;
670 // if we have a line with two identical points it is not really a line,
671 // but used by SVG sometimes to paint a single dot.In that case, create
672 // the geometry for a single dot
673 if(1 == rPath
.count())
675 const basegfx::B2DPolygon
aSingle(rPath
.getB2DPolygon(0));
677 if(2 == aSingle
.count() && aSingle
.getB2DPoint(0).equal(aSingle
.getB2DPoint(1)))
679 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
680 basegfx::B2DPolyPolygon(
681 basegfx::utils::createPolygonFromCircle(
682 aSingle
.getB2DPoint(0),
683 fStrokeWidth
* (1.44 * 0.5))),
684 pStroke
? *pStroke
: basegfx::BColor(0.0, 0.0, 0.0));
688 if(!aNewLinePrimitive
.is())
690 // get LineJoin, LineCap and stroke array
691 const basegfx::B2DLineJoin
aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin()));
692 const css::drawing::LineCap
aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap()));
693 ::std::vector
< double > aDashArray
;
695 if(!getStrokeDasharray().empty())
697 aDashArray
= solveSvgNumberVector(getStrokeDasharray(), mrOwner
);
700 // convert svg:stroke-miterlimit to LineAttrute:mfMiterMinimumAngle
701 // The default needs to be set explicitly, because svg default <> Draw default
702 double fMiterMinimumAngle
;
703 if (getStrokeMiterLimit().isSet())
705 fMiterMinimumAngle
= 2.0 * asin(1.0/getStrokeMiterLimit().getNumber());
709 fMiterMinimumAngle
= 2.0 * asin(0.25); // 1.0/default 4.0
712 // todo: Handle getStrokeDashOffset()
714 // prepare line attribute
715 const drawinglayer::attribute::LineAttribute
aLineAttribute(
716 pStroke
? *pStroke
: basegfx::BColor(0.0, 0.0, 0.0),
722 if(aDashArray
.empty())
724 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
730 const drawinglayer::attribute::StrokeAttribute
aStrokeAttribute(aDashArray
);
732 aNewLinePrimitive
= new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
739 if(pStrokeGradient
|| pStrokePattern
)
741 // put primitive into Primitive2DReference and Primitive2DSequence
742 const drawinglayer::primitive2d::Primitive2DContainer aSeq
{ aNewLinePrimitive
};
744 // use neutral ViewInformation and create LineGeometryExtractor2D
745 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
746 drawinglayer::processor2d::LineGeometryExtractor2D
aExtractor(aViewInformation2D
);
749 aExtractor
.process(aSeq
);
751 // check for fill rsults
752 const basegfx::B2DPolyPolygonVector
& rLineFillVector(aExtractor
.getExtractedLineFills());
754 if(!rLineFillVector
.empty())
756 const basegfx::B2DPolyPolygon
aMergedArea(
757 basegfx::utils::mergeToSinglePolyPolygon(
760 if(aMergedArea
.count())
764 // create fill content with SVG gradient primitive. Use original GeoRange,
765 // e.g. from circle without LineWidth
766 add_fillGradient(aMergedArea
, aNewStroke
, *pStrokeGradient
, rGeoRange
);
768 else // if(pStrokePattern)
770 // create fill content with SVG pattern primitive. Use GeoRange
771 // from the expanded data, e.g. circle with extended geo by half linewidth
772 add_fillPatternTransform(aMergedArea
, aNewStroke
, *pStrokePattern
, aMergedArea
.getB2DRange());
779 aNewStroke
.push_back(aNewLinePrimitive
);
782 if(!aNewStroke
.empty())
784 if(basegfx::fTools::less(fStrokeOpacity
, 1.0))
786 // embed in UnifiedTransparencePrimitive2D
788 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
790 1.0 - fStrokeOpacity
));
795 rTarget
.append(aNewStroke
);
803 bool SvgStyleAttributes::prepare_singleMarker(
804 drawinglayer::primitive2d::Primitive2DContainer
& rMarkerPrimitives
,
805 basegfx::B2DHomMatrix
& rMarkerTransform
,
806 basegfx::B2DRange
& rClipRange
,
807 const SvgMarkerNode
& rMarker
) const
809 // reset return values
810 rMarkerTransform
.identity();
813 // get marker primitive representation
814 rMarkerPrimitives
= rMarker
.getMarkerPrimitives();
816 if(!rMarkerPrimitives
.empty())
818 basegfx::B2DRange
aPrimitiveRange(0.0, 0.0, 1.0, 1.0);
819 const basegfx::B2DRange
* pViewBox
= rMarker
.getViewBox();
823 aPrimitiveRange
= *pViewBox
;
826 if(aPrimitiveRange
.getWidth() > 0.0 && aPrimitiveRange
.getHeight() > 0.0)
828 double fTargetWidth(rMarker
.getMarkerWidth().isSet() ? rMarker
.getMarkerWidth().solve(mrOwner
, xcoordinate
) : 3.0);
829 double fTargetHeight(rMarker
.getMarkerHeight().isSet() ? rMarker
.getMarkerHeight().solve(mrOwner
, xcoordinate
) : 3.0);
830 const bool bStrokeWidth(SvgMarkerNode::strokeWidth
== rMarker
.getMarkerUnits());
831 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner
) : 1.0);
835 // relative to strokeWidth
836 fTargetWidth
*= fStrokeWidth
;
837 fTargetHeight
*= fStrokeWidth
;
840 if(fTargetWidth
> 0.0 && fTargetHeight
> 0.0)
843 const basegfx::B2DRange
aTargetRange(0.0, 0.0, fTargetWidth
, fTargetHeight
);
844 const SvgAspectRatio
& rRatio
= rMarker
.getSvgAspectRatio();
848 // let mapping be created from SvgAspectRatio
849 rMarkerTransform
= rRatio
.createMapping(aTargetRange
, aPrimitiveRange
);
851 if(rRatio
.isMeetOrSlice())
854 rClipRange
= aPrimitiveRange
;
863 // adapt to strokewidth if needed
864 rMarkerTransform
.scale(fStrokeWidth
, fStrokeWidth
);
869 // choose default mapping
870 rMarkerTransform
= SvgAspectRatio::createLinearMapping(aTargetRange
, aPrimitiveRange
);
874 // get and apply reference point. Initially it's in marker local coordinate system
875 basegfx::B2DPoint
aRefPoint(
876 rMarker
.getRefX().isSet() ? rMarker
.getRefX().solve(mrOwner
, xcoordinate
) : 0.0,
877 rMarker
.getRefY().isSet() ? rMarker
.getRefY().solve(mrOwner
, ycoordinate
) : 0.0);
879 // apply MarkerTransform to have it in mapped coordinates
880 aRefPoint
*= rMarkerTransform
;
882 // apply by moving RepPoint to (0.0)
883 rMarkerTransform
.translate(-aRefPoint
.getX(), -aRefPoint
.getY());
893 void SvgStyleAttributes::add_markers(
894 const basegfx::B2DPolyPolygon
& rPath
,
895 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
896 const basegfx::utils::PointIndexSet
* pHelpPointIndices
) const
898 // try to access linked markers
899 const SvgMarkerNode
* pStart
= accessMarkerStartXLink();
900 const SvgMarkerNode
* pMid
= accessMarkerMidXLink();
901 const SvgMarkerNode
* pEnd
= accessMarkerEndXLink();
903 if(pStart
|| pMid
|| pEnd
)
905 const sal_uInt32
nSubPathCount(rPath
.count());
909 // remember prepared marker; pStart, pMid and pEnd may all be equal when
910 // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end',
911 // see 'case SVGTokenMarker' in this file; thus in this case only one common
912 // marker in primitive form will be prepared
913 const SvgMarkerNode
* pPrepared
= nullptr;
915 // values for the prepared marker, results of prepare_singleMarker
916 drawinglayer::primitive2d::Primitive2DContainer aPreparedMarkerPrimitives
;
917 basegfx::B2DHomMatrix aPreparedMarkerTransform
;
918 basegfx::B2DRange aPreparedMarkerClipRange
;
920 for (sal_uInt32
a(0); a
< nSubPathCount
; a
++)
922 // iterate over sub-paths
923 const basegfx::B2DPolygon
aSubPolygonPath(rPath
.getB2DPolygon(a
));
924 const sal_uInt32
nSubPolygonPointCount(aSubPolygonPath
.count());
925 const bool bSubPolygonPathIsClosed(aSubPolygonPath
.isClosed());
927 if(nSubPolygonPointCount
)
929 // for each sub-path, create one marker per point (when closed, two markers
930 // need to pe created for the 1st point)
931 const sal_uInt32
nTargetMarkerCount(bSubPolygonPathIsClosed
? nSubPolygonPointCount
+ 1 : nSubPolygonPointCount
);
933 for (sal_uInt32
b(0); b
< nTargetMarkerCount
; b
++)
935 const bool bIsFirstMarker(!a
&& !b
);
936 const bool bIsLastMarker(nSubPathCount
- 1 == a
&& nTargetMarkerCount
- 1 == b
);
937 const SvgMarkerNode
* pNeeded
= nullptr;
941 // 1st point in 1st sub-polygon, use pStart
944 else if(bIsLastMarker
)
946 // last point in last sub-polygon, use pEnd
951 // anything in-between, use pMid
955 if(pHelpPointIndices
&& !pHelpPointIndices
->empty())
957 const basegfx::utils::PointIndexSet::const_iterator
aFound(
958 pHelpPointIndices
->find(basegfx::utils::PointIndex(a
, b
)));
960 if(aFound
!= pHelpPointIndices
->end())
962 // this point is a pure helper point; do not create a marker for it
969 // no marker needs to be created for this point
973 if(pPrepared
!= pNeeded
)
975 // if needed marker is not yet prepared, do it now
976 if(prepare_singleMarker(aPreparedMarkerPrimitives
, aPreparedMarkerTransform
, aPreparedMarkerClipRange
, *pNeeded
))
982 // error: could not prepare given marker
983 OSL_ENSURE(false, "OOps, could not prepare given marker as primitives (!)");
989 // prepare complete transform
990 basegfx::B2DHomMatrix
aCombinedTransform(aPreparedMarkerTransform
);
993 if(pPrepared
->getOrientAuto())
995 const sal_uInt32
nPointIndex(b
% nSubPolygonPointCount
);
997 // get entering and leaving tangents; this will search backward/forward
998 // in the polygon to find tangents unequal to zero, skipping empty edges
999 // see basegfx descriptions)
1000 // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker
1001 // and entering tangent for end marker. To achieve this (if wanted) it is possible
1002 // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker.
1003 // This is not done here, see comment 14 in task #1232379#
1004 // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute
1005 basegfx::B2DVector
aEntering(
1006 basegfx::utils::getTangentEnteringPoint(
1009 basegfx::B2DVector
aLeaving(
1010 basegfx::utils::getTangentLeavingPoint(
1013 const bool bEntering(!aEntering
.equalZero());
1014 const bool bLeaving(!aLeaving
.equalZero());
1016 if(bEntering
|| bLeaving
)
1018 basegfx::B2DVector
aSum(0.0, 0.0);
1022 aSum
+= aEntering
.normalize();
1027 aSum
+= aLeaving
.normalize();
1030 if(!aSum
.equalZero())
1032 const double fAngle(atan2(aSum
.getY(), aSum
.getX()));
1035 aCombinedTransform
.rotate(fAngle
);
1042 aCombinedTransform
.rotate(pPrepared
->getAngle());
1045 // get and apply target position
1046 const basegfx::B2DPoint
aPoint(aSubPolygonPath
.getB2DPoint(b
% nSubPolygonPointCount
));
1048 aCombinedTransform
.translate(aPoint
.getX(), aPoint
.getY());
1051 drawinglayer::primitive2d::Primitive2DReference
xMarker(
1052 new drawinglayer::primitive2d::TransformPrimitive2D(
1054 aPreparedMarkerPrimitives
));
1056 if(!aPreparedMarkerClipRange
.isEmpty())
1058 // marker needs to be clipped, it's bigger as the mapping
1059 basegfx::B2DPolyPolygon
aClipPolygon(basegfx::utils::createPolygonFromRect(aPreparedMarkerClipRange
));
1061 aClipPolygon
.transform(aCombinedTransform
);
1062 xMarker
= new drawinglayer::primitive2d::MaskPrimitive2D(
1064 drawinglayer::primitive2d::Primitive2DContainer
{ xMarker
});
1068 rTarget
.push_back(xMarker
);
1076 void SvgStyleAttributes::add_path(
1077 const basegfx::B2DPolyPolygon
& rPath
,
1078 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
1079 const basegfx::utils::PointIndexSet
* pHelpPointIndices
) const
1083 // no geometry at all
1087 const basegfx::B2DRange
aGeoRange(rPath
.getB2DRange());
1089 if(aGeoRange
.isEmpty())
1091 // no geometry range
1095 const double fOpacity(getOpacity().solve(mrOwner
));
1097 if(basegfx::fTools::equalZero(fOpacity
))
1103 // check if it's a line
1104 const bool bNoWidth(basegfx::fTools::equalZero(aGeoRange
.getWidth()));
1105 const bool bNoHeight(basegfx::fTools::equalZero(aGeoRange
.getHeight()));
1106 const bool bIsTwoPointLine(1 == rPath
.count()
1107 && !rPath
.areControlPointsUsed()
1108 && 2 == rPath
.getB2DPolygon(0).count());
1109 const bool bIsLine(bIsTwoPointLine
|| bNoWidth
|| bNoHeight
);
1114 basegfx::B2DPolyPolygon
aPath(rPath
);
1115 const bool bNeedToCheckClipRule(SVGTokenPath
== mrOwner
.getType() || SVGTokenPolygon
== mrOwner
.getType());
1116 const bool bClipPathIsNonzero(bNeedToCheckClipRule
&& mbIsClipPathContent
&& FillRule_nonzero
== maClipRule
);
1117 const bool bFillRuleIsNonzero(bNeedToCheckClipRule
&& !mbIsClipPathContent
&& FillRule_nonzero
== getFillRule());
1119 if(bClipPathIsNonzero
|| bFillRuleIsNonzero
)
1121 if(getFill() || getSvgGradientNodeFill() || getSvgPatternNodeFill()) {
1122 // nonzero is wanted, solve geometrically (see description on basegfx)
1123 // basegfx::utils::createNonzeroConform() is expensive for huge paths
1124 // and is only needed if path will be filled later on
1125 aPath
= basegfx::utils::createNonzeroConform(aPath
);
1129 add_fill(aPath
, rTarget
, aGeoRange
);
1133 add_stroke(rPath
, rTarget
, aGeoRange
);
1135 // Svg supports markers for path, polygon, polyline and line
1136 if(SVGTokenPath
== mrOwner
.getType() || // path
1137 SVGTokenPolygon
== mrOwner
.getType() || // polygon, polyline
1138 SVGTokenLine
== mrOwner
.getType()) // line
1140 // try to add markers
1141 add_markers(rPath
, rTarget
, pHelpPointIndices
);
1145 void SvgStyleAttributes::add_postProcess(
1146 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
1147 const drawinglayer::primitive2d::Primitive2DContainer
& rSource
,
1148 const basegfx::B2DHomMatrix
* pTransform
) const
1150 if(!rSource
.empty())
1152 const double fOpacity(getOpacity().solve(mrOwner
));
1154 if(basegfx::fTools::equalZero(fOpacity
))
1159 drawinglayer::primitive2d::Primitive2DContainer
aSource(rSource
);
1161 if(basegfx::fTools::less(fOpacity
, 1.0))
1163 // embed in UnifiedTransparencePrimitive2D
1164 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1165 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1169 aSource
= drawinglayer::primitive2d::Primitive2DContainer
{ xRef
};
1174 // create embedding group element with transformation. This applies the given
1175 // transformation to the graphical content, but *not* to mask and/or clip (as needed)
1176 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1177 new drawinglayer::primitive2d::TransformPrimitive2D(
1181 aSource
= drawinglayer::primitive2d::Primitive2DContainer
{ xRef
};
1184 const SvgClipPathNode
* pClip
= accessClipPathXLink();
1187 // #i124852# transform may be needed when userSpaceOnUse
1188 pClip
->apply(aSource
, pTransform
);
1189 pClip
= pClip
->getSvgStyleAttributes()->accessClipPathXLink();
1192 if(!aSource
.empty()) // test again, applied clipPath may have lead to empty geometry
1194 const SvgMaskNode
* pMask
= accessMaskXLink();
1197 // #i124852# transform may be needed when userSpaceOnUse
1198 pMask
->apply(aSource
, pTransform
);
1202 // This is part of the SVG import of self-written SVGs from
1203 // Draw/Impress containing multiple Slides/Pages. To be able
1204 // to later 'break' these to multiple Pages if wanted, embed
1205 // each Page-Content in a identifiable Primitive Grouping
1207 // This is the case when the current Node is a GroupNode, has
1208 // class="Page" set, has a parent that also is a GroupNode
1209 // at which class="Slide" is set.
1210 // Multiple Slides/Pages are possible for Draw and Impress.
1211 if(SVGTokenG
== mrOwner
.getType() && mrOwner
.getClass())
1213 const OUString
aOwnerClass(*mrOwner
.getClass());
1215 if("Page" == aOwnerClass
)
1217 const SvgNode
* pParent(mrOwner
.getParent());
1219 if(nullptr != pParent
&& SVGTokenG
== pParent
->getType() && pParent
->getClass())
1221 const OUString
aParentClass(*pParent
->getClass());
1223 if("Slide" == aParentClass
)
1225 // embed to grouping primitive to identify the
1226 // Slide/Page information
1227 const drawinglayer::primitive2d::Primitive2DReference
xRef(
1228 new drawinglayer::primitive2d::PageHierarchyPrimitive2D(
1231 aSource
= drawinglayer::primitive2d::Primitive2DContainer
{ xRef
};
1237 if(!aSource
.empty()) // test again, applied mask may have lead to empty geometry
1239 // append to current target
1240 rTarget
.append(aSource
);
1245 SvgStyleAttributes::SvgStyleAttributes(SvgNode
& rOwner
)
1247 mpCssStyleParent(nullptr),
1250 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
1253 mpSvgGradientNodeFill(nullptr),
1254 mpSvgGradientNodeStroke(nullptr),
1255 mpSvgPatternNodeFill(nullptr),
1256 mpSvgPatternNodeStroke(nullptr),
1258 maStrokeDasharray(),
1259 maStrokeDashOffset(),
1260 maStrokeLinecap(StrokeLinecap_notset
),
1261 maStrokeLinejoin(StrokeLinejoin_notset
),
1262 maStrokeMiterLimit(),
1267 maFontStretch(FontStretch_notset
),
1268 maFontStyle(FontStyle_notset
),
1269 maFontWeight(FontWeight_notset
),
1270 maTextAlign(TextAlign_notset
),
1271 maTextDecoration(TextDecoration_notset
),
1272 maTextAnchor(TextAnchor_notset
),
1275 maVisibility(Visibility_notset
),
1279 mpClipPathXLink(nullptr),
1281 mpMaskXLink(nullptr),
1282 maMarkerStartXLink(),
1283 mpMarkerStartXLink(nullptr),
1285 mpMarkerMidXLink(nullptr),
1287 mpMarkerEndXLink(nullptr),
1288 maFillRule(FillRule_notset
),
1289 maClipRule(FillRule_nonzero
),
1290 maBaselineShift(BaselineShift_Baseline
),
1291 maBaselineShiftNumber(0),
1292 mbIsClipPathContent(SVGTokenClipPathNode
== mrOwner
.getType()),
1293 mbStrokeDasharraySet(false)
1295 const SvgStyleAttributes
* pParentStyle
= getParentStyle();
1296 if(!mbIsClipPathContent
)
1300 mbIsClipPathContent
= pParentStyle
->mbIsClipPathContent
;
1305 SvgStyleAttributes::~SvgStyleAttributes()
1309 void SvgStyleAttributes::parseStyleAttribute(
1311 const OUString
& aContent
,
1312 bool bCaseIndependent
)
1322 if(readSvgPaint(aContent
, aSvgPaint
, aURL
, bCaseIndependent
, aOpacity
))
1325 if(aOpacity
.isSet())
1327 setOpacity(SvgNumber(basegfx::clamp(aOpacity
.getNumber(), 0.0, 1.0)));
1330 else if(!aURL
.isEmpty())
1332 const SvgNode
* pNode
= mrOwner
.getDocument().findSvgNodeById(aURL
);
1336 if(SVGTokenLinearGradient
== pNode
->getType() || SVGTokenRadialGradient
== pNode
->getType())
1338 mpSvgGradientNodeFill
= static_cast< const SvgGradientNode
* >(pNode
);
1340 else if(SVGTokenPattern
== pNode
->getType())
1342 mpSvgPatternNodeFill
= static_cast< const SvgPatternNode
* >(pNode
);
1348 case SVGTokenFillOpacity
:
1352 if(readSingleNumber(aContent
, aNum
))
1354 maFillOpacity
= SvgNumber(basegfx::clamp(aNum
.getNumber(), 0.0, 1.0), aNum
.getUnit(), aNum
.isSet());
1358 case SVGTokenFillRule
:
1360 if(!aContent
.isEmpty())
1362 if(aContent
.match(commonStrings::aStrNonzero
))
1364 maFillRule
= FillRule_nonzero
;
1366 else if(aContent
.match(commonStrings::aStrEvenOdd
))
1368 maFillRule
= FillRule_evenodd
;
1373 case SVGTokenStroke
:
1379 if(readSvgPaint(aContent
, aSvgPaint
, aURL
, bCaseIndependent
, aOpacity
))
1381 maStroke
= aSvgPaint
;
1382 if(aOpacity
.isSet())
1384 setOpacity(SvgNumber(basegfx::clamp(aOpacity
.getNumber(), 0.0, 1.0)));
1387 else if(!aURL
.isEmpty())
1389 const SvgNode
* pNode
= mrOwner
.getDocument().findSvgNodeById(aURL
);
1393 if(SVGTokenLinearGradient
== pNode
->getType() || SVGTokenRadialGradient
== pNode
->getType())
1395 mpSvgGradientNodeStroke
= static_cast< const SvgGradientNode
* >(pNode
);
1397 else if(SVGTokenPattern
== pNode
->getType())
1399 mpSvgPatternNodeStroke
= static_cast< const SvgPatternNode
* >(pNode
);
1405 case SVGTokenStrokeDasharray
:
1407 if(!aContent
.isEmpty())
1409 SvgNumberVector aVector
;
1411 if(aContent
.startsWith("none"))
1413 // #121221# The special value 'none' needs to be handled
1414 // in the sense that *when* it is set, the parent shall not
1415 // be used. Before this was only dependent on the array being
1417 mbStrokeDasharraySet
= true;
1419 else if(readSvgNumberVector(aContent
, aVector
))
1421 maStrokeDasharray
= aVector
;
1426 case SVGTokenStrokeDashoffset
:
1430 if(readSingleNumber(aContent
, aNum
))
1432 if(aNum
.isPositive())
1434 maStrokeDashOffset
= aNum
;
1439 case SVGTokenStrokeLinecap
:
1441 if(!aContent
.isEmpty())
1443 if(aContent
.startsWith("butt"))
1445 setStrokeLinecap(StrokeLinecap_butt
);
1447 else if(aContent
.startsWith("round"))
1449 setStrokeLinecap(StrokeLinecap_round
);
1451 else if(aContent
.startsWith("square"))
1453 setStrokeLinecap(StrokeLinecap_square
);
1458 case SVGTokenStrokeLinejoin
:
1460 if(!aContent
.isEmpty())
1462 if(aContent
.startsWith("miter"))
1464 setStrokeLinejoin(StrokeLinejoin_miter
);
1466 else if(aContent
.startsWith("round"))
1468 setStrokeLinejoin(StrokeLinejoin_round
);
1470 else if(aContent
.startsWith("bevel"))
1472 setStrokeLinejoin(StrokeLinejoin_bevel
);
1477 case SVGTokenStrokeMiterlimit
:
1481 if(readSingleNumber(aContent
, aNum
))
1483 if(basegfx::fTools::moreOrEqual(aNum
.getNumber(), 1.0))
1484 { //readSingleNumber sets Unit_px as default, if unit is missing. Correct it here.
1485 maStrokeMiterLimit
= SvgNumber(aNum
.getNumber(), Unit_none
);
1490 case SVGTokenStrokeOpacity
:
1495 if(readSingleNumber(aContent
, aNum
))
1497 maStrokeOpacity
= SvgNumber(basegfx::clamp(aNum
.getNumber(), 0.0, 1.0), aNum
.getUnit(), aNum
.isSet());
1501 case SVGTokenStrokeWidth
:
1505 if(readSingleNumber(aContent
, aNum
))
1507 if(aNum
.isPositive())
1509 maStrokeWidth
= aNum
;
1514 case SVGTokenStopColor
:
1520 if(readSvgPaint(aContent
, aSvgPaint
, aURL
, bCaseIndependent
, aOpacity
))
1522 maStopColor
= aSvgPaint
;
1523 if(aOpacity
.isSet())
1525 setOpacity(SvgNumber(basegfx::clamp(aOpacity
.getNumber(), 0.0, 1.0)));
1530 case SVGTokenStopOpacity
:
1534 if(readSingleNumber(aContent
, aNum
))
1536 if(aNum
.isPositive())
1538 maStopOpacity
= aNum
;
1547 case SVGTokenFontFamily
:
1549 SvgStringVector aSvgStringVector
;
1551 if(readSvgStringVector(aContent
, aSvgStringVector
))
1553 maFontFamily
= aSvgStringVector
;
1557 case SVGTokenFontSize
:
1559 if(!aContent
.isEmpty())
1561 if(aContent
.startsWith("xx-small"))
1563 setFontSize(FontSize_xx_small
);
1565 else if(aContent
.startsWith("x-small"))
1567 setFontSize(FontSize_x_small
);
1569 else if(aContent
.startsWith("small"))
1571 setFontSize(FontSize_small
);
1573 else if(aContent
.startsWith("smaller"))
1575 setFontSize(FontSize_smaller
);
1577 else if(aContent
.startsWith("medium"))
1579 setFontSize(FontSize_medium
);
1581 else if(aContent
.startsWith("larger"))
1583 setFontSize(FontSize_larger
);
1585 else if(aContent
.startsWith("large"))
1587 setFontSize(FontSize_large
);
1589 else if(aContent
.startsWith("x-large"))
1591 setFontSize(FontSize_x_large
);
1593 else if(aContent
.startsWith("xx-large"))
1595 setFontSize(FontSize_xx_large
);
1597 else if(aContent
.startsWith("initial"))
1599 setFontSize(FontSize_initial
);
1605 if(readSingleNumber(aContent
, aNum
))
1607 maFontSizeNumber
= aNum
;
1613 case SVGTokenFontSizeAdjust
:
1617 case SVGTokenFontStretch
:
1619 if(!aContent
.isEmpty())
1621 if(aContent
.startsWith("normal"))
1623 setFontStretch(FontStretch_normal
);
1625 else if(aContent
.startsWith("wider"))
1627 setFontStretch(FontStretch_wider
);
1629 else if(aContent
.startsWith("narrower"))
1631 setFontStretch(FontStretch_narrower
);
1633 else if(aContent
.startsWith("ultra-condensed"))
1635 setFontStretch(FontStretch_ultra_condensed
);
1637 else if(aContent
.startsWith("extra-condensed"))
1639 setFontStretch(FontStretch_extra_condensed
);
1641 else if(aContent
.startsWith("condensed"))
1643 setFontStretch(FontStretch_condensed
);
1645 else if(aContent
.startsWith("semi-condensed"))
1647 setFontStretch(FontStretch_semi_condensed
);
1649 else if(aContent
.startsWith("semi-expanded"))
1651 setFontStretch(FontStretch_semi_expanded
);
1653 else if(aContent
.startsWith("expanded"))
1655 setFontStretch(FontStretch_expanded
);
1657 else if(aContent
.startsWith("extra-expanded"))
1659 setFontStretch(FontStretch_extra_expanded
);
1661 else if(aContent
.startsWith("ultra-expanded"))
1663 setFontStretch(FontStretch_ultra_expanded
);
1668 case SVGTokenFontStyle
:
1670 if(!aContent
.isEmpty())
1672 if(aContent
.startsWith("normal"))
1674 setFontStyle(FontStyle_normal
);
1676 else if(aContent
.startsWith("italic"))
1678 setFontStyle(FontStyle_italic
);
1680 else if(aContent
.startsWith("oblique"))
1682 setFontStyle(FontStyle_oblique
);
1687 case SVGTokenFontVariant
:
1691 case SVGTokenFontWeight
:
1693 if(!aContent
.isEmpty())
1695 if(aContent
.startsWith("100"))
1697 setFontWeight(FontWeight_100
);
1699 else if(aContent
.startsWith("200"))
1701 setFontWeight(FontWeight_200
);
1703 else if(aContent
.startsWith("300"))
1705 setFontWeight(FontWeight_300
);
1707 else if(aContent
.startsWith("400") || aContent
.startsWith("normal"))
1709 setFontWeight(FontWeight_400
);
1711 else if(aContent
.startsWith("500"))
1713 setFontWeight(FontWeight_500
);
1715 else if(aContent
.startsWith("600"))
1717 setFontWeight(FontWeight_600
);
1719 else if(aContent
.startsWith("700") || aContent
.startsWith("bold"))
1721 setFontWeight(FontWeight_700
);
1723 else if(aContent
.startsWith("800"))
1725 setFontWeight(FontWeight_800
);
1727 else if(aContent
.startsWith("900"))
1729 setFontWeight(FontWeight_900
);
1731 else if(aContent
.startsWith("bolder"))
1733 setFontWeight(FontWeight_bolder
);
1735 else if(aContent
.startsWith("lighter"))
1737 setFontWeight(FontWeight_lighter
);
1742 case SVGTokenDirection
:
1746 case SVGTokenLetterSpacing
:
1750 case SVGTokenTextDecoration
:
1752 if(!aContent
.isEmpty())
1754 if(aContent
.startsWith("none"))
1756 setTextDecoration(TextDecoration_none
);
1758 else if(aContent
.startsWith("underline"))
1760 setTextDecoration(TextDecoration_underline
);
1762 else if(aContent
.startsWith("overline"))
1764 setTextDecoration(TextDecoration_overline
);
1766 else if(aContent
.startsWith("line-through"))
1768 setTextDecoration(TextDecoration_line_through
);
1770 else if(aContent
.startsWith("blink"))
1772 setTextDecoration(TextDecoration_blink
);
1777 case SVGTokenUnicodeBidi
:
1781 case SVGTokenWordSpacing
:
1785 case SVGTokenTextAnchor
:
1787 if(!aContent
.isEmpty())
1789 if(aContent
.startsWith("start"))
1791 setTextAnchor(TextAnchor_start
);
1793 else if(aContent
.startsWith("middle"))
1795 setTextAnchor(TextAnchor_middle
);
1797 else if(aContent
.startsWith("end"))
1799 setTextAnchor(TextAnchor_end
);
1804 case SVGTokenTextAlign
:
1806 if(!aContent
.isEmpty())
1808 if(aContent
.startsWith("left"))
1810 setTextAlign(TextAlign_left
);
1812 else if(aContent
.startsWith("right"))
1814 setTextAlign(TextAlign_right
);
1816 else if(aContent
.startsWith("center"))
1818 setTextAlign(TextAlign_center
);
1820 else if(aContent
.startsWith("justify"))
1822 setTextAlign(TextAlign_justify
);
1833 if(readSvgPaint(aContent
, aSvgPaint
, aURL
, bCaseIndependent
, aOpacity
))
1835 maColor
= aSvgPaint
;
1836 if(aOpacity
.isSet())
1838 setOpacity(SvgNumber(basegfx::clamp(aOpacity
.getNumber(), 0.0, 1.0)));
1843 case SVGTokenOpacity
:
1847 if(readSingleNumber(aContent
, aNum
))
1849 setOpacity(SvgNumber(basegfx::clamp(aNum
.getNumber(), 0.0, 1.0), aNum
.getUnit(), aNum
.isSet()));
1853 case SVGTokenVisibility
:
1855 if(!aContent
.isEmpty())
1857 if(aContent
.startsWith("visible"))
1859 setVisibility(Visibility_visible
);
1861 else if(aContent
.startsWith("hidden"))
1863 setVisibility(Visibility_hidden
);
1865 else if(aContent
.startsWith("collapse"))
1867 setVisibility(Visibility_collapse
);
1869 else if(aContent
.startsWith("inherit"))
1871 setVisibility(Visibility_inherit
);
1886 case SVGTokenClipPathProperty
:
1888 readLocalUrl(aContent
, maClipPathXLink
);
1893 readLocalUrl(aContent
, maMaskXLink
);
1896 case SVGTokenClipRule
:
1898 if(!aContent
.isEmpty())
1900 if(aContent
.match(commonStrings::aStrNonzero
))
1902 maClipRule
= FillRule_nonzero
;
1904 else if(aContent
.match(commonStrings::aStrEvenOdd
))
1906 maClipRule
= FillRule_evenodd
;
1911 case SVGTokenMarker
:
1913 if(bCaseIndependent
)
1915 readLocalUrl(aContent
, maMarkerEndXLink
);
1916 maMarkerStartXLink
= maMarkerMidXLink
= maMarkerEndXLink
;
1920 case SVGTokenMarkerStart
:
1922 readLocalUrl(aContent
, maMarkerStartXLink
);
1925 case SVGTokenMarkerMid
:
1927 readLocalUrl(aContent
, maMarkerMidXLink
);
1930 case SVGTokenMarkerEnd
:
1932 readLocalUrl(aContent
, maMarkerEndXLink
);
1935 case SVGTokenDisplay
:
1937 // There may be display:none statements inside of style defines, e.g. the following line:
1938 // style="display:none"
1939 // taken from a svg example; this needs to be parsed and set at the owning node. Do not call
1940 // mrOwner.parseAttribute(...) here, this would lead to a recursion
1941 if(!aContent
.isEmpty())
1943 mrOwner
.setDisplay(getDisplayFromContent(aContent
));
1947 case SVGTokenBaselineShift
:
1949 if(!aContent
.isEmpty())
1953 if(aContent
.startsWith("sub"))
1955 setBaselineShift(BaselineShift_Sub
);
1957 else if(aContent
.startsWith("super"))
1959 setBaselineShift(BaselineShift_Super
);
1961 else if(readSingleNumber(aContent
, aNum
))
1963 maBaselineShiftNumber
= aNum
;
1965 if(Unit_percent
== aNum
.getUnit())
1967 setBaselineShift(BaselineShift_Percentage
);
1971 setBaselineShift(BaselineShift_Length
);
1976 // no BaselineShift or inherit (which is automatically)
1977 setBaselineShift(BaselineShift_Baseline
);
1989 // #i125258# ask if fill is a direct hard attribute (no hierarchy)
1990 bool SvgStyleAttributes::isFillSet() const
1992 if(mbIsClipPathContent
)
1996 else if(maFill
.isSet())
2004 const basegfx::BColor
* SvgStyleAttributes::getCurrentColor() const
2006 static basegfx::BColor
aBlack(0.0, 0.0, 0.0);
2007 const basegfx::BColor
*aColor
= getColor();
2014 const basegfx::BColor
* SvgStyleAttributes::getFill() const
2018 if(maFill
.isCurrent())
2020 return getCurrentColor();
2022 else if(maFill
.isOn())
2024 return &maFill
.getBColor();
2027 else if (!mpSvgGradientNodeFill
&& !mpSvgPatternNodeFill
)
2029 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2031 if(pSvgStyleAttributes
)
2033 const basegfx::BColor
* pFill
= pSvgStyleAttributes
->getFill();
2035 if(mbIsClipPathContent
)
2043 static basegfx::BColor
aBlack(0.0, 0.0, 0.0);
2057 const basegfx::BColor
* SvgStyleAttributes::getStroke() const
2059 if(maStroke
.isSet())
2061 if(maStroke
.isCurrent())
2063 return getCurrentColor();
2065 else if(maStroke
.isOn())
2067 return &maStroke
.getBColor();
2070 else if (!mpSvgGradientNodeStroke
&& !mpSvgPatternNodeStroke
)
2072 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2074 if(pSvgStyleAttributes
)
2076 return pSvgStyleAttributes
->getStroke();
2083 const basegfx::BColor
& SvgStyleAttributes::getStopColor() const
2085 if(maStopColor
.isCurrent())
2087 return *getCurrentColor();
2091 return maStopColor
.getBColor();
2095 const SvgGradientNode
* SvgStyleAttributes::getSvgGradientNodeFill() const
2097 if(mpSvgGradientNodeFill
)
2099 return mpSvgGradientNodeFill
;
2101 else if (!maFill
.isSet() && !mpSvgPatternNodeFill
)
2103 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2105 if(pSvgStyleAttributes
)
2107 return pSvgStyleAttributes
->getSvgGradientNodeFill();
2114 const SvgGradientNode
* SvgStyleAttributes::getSvgGradientNodeStroke() const
2116 if(mpSvgGradientNodeStroke
)
2118 return mpSvgGradientNodeStroke
;
2120 else if (!maStroke
.isSet() && !mpSvgPatternNodeStroke
)
2122 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2124 if(pSvgStyleAttributes
)
2126 return pSvgStyleAttributes
->getSvgGradientNodeStroke();
2133 const SvgPatternNode
* SvgStyleAttributes::getSvgPatternNodeFill() const
2135 if(mpSvgPatternNodeFill
)
2137 return mpSvgPatternNodeFill
;
2139 else if (!maFill
.isSet() && !mpSvgGradientNodeFill
)
2141 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2143 if(pSvgStyleAttributes
)
2145 return pSvgStyleAttributes
->getSvgPatternNodeFill();
2152 const SvgPatternNode
* SvgStyleAttributes::getSvgPatternNodeStroke() const
2154 if(mpSvgPatternNodeStroke
)
2156 return mpSvgPatternNodeStroke
;
2158 else if (!maStroke
.isSet() && !mpSvgGradientNodeStroke
)
2160 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2162 if(pSvgStyleAttributes
)
2164 return pSvgStyleAttributes
->getSvgPatternNodeStroke();
2171 SvgNumber
SvgStyleAttributes::getStrokeWidth() const
2173 if(maStrokeWidth
.isSet())
2175 return maStrokeWidth
;
2178 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2180 if(pSvgStyleAttributes
)
2182 return pSvgStyleAttributes
->getStrokeWidth();
2185 if(mbIsClipPathContent
)
2187 return SvgNumber(0.0);
2191 return SvgNumber(1.0);
2194 SvgNumber
SvgStyleAttributes::getStopOpacity() const
2196 if(maStopOpacity
.isSet())
2198 return maStopOpacity
;
2202 return SvgNumber(1.0);
2205 SvgNumber
SvgStyleAttributes::getFillOpacity() const
2207 if(maFillOpacity
.isSet())
2209 return maFillOpacity
;
2212 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2214 if(pSvgStyleAttributes
)
2216 return pSvgStyleAttributes
->getFillOpacity();
2220 return SvgNumber(1.0);
2223 SvgNumber
SvgStyleAttributes::getOpacity() const
2225 if(maOpacity
.isSet())
2230 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2232 if(pSvgStyleAttributes
)
2234 return pSvgStyleAttributes
->getOpacity();
2238 return SvgNumber(1.0);
2241 Visibility
SvgStyleAttributes::getVisibility() const
2243 if(Visibility_notset
== maVisibility
|| Visibility_inherit
== maVisibility
)
2245 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2247 if(pSvgStyleAttributes
)
2249 return pSvgStyleAttributes
->getVisibility();
2251 //default is Visible
2252 return Visibility_visible
;
2255 // Visibility correction/exception for self-exported SVGs:
2256 // When Impress exports single or multi-page SVGs, it puts the
2257 // single slides into <g visibility="hidden">. Not sure why
2258 // whis happens, but this leads (correctly) to empty imported
2260 // Thus, if Visibility_hidden is active and owner is a SVGTokenG
2261 // and it's parent is also a SVGTokenG and it has a Class 'SlideGroup'
2262 // set, check if we are an Impress export.
2263 // We are an Impress export if an SVG-Node titled 'ooo:meta_slides'
2265 // All togehter gives:
2266 if(Visibility_hidden
== maVisibility
2267 && SVGTokenG
== mrOwner
.getType()
2268 && nullptr != mrOwner
.getDocument().findSvgNodeById("ooo:meta_slides"))
2270 const SvgNode
* pParent(mrOwner
.getParent());
2272 if(nullptr != pParent
&& SVGTokenG
== pParent
->getType() && pParent
->getClass())
2274 const OUString
aClass(*pParent
->getClass());
2276 if("SlideGroup" == aClass
)
2278 // if we detect this exception,
2279 // ovverride Visibility_hidden -> Visibility_visible
2280 return Visibility_visible
;
2285 return maVisibility
;
2288 FillRule
SvgStyleAttributes::getFillRule() const
2290 if(FillRule_notset
!= maFillRule
)
2295 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2297 if(pSvgStyleAttributes
)
2299 return pSvgStyleAttributes
->getFillRule();
2302 // default is NonZero
2303 return FillRule_nonzero
;
2306 const SvgNumberVector
& SvgStyleAttributes::getStrokeDasharray() const
2308 if(!maStrokeDasharray
.empty())
2310 return maStrokeDasharray
;
2312 else if(mbStrokeDasharraySet
)
2314 // #121221# is set to empty *by purpose*, do not visit parent styles
2315 return maStrokeDasharray
;
2318 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2320 if(pSvgStyleAttributes
)
2322 return pSvgStyleAttributes
->getStrokeDasharray();
2326 return maStrokeDasharray
;
2329 SvgNumber
SvgStyleAttributes::getStrokeDashOffset() const
2331 if(maStrokeDashOffset
.isSet())
2333 return maStrokeDashOffset
;
2336 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2338 if(pSvgStyleAttributes
)
2340 return pSvgStyleAttributes
->getStrokeDashOffset();
2344 return SvgNumber(0.0);
2347 StrokeLinecap
SvgStyleAttributes::getStrokeLinecap() const
2349 if(maStrokeLinecap
!= StrokeLinecap_notset
)
2351 return maStrokeLinecap
;
2354 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2356 if(pSvgStyleAttributes
)
2358 return pSvgStyleAttributes
->getStrokeLinecap();
2361 // default is StrokeLinecap_butt
2362 return StrokeLinecap_butt
;
2365 StrokeLinejoin
SvgStyleAttributes::getStrokeLinejoin() const
2367 if(maStrokeLinejoin
!= StrokeLinejoin_notset
)
2369 return maStrokeLinejoin
;
2372 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2374 if(pSvgStyleAttributes
)
2376 return pSvgStyleAttributes
->getStrokeLinejoin();
2379 // default is StrokeLinejoin_butt
2380 return StrokeLinejoin_miter
;
2383 SvgNumber
SvgStyleAttributes::getStrokeMiterLimit() const
2385 if(maStrokeMiterLimit
.isSet())
2387 return maStrokeMiterLimit
;
2390 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2392 if(pSvgStyleAttributes
)
2394 return pSvgStyleAttributes
->getStrokeMiterLimit();
2398 return SvgNumber(4.0, Unit_none
);
2401 SvgNumber
SvgStyleAttributes::getStrokeOpacity() const
2403 if(maStrokeOpacity
.isSet())
2405 return maStrokeOpacity
;
2408 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2410 if(pSvgStyleAttributes
)
2412 return pSvgStyleAttributes
->getStrokeOpacity();
2416 return SvgNumber(1.0);
2419 const SvgStringVector
& SvgStyleAttributes::getFontFamily() const
2421 if(!maFontFamily
.empty() && !maFontFamily
[0].startsWith("inherit"))
2423 return maFontFamily
;
2426 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2428 if(pSvgStyleAttributes
)
2430 return pSvgStyleAttributes
->getFontFamily();
2434 return maFontFamily
;
2437 SvgNumber
SvgStyleAttributes::getFontSizeNumber() const
2439 // default size is 'medium' or 16px, which is equal to the default PPI used in svgio ( 96.0 )
2440 // converted to pixels
2441 const double aDefaultSize
= F_SVG_PIXEL_PER_INCH
/ 6.0;
2443 if(maFontSizeNumber
.isSet())
2445 if(!maFontSizeNumber
.isPositive())
2446 return aDefaultSize
;
2448 // #122524# Handle Unit_percent realtive to parent FontSize (see SVG1.1
2449 // spec 10.10 Font selection properties \91font-size\92, lastline (click 'normative
2450 // definition of the property')
2451 if(Unit_percent
== maFontSizeNumber
.getUnit())
2453 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2455 if(pSvgStyleAttributes
)
2457 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getFontSizeNumber();
2460 aParentNumber
.getNumber() * maFontSizeNumber
.getNumber() * 0.01,
2461 aParentNumber
.getUnit(),
2464 // if there's no parent style, set the font size based on the default size
2467 maFontSizeNumber
.getNumber() * aDefaultSize
/ 100.0, Unit_px
, true);
2469 else if((Unit_em
== maFontSizeNumber
.getUnit()) || (Unit_ex
== maFontSizeNumber
.getUnit()))
2471 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2473 if(pSvgStyleAttributes
)
2475 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getFontSizeNumber();
2478 aParentNumber
.getNumber() * maFontSizeNumber
.getNumber(),
2479 aParentNumber
.getUnit(),
2484 return maFontSizeNumber
;
2487 //In CSS2, the suggested scaling factor between adjacent indexes is 1.2
2490 case FontSize_notset
:
2492 case FontSize_xx_small
:
2494 return SvgNumber(aDefaultSize
/ 1.728);
2496 case FontSize_x_small
:
2498 return SvgNumber(aDefaultSize
/ 1.44);
2500 case FontSize_small
:
2502 return SvgNumber(aDefaultSize
/ 1.2);
2504 case FontSize_smaller
:
2506 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2507 if(pSvgStyleAttributes
)
2509 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getFontSizeNumber();
2510 return SvgNumber(aParentNumber
.getNumber() / 1.2, aParentNumber
.getUnit());
2514 case FontSize_medium
:
2515 case FontSize_initial
:
2517 return SvgNumber(aDefaultSize
);
2519 case FontSize_large
:
2521 return SvgNumber(aDefaultSize
* 1.2);
2523 case FontSize_larger
:
2525 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2526 if(pSvgStyleAttributes
)
2528 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getFontSizeNumber();
2529 return SvgNumber(aParentNumber
.getNumber() * 1.2, aParentNumber
.getUnit());
2533 case FontSize_x_large
:
2535 return SvgNumber(aDefaultSize
* 1.44);
2537 case FontSize_xx_large
:
2539 return SvgNumber(aDefaultSize
* 1.728);
2543 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2545 if(pSvgStyleAttributes
)
2547 return pSvgStyleAttributes
->getFontSizeNumber();
2550 return SvgNumber(aDefaultSize
);
2553 FontStretch
SvgStyleAttributes::getFontStretch() const
2555 if(maFontStretch
!= FontStretch_notset
)
2557 if(FontStretch_wider
!= maFontStretch
&& FontStretch_narrower
!= maFontStretch
)
2559 return maFontStretch
;
2563 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2565 if(pSvgStyleAttributes
)
2567 FontStretch aInherited
= pSvgStyleAttributes
->getFontStretch();
2569 if(FontStretch_wider
== maFontStretch
)
2571 aInherited
= getWider(aInherited
);
2573 else if(FontStretch_narrower
== maFontStretch
)
2575 aInherited
= getNarrower(aInherited
);
2581 // default is FontStretch_normal
2582 return FontStretch_normal
;
2585 FontStyle
SvgStyleAttributes::getFontStyle() const
2587 if(maFontStyle
!= FontStyle_notset
)
2592 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2594 if(pSvgStyleAttributes
)
2596 return pSvgStyleAttributes
->getFontStyle();
2599 // default is FontStyle_normal
2600 return FontStyle_normal
;
2603 FontWeight
SvgStyleAttributes::getFontWeight() const
2605 if(maFontWeight
!= FontWeight_notset
)
2607 if(FontWeight_bolder
!= maFontWeight
&& FontWeight_lighter
!= maFontWeight
)
2609 return maFontWeight
;
2613 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2615 if(pSvgStyleAttributes
)
2617 FontWeight aInherited
= pSvgStyleAttributes
->getFontWeight();
2619 if(FontWeight_bolder
== maFontWeight
)
2621 aInherited
= getBolder(aInherited
);
2623 else if(FontWeight_lighter
== maFontWeight
)
2625 aInherited
= getLighter(aInherited
);
2631 // default is FontWeight_400 (FontWeight_normal)
2632 return FontWeight_400
;
2635 TextAlign
SvgStyleAttributes::getTextAlign() const
2637 if(maTextAlign
!= TextAlign_notset
)
2642 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2644 if(pSvgStyleAttributes
)
2646 return pSvgStyleAttributes
->getTextAlign();
2649 // default is TextAlign_left
2650 return TextAlign_left
;
2653 const SvgStyleAttributes
* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const
2655 if(maTextDecoration
!= TextDecoration_notset
)
2660 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2662 if(pSvgStyleAttributes
)
2664 return pSvgStyleAttributes
->getTextDecorationDefiningSvgStyleAttributes();
2671 TextDecoration
SvgStyleAttributes::getTextDecoration() const
2673 const SvgStyleAttributes
* pDefining
= getTextDecorationDefiningSvgStyleAttributes();
2677 return pDefining
->maTextDecoration
;
2681 // default is TextDecoration_none
2682 return TextDecoration_none
;
2686 TextAnchor
SvgStyleAttributes::getTextAnchor() const
2688 if(maTextAnchor
!= TextAnchor_notset
)
2690 return maTextAnchor
;
2693 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2695 if(pSvgStyleAttributes
)
2697 return pSvgStyleAttributes
->getTextAnchor();
2700 // default is TextAnchor_start
2701 return TextAnchor_start
;
2704 const basegfx::BColor
* SvgStyleAttributes::getColor() const
2708 if(maColor
.isCurrent())
2710 OSL_ENSURE(false, "Svg error: current color uses current color (!)");
2713 else if(maColor
.isOn())
2715 return &maColor
.getBColor();
2720 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2722 if(pSvgStyleAttributes
)
2724 return pSvgStyleAttributes
->getColor();
2731 OUString
const & SvgStyleAttributes::getClipPathXLink() const
2733 return maClipPathXLink
;
2736 const SvgClipPathNode
* SvgStyleAttributes::accessClipPathXLink() const
2738 if(!mpClipPathXLink
)
2740 const OUString
aClipPath(getClipPathXLink());
2742 if(!aClipPath
.isEmpty())
2744 const_cast< SvgStyleAttributes
* >(this)->mpClipPathXLink
= dynamic_cast< const SvgClipPathNode
* >(mrOwner
.getDocument().findSvgNodeById(aClipPath
));
2748 return mpClipPathXLink
;
2751 OUString
SvgStyleAttributes::getMaskXLink() const
2753 if(!maMaskXLink
.isEmpty())
2758 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2760 if(pSvgStyleAttributes
&& !pSvgStyleAttributes
->maMaskXLink
.isEmpty())
2762 return pSvgStyleAttributes
->getMaskXLink();
2768 const SvgMaskNode
* SvgStyleAttributes::accessMaskXLink() const
2772 const OUString
aMask(getMaskXLink());
2774 if(!aMask
.isEmpty())
2776 const_cast< SvgStyleAttributes
* >(this)->mpMaskXLink
= dynamic_cast< const SvgMaskNode
* >(mrOwner
.getDocument().findSvgNodeById(aMask
));
2783 OUString
SvgStyleAttributes::getMarkerStartXLink() const
2785 if(!maMarkerStartXLink
.isEmpty())
2787 return maMarkerStartXLink
;
2790 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2792 if(pSvgStyleAttributes
)
2794 return pSvgStyleAttributes
->getMarkerStartXLink();
2800 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerStartXLink() const
2802 if(!mpMarkerStartXLink
)
2804 const OUString
aMarker(getMarkerStartXLink());
2806 if(!aMarker
.isEmpty())
2808 const_cast< SvgStyleAttributes
* >(this)->mpMarkerStartXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerStartXLink()));
2812 return mpMarkerStartXLink
;
2815 OUString
SvgStyleAttributes::getMarkerMidXLink() const
2817 if(!maMarkerMidXLink
.isEmpty())
2819 return maMarkerMidXLink
;
2822 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2824 if(pSvgStyleAttributes
)
2826 return pSvgStyleAttributes
->getMarkerMidXLink();
2832 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerMidXLink() const
2834 if(!mpMarkerMidXLink
)
2836 const OUString
aMarker(getMarkerMidXLink());
2838 if(!aMarker
.isEmpty())
2840 const_cast< SvgStyleAttributes
* >(this)->mpMarkerMidXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerMidXLink()));
2844 return mpMarkerMidXLink
;
2847 OUString
SvgStyleAttributes::getMarkerEndXLink() const
2849 if(!maMarkerEndXLink
.isEmpty())
2851 return maMarkerEndXLink
;
2854 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2856 if(pSvgStyleAttributes
)
2858 return pSvgStyleAttributes
->getMarkerEndXLink();
2864 const SvgMarkerNode
* SvgStyleAttributes::accessMarkerEndXLink() const
2866 if(!mpMarkerEndXLink
)
2868 const OUString
aMarker(getMarkerEndXLink());
2870 if(!aMarker
.isEmpty())
2872 const_cast< SvgStyleAttributes
* >(this)->mpMarkerEndXLink
= dynamic_cast< const SvgMarkerNode
* >(mrOwner
.getDocument().findSvgNodeById(getMarkerEndXLink()));
2876 return mpMarkerEndXLink
;
2879 SvgNumber
SvgStyleAttributes::getBaselineShiftNumber() const
2881 // #122524# Handle Unit_percent realtive to parent BaselineShift
2882 if(Unit_percent
== maBaselineShiftNumber
.getUnit())
2884 const SvgStyleAttributes
* pSvgStyleAttributes
= getParentStyle();
2886 if(pSvgStyleAttributes
)
2888 const SvgNumber aParentNumber
= pSvgStyleAttributes
->getBaselineShiftNumber();
2891 aParentNumber
.getNumber() * maBaselineShiftNumber
.getNumber() * 0.01,
2892 aParentNumber
.getUnit(),
2897 return maBaselineShiftNumber
;
2899 } // end of namespace svgreader
2900 } // end of namespace svgio
2902 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */