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