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 230 : basegfx::B2DLineJoin StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin)
47 : {
48 230 : if(StrokeLinejoin_round == aStrokeLinejoin)
49 : {
50 30 : return basegfx::B2DLineJoin::Round;
51 : }
52 200 : else if(StrokeLinejoin_bevel == aStrokeLinejoin)
53 : {
54 0 : return basegfx::B2DLineJoin::Bevel;
55 : }
56 :
57 200 : return basegfx::B2DLineJoin::Miter;
58 : }
59 :
60 230 : com::sun::star::drawing::LineCap StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap)
61 : {
62 230 : switch(aStrokeLinecap)
63 : {
64 : default: /* StrokeLinecap_notset, StrokeLinecap_butt */
65 : {
66 192 : 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 3571 : void SvgStyleAttributes::readCssStyle(const OUString& rCandidate)
173 : {
174 3571 : const sal_Int32 nLen(rCandidate.getLength());
175 3571 : sal_Int32 nPos(0);
176 :
177 10768 : while(nPos < nLen)
178 : {
179 : // get TokenName
180 3626 : OUStringBuffer aTokenName;
181 3626 : skip_char(rCandidate, sal_Unicode(' '), nPos, nLen);
182 3626 : copyString(rCandidate, nPos, aTokenName, nLen);
183 :
184 3626 : 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 7252 : OUStringBuffer aTokenValue;
194 3626 : skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(':'), nPos, nLen);
195 3626 : copyToLimiter(rCandidate, sal_Unicode(';'), nPos, aTokenValue, nLen);
196 3626 : skip_char(rCandidate, sal_Unicode(' '), sal_Unicode(';'), nPos, nLen);
197 :
198 3626 : if (aTokenValue.isEmpty())
199 : {
200 : // no value - continue
201 0 : continue;
202 : }
203 :
204 : // generate OUStrings
205 7252 : const OUString aOUTokenName(aTokenName.makeStringAndClear());
206 7252 : OUString aOUTokenValue(aTokenValue.makeStringAndClear());
207 :
208 : // check for '!important' CssStyle mark, currently not supported
209 : // but needs to be extracted for correct parsing
210 7252 : OUString aTokenImportant("!important");
211 3626 : const sal_Int32 nIndexTokenImportant(aOUTokenValue.indexOf(aTokenImportant));
212 :
213 3626 : 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 3626 : parseStyleAttribute(aOUTokenName, StrToSVGToken(aOUTokenName, true), aOUTokenValue, true);
236 3626 : }
237 3571 : }
238 :
239 138056 : const SvgStyleAttributes* SvgStyleAttributes::getParentStyle() const
240 : {
241 138056 : if(getCssStyleParent())
242 : {
243 2124 : return getCssStyleParent();
244 : }
245 :
246 135932 : if(mrOwner.supportsParentStyle() && mrOwner.getParent())
247 : {
248 108871 : return mrOwner.getParent()->getSvgStyleAttributes();
249 : }
250 :
251 27061 : 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 2978 : void SvgStyleAttributes::add_fill(
595 : const basegfx::B2DPolyPolygon& rPath,
596 : drawinglayer::primitive2d::Primitive2DSequence& rTarget,
597 : const basegfx::B2DRange& rGeoRange) const
598 : {
599 2978 : const basegfx::BColor* pFill = getFill();
600 2978 : const SvgGradientNode* pFillGradient = getSvgGradientNodeFill();
601 2978 : const SvgPatternNode* pFillPattern = getSvgPatternNodeFill();
602 :
603 2978 : if(pFill || pFillGradient || pFillPattern)
604 : {
605 2892 : const double fFillOpacity(getFillOpacity().solve(mrOwner, length));
606 :
607 2892 : if(basegfx::fTools::more(fFillOpacity, 0.0))
608 : {
609 2892 : drawinglayer::primitive2d::Primitive2DSequence aNewFill;
610 :
611 2892 : if(pFillGradient)
612 : {
613 : // create fill content with SVG gradient primitive
614 1244 : add_fillGradient(rPath, aNewFill, *pFillGradient, rGeoRange);
615 : }
616 1648 : 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 1648 : aNewFill.realloc(1);
625 3296 : aNewFill[0] = new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
626 : rPath,
627 3296 : *pFill);
628 : }
629 :
630 2892 : if(aNewFill.hasElements())
631 : {
632 2892 : 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 2892 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewFill);
645 : }
646 2892 : }
647 : }
648 : }
649 2978 : }
650 :
651 2997 : void SvgStyleAttributes::add_stroke(
652 : const basegfx::B2DPolyPolygon& rPath,
653 : drawinglayer::primitive2d::Primitive2DSequence& rTarget,
654 : const basegfx::B2DRange& rGeoRange) const
655 : {
656 2997 : const basegfx::BColor* pStroke = getStroke();
657 2997 : const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke();
658 2997 : const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke();
659 :
660 2997 : if(pStroke || pStrokeGradient || pStrokePattern)
661 : {
662 230 : drawinglayer::primitive2d::Primitive2DSequence aNewStroke;
663 230 : const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner, length));
664 :
665 230 : 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 230 : const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner, length) : 1.0);
669 :
670 230 : if(basegfx::fTools::more(fStrokeWidth, 0.0))
671 : {
672 : // get LineJoin, LineCap and stroke array
673 230 : const basegfx::B2DLineJoin aB2DLineJoin(StrokeLinejoinToB2DLineJoin(getStrokeLinejoin()));
674 230 : const com::sun::star::drawing::LineCap aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap()));
675 230 : ::std::vector< double > aDashArray;
676 :
677 230 : if(!getStrokeDasharray().empty())
678 : {
679 15 : aDashArray = solveSvgNumberVector(getStrokeDasharray(), mrOwner, length);
680 : }
681 :
682 : // todo: Handle getStrokeDashOffset()
683 :
684 : // prepare line attribute
685 460 : 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 460 : aLineCap);
691 :
692 230 : if(aDashArray.empty())
693 : {
694 430 : aNewLinePrimitive = new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
695 : rPath,
696 430 : 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 230 : 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 230 : drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(aNewStroke, aNewLinePrimitive);
749 : }
750 :
751 230 : if(aNewStroke.hasElements())
752 : {
753 230 : 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 230 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewStroke);
766 : }
767 230 : }
768 : }
769 230 : }
770 : }
771 2997 : }
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 2524 : 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 2524 : const SvgMarkerNode* pStart = accessMarkerStartXLink();
870 2524 : const SvgMarkerNode* pMid = accessMarkerMidXLink();
871 2524 : const SvgMarkerNode* pEnd = accessMarkerEndXLink();
872 :
873 2524 : 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 2524 : }
1045 :
1046 2997 : void SvgStyleAttributes::add_path(
1047 : const basegfx::B2DPolyPolygon& rPath,
1048 : drawinglayer::primitive2d::Primitive2DSequence& rTarget,
1049 : const basegfx::tools::PointIndexSet* pHelpPointIndices) const
1050 : {
1051 2997 : if(!rPath.count())
1052 : {
1053 : // no geometry at all
1054 0 : return;
1055 : }
1056 :
1057 2997 : const basegfx::B2DRange aGeoRange(rPath.getB2DRange());
1058 :
1059 2997 : if(aGeoRange.isEmpty())
1060 : {
1061 : // no geometry range
1062 0 : return;
1063 : }
1064 :
1065 2997 : const double fOpacity(getOpacity().getNumber());
1066 :
1067 2997 : if(basegfx::fTools::equalZero(fOpacity))
1068 : {
1069 : // not visible
1070 0 : return;
1071 : }
1072 :
1073 : // check if it's a line
1074 2997 : const bool bNoWidth(basegfx::fTools::equalZero(aGeoRange.getWidth()));
1075 2997 : const bool bNoHeight(basegfx::fTools::equalZero(aGeoRange.getHeight()));
1076 2997 : const bool bIsTwoPointLine(1 == rPath.count()
1077 2484 : && !rPath.areControlPointsUsed()
1078 6836 : && 2 == rPath.getB2DPolygon(0).count());
1079 2997 : const bool bIsLine(bIsTwoPointLine || bNoWidth || bNoHeight);
1080 :
1081 2997 : if(!bIsLine)
1082 : {
1083 : // create fill
1084 2978 : basegfx::B2DPolyPolygon aPath(rPath);
1085 2978 : const bool bNeedToCheckClipRule(SVGTokenPath == mrOwner.getType() || SVGTokenPolygon == mrOwner.getType());
1086 2978 : const bool bClipPathIsNonzero(!bIsLine && bNeedToCheckClipRule && mbIsClipPathContent && FillRule_nonzero == maClipRule);
1087 2978 : const bool bFillRuleIsNonzero(!bIsLine && bNeedToCheckClipRule && !mbIsClipPathContent && FillRule_nonzero == getFillRule());
1088 :
1089 2978 : if(bClipPathIsNonzero || bFillRuleIsNonzero)
1090 : {
1091 2484 : if(getFill() || getSvgGradientNodeFill() || getSvgPatternNodeFill()) {
1092 : // nonzero is wanted, solve geometrically (see description on basegfx)
1093 : // basegfx::tools::createNonzeroConform() is expensive for huge paths
1094 : // and is only needed if path will be filled later on
1095 2434 : aPath = basegfx::tools::createNonzeroConform(aPath);
1096 : }
1097 : }
1098 :
1099 2978 : add_fill(aPath, rTarget, aGeoRange);
1100 : }
1101 :
1102 : // create stroke
1103 2997 : add_stroke(rPath, rTarget, aGeoRange);
1104 :
1105 : // Svg supports markers for path, polygon, polyline and line
1106 7168 : if(SVGTokenPath == mrOwner.getType() || // path
1107 3473 : SVGTokenPolygon == mrOwner.getType() || // polygon, polyline
1108 476 : SVGTokenLine == mrOwner.getType()) // line
1109 : {
1110 : // try to add markers
1111 2524 : add_markers(rPath, rTarget, pHelpPointIndices);
1112 : }
1113 : }
1114 :
1115 5006 : void SvgStyleAttributes::add_postProcess(
1116 : drawinglayer::primitive2d::Primitive2DSequence& rTarget,
1117 : const drawinglayer::primitive2d::Primitive2DSequence& rSource,
1118 : const basegfx::B2DHomMatrix* pTransform) const
1119 : {
1120 5006 : if(rSource.hasElements())
1121 : {
1122 5006 : const double fOpacity(getOpacity().getNumber());
1123 :
1124 5006 : if(basegfx::fTools::equalZero(fOpacity))
1125 : {
1126 5006 : return;
1127 : }
1128 :
1129 5006 : drawinglayer::primitive2d::Primitive2DSequence aSource(rSource);
1130 :
1131 5006 : if(basegfx::fTools::less(fOpacity, 1.0))
1132 : {
1133 : // embed in UnifiedTransparencePrimitive2D
1134 : const drawinglayer::primitive2d::Primitive2DReference xRef(
1135 : new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1136 : aSource,
1137 670 : 1.0 - fOpacity));
1138 :
1139 670 : aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
1140 : }
1141 :
1142 5006 : if(pTransform)
1143 : {
1144 : // create embedding group element with transformation. This applies the given
1145 : // transformation to the graphical content, but *not* to mask and/or clip (as needed)
1146 : const drawinglayer::primitive2d::Primitive2DReference xRef(
1147 : new drawinglayer::primitive2d::TransformPrimitive2D(
1148 : *pTransform,
1149 331 : aSource));
1150 :
1151 331 : aSource = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
1152 : }
1153 :
1154 5006 : if(!getClipPathXLink().isEmpty())
1155 : {
1156 : // try to access linked ClipPath
1157 156 : const SvgClipPathNode* mpClip = dynamic_cast< const SvgClipPathNode* >(mrOwner.getDocument().findSvgNodeById(getClipPathXLink()));
1158 :
1159 156 : if(mpClip)
1160 : {
1161 : // #i124852# transform may be needed when userSpaceOnUse
1162 156 : mpClip->apply(aSource, pTransform);
1163 : }
1164 : }
1165 :
1166 5006 : if(aSource.hasElements()) // test again, applied clipPath may have lead to empty geometry
1167 : {
1168 5006 : if(!getMaskXLink().isEmpty())
1169 : {
1170 : // try to access linked Mask
1171 153 : const SvgMaskNode* mpMask = dynamic_cast< const SvgMaskNode* >(mrOwner.getDocument().findSvgNodeById(getMaskXLink()));
1172 :
1173 153 : if(mpMask)
1174 : {
1175 : // #i124852# transform may be needed when userSpaceOnUse
1176 153 : mpMask->apply(aSource, pTransform);
1177 : }
1178 : }
1179 :
1180 5006 : if(aSource.hasElements()) // test again, applied mask may have lead to empty geometry
1181 : {
1182 : // append to current target
1183 5004 : drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aSource);
1184 : }
1185 5006 : }
1186 : }
1187 : }
1188 :
1189 14346 : SvgStyleAttributes::SvgStyleAttributes(SvgNode& rOwner)
1190 : : mrOwner(rOwner),
1191 : mpCssStyleParent(0),
1192 : maFill(),
1193 : maStroke(),
1194 : maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
1195 : maStrokeWidth(),
1196 : maStopOpacity(),
1197 : mpSvgGradientNodeFill(0),
1198 : mpSvgGradientNodeStroke(0),
1199 : mpSvgPatternNodeFill(0),
1200 : mpSvgPatternNodeStroke(0),
1201 : maFillOpacity(),
1202 : maStrokeDasharray(),
1203 : maStrokeDashOffset(),
1204 : maStrokeLinecap(StrokeLinecap_notset),
1205 : maStrokeLinejoin(StrokeLinejoin_notset),
1206 : maStrokeMiterLimit(),
1207 : maStrokeOpacity(),
1208 : maFontFamily(),
1209 : maFontSize(),
1210 : maFontStretch(FontStretch_notset),
1211 : maFontStyle(FontStyle_notset),
1212 : maFontVariant(FontVariant_notset),
1213 : maFontWeight(FontWeight_notset),
1214 : maTextAlign(TextAlign_notset),
1215 : maTextDecoration(TextDecoration_notset),
1216 : maTextAnchor(TextAnchor_notset),
1217 : maColor(),
1218 : maOpacity(1.0),
1219 : maVisibility(Visibility_visible),
1220 : maTitle(),
1221 : maDesc(),
1222 : maClipPathXLink(),
1223 : maMaskXLink(),
1224 : maMarkerStartXLink(),
1225 : mpMarkerStartXLink(0),
1226 : maMarkerMidXLink(),
1227 : mpMarkerMidXLink(0),
1228 : maMarkerEndXLink(),
1229 : mpMarkerEndXLink(0),
1230 : maFillRule(FillRule_notset),
1231 : maClipRule(FillRule_nonzero),
1232 : maBaselineShift(BaselineShift_Baseline),
1233 : maBaselineShiftNumber(0),
1234 14346 : mbIsClipPathContent(SVGTokenClipPathNode == mrOwner.getType()),
1235 28692 : mbStrokeDasharraySet(false)
1236 : {
1237 14346 : const SvgStyleAttributes* pParentStyle = getParentStyle();
1238 14346 : if(!mbIsClipPathContent)
1239 : {
1240 14253 : if(pParentStyle)
1241 : {
1242 13847 : mbIsClipPathContent = pParentStyle->mbIsClipPathContent;
1243 : }
1244 : }
1245 14346 : if(pParentStyle)
1246 : {
1247 13940 : maVisibility = pParentStyle->maVisibility;
1248 : }
1249 14346 : }
1250 :
1251 14346 : SvgStyleAttributes::~SvgStyleAttributes()
1252 : {
1253 14346 : }
1254 :
1255 32998 : void SvgStyleAttributes::parseStyleAttribute(
1256 : const OUString& /* rTokenName */,
1257 : SVGToken aSVGToken,
1258 : const OUString& aContent,
1259 : bool bCaseIndependent)
1260 : {
1261 32998 : switch(aSVGToken)
1262 : {
1263 : case SVGTokenFill:
1264 : {
1265 2868 : SvgPaint aSvgPaint;
1266 5736 : OUString aURL;
1267 :
1268 2868 : if(readSvgPaint(aContent, aSvgPaint, aURL, bCaseIndependent))
1269 : {
1270 1585 : setFill(aSvgPaint);
1271 : }
1272 1283 : else if(!aURL.isEmpty())
1273 : {
1274 1283 : const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL);
1275 :
1276 1283 : if(pNode)
1277 : {
1278 1283 : if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType())
1279 : {
1280 1283 : setSvgGradientNodeFill(static_cast< const SvgGradientNode* >(pNode));
1281 : }
1282 0 : else if(SVGTokenPattern == pNode->getType())
1283 : {
1284 0 : setSvgPatternNodeFill(static_cast< const SvgPatternNode* >(pNode));
1285 : }
1286 : }
1287 : }
1288 5736 : break;
1289 : }
1290 : case SVGTokenFillOpacity:
1291 : {
1292 1 : SvgNumber aNum;
1293 :
1294 1 : if(readSingleNumber(aContent, aNum))
1295 : {
1296 1 : setFillOpacity(SvgNumber(basegfx::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet()));
1297 : }
1298 1 : break;
1299 : }
1300 : case SVGTokenFillRule:
1301 : {
1302 21 : if(!aContent.isEmpty())
1303 : {
1304 21 : if(aContent.match(commonStrings::aStrNonzero))
1305 : {
1306 0 : maFillRule = FillRule_nonzero;
1307 : }
1308 21 : else if(aContent.match(commonStrings::aStrEvenOdd))
1309 : {
1310 21 : maFillRule = FillRule_evenodd;
1311 : }
1312 : }
1313 21 : break;
1314 : }
1315 : case SVGTokenStroke:
1316 : {
1317 230 : SvgPaint aSvgPaint;
1318 460 : OUString aURL;
1319 :
1320 230 : if(readSvgPaint(aContent, aSvgPaint, aURL, bCaseIndependent))
1321 : {
1322 230 : setStroke(aSvgPaint);
1323 : }
1324 0 : else if(!aURL.isEmpty())
1325 : {
1326 0 : const SvgNode* pNode = mrOwner.getDocument().findSvgNodeById(aURL);
1327 :
1328 0 : if(pNode)
1329 : {
1330 0 : if(SVGTokenLinearGradient == pNode->getType() || SVGTokenRadialGradient == pNode->getType())
1331 : {
1332 0 : setSvgGradientNodeStroke(static_cast< const SvgGradientNode* >(pNode));
1333 : }
1334 0 : else if(SVGTokenPattern == pNode->getType())
1335 : {
1336 0 : setSvgPatternNodeStroke(static_cast< const SvgPatternNode* >(pNode));
1337 : }
1338 : }
1339 : }
1340 460 : break;
1341 : }
1342 : case SVGTokenStrokeDasharray:
1343 : {
1344 15 : if(!aContent.isEmpty())
1345 : {
1346 15 : SvgNumberVector aVector;
1347 :
1348 15 : if(aContent.startsWith("none"))
1349 : {
1350 : // #121221# The special value 'none' needs to be handled
1351 : // in the sense that *when* it is set, the parent shall not
1352 : // be used. Before this was only dependent on the array being
1353 : // empty
1354 0 : setStrokeDasharraySet(true);
1355 : }
1356 15 : else if(readSvgNumberVector(aContent, aVector))
1357 : {
1358 15 : setStrokeDasharray(aVector);
1359 15 : }
1360 : }
1361 15 : break;
1362 : }
1363 : case SVGTokenStrokeDashoffset:
1364 : {
1365 0 : SvgNumber aNum;
1366 :
1367 0 : if(readSingleNumber(aContent, aNum))
1368 : {
1369 0 : if(aNum.isPositive())
1370 : {
1371 0 : setStrokeDashOffset(aNum);
1372 : }
1373 : }
1374 0 : break;
1375 : }
1376 : case SVGTokenStrokeLinecap:
1377 : {
1378 38 : if(!aContent.isEmpty())
1379 : {
1380 38 : if(aContent.startsWith("butt"))
1381 : {
1382 0 : setStrokeLinecap(StrokeLinecap_butt);
1383 : }
1384 38 : else if(aContent.startsWith("round"))
1385 : {
1386 38 : setStrokeLinecap(StrokeLinecap_round);
1387 : }
1388 0 : else if(aContent.startsWith("square"))
1389 : {
1390 0 : setStrokeLinecap(StrokeLinecap_square);
1391 : }
1392 : }
1393 38 : break;
1394 : }
1395 : case SVGTokenStrokeLinejoin:
1396 : {
1397 30 : if(!aContent.isEmpty())
1398 : {
1399 30 : if(aContent.startsWith("miter"))
1400 : {
1401 0 : setStrokeLinejoin(StrokeLinejoin_miter);
1402 : }
1403 30 : else if(aContent.startsWith("round"))
1404 : {
1405 30 : setStrokeLinejoin(StrokeLinejoin_round);
1406 : }
1407 0 : else if(aContent.startsWith("bevel"))
1408 : {
1409 0 : setStrokeLinejoin(StrokeLinejoin_bevel);
1410 : }
1411 : }
1412 30 : break;
1413 : }
1414 : case SVGTokenStrokeMiterlimit:
1415 : {
1416 0 : SvgNumber aNum;
1417 :
1418 0 : if(readSingleNumber(aContent, aNum))
1419 : {
1420 0 : if(aNum.isPositive())
1421 : {
1422 0 : setStrokeMiterLimit(aNum);
1423 : }
1424 : }
1425 0 : break;
1426 : }
1427 : case SVGTokenStrokeOpacity:
1428 : {
1429 0 : SvgNumber aNum;
1430 :
1431 0 : if(readSingleNumber(aContent, aNum))
1432 : {
1433 0 : if(aNum.isPositive())
1434 : {
1435 0 : setStrokeOpacity(aNum);
1436 : }
1437 : }
1438 0 : break;
1439 : }
1440 : case SVGTokenStrokeWidth:
1441 : {
1442 216 : SvgNumber aNum;
1443 :
1444 216 : if(readSingleNumber(aContent, aNum))
1445 : {
1446 216 : if(aNum.isPositive())
1447 : {
1448 216 : setStrokeWidth(aNum);
1449 : }
1450 : }
1451 216 : break;
1452 : }
1453 : case SVGTokenStopColor:
1454 : {
1455 3433 : SvgPaint aSvgPaint;
1456 6866 : OUString aURL;
1457 :
1458 3433 : if(readSvgPaint(aContent, aSvgPaint, aURL, bCaseIndependent))
1459 : {
1460 3433 : setStopColor(aSvgPaint);
1461 : }
1462 6866 : break;
1463 : }
1464 : case SVGTokenStopOpacity:
1465 : {
1466 0 : SvgNumber aNum;
1467 :
1468 0 : if(readSingleNumber(aContent, aNum))
1469 : {
1470 0 : if(aNum.isPositive())
1471 : {
1472 0 : setStopOpacity(aNum);
1473 : }
1474 : }
1475 0 : break;
1476 : }
1477 : case SVGTokenFont:
1478 : {
1479 0 : break;
1480 : }
1481 : case SVGTokenFontFamily:
1482 : {
1483 0 : SvgStringVector aSvgStringVector;
1484 :
1485 0 : if(readSvgStringVector(aContent, aSvgStringVector))
1486 : {
1487 0 : setFontFamily(aSvgStringVector);
1488 : }
1489 0 : break;
1490 : }
1491 : case SVGTokenFontSize:
1492 : {
1493 0 : SvgNumber aNum;
1494 :
1495 0 : if(readSingleNumber(aContent, aNum))
1496 : {
1497 0 : setFontSize(aNum);
1498 : }
1499 0 : break;
1500 : }
1501 : case SVGTokenFontSizeAdjust:
1502 : {
1503 0 : break;
1504 : }
1505 : case SVGTokenFontStretch:
1506 : {
1507 0 : if(!aContent.isEmpty())
1508 : {
1509 0 : if(aContent.startsWith("normal"))
1510 : {
1511 0 : setFontStretch(FontStretch_normal);
1512 : }
1513 0 : else if(aContent.startsWith("wider"))
1514 : {
1515 0 : setFontStretch(FontStretch_wider);
1516 : }
1517 0 : else if(aContent.startsWith("narrower"))
1518 : {
1519 0 : setFontStretch(FontStretch_narrower);
1520 : }
1521 0 : else if(aContent.startsWith("ultra-condensed"))
1522 : {
1523 0 : setFontStretch(FontStretch_ultra_condensed);
1524 : }
1525 0 : else if(aContent.startsWith("extra-condensed"))
1526 : {
1527 0 : setFontStretch(FontStretch_extra_condensed);
1528 : }
1529 0 : else if(aContent.startsWith("condensed"))
1530 : {
1531 0 : setFontStretch(FontStretch_condensed);
1532 : }
1533 0 : else if(aContent.startsWith("semi-condensed"))
1534 : {
1535 0 : setFontStretch(FontStretch_semi_condensed);
1536 : }
1537 0 : else if(aContent.startsWith("semi-expanded"))
1538 : {
1539 0 : setFontStretch(FontStretch_semi_expanded);
1540 : }
1541 0 : else if(aContent.startsWith("expanded"))
1542 : {
1543 0 : setFontStretch(FontStretch_expanded);
1544 : }
1545 0 : else if(aContent.startsWith("extra-expanded"))
1546 : {
1547 0 : setFontStretch(FontStretch_extra_expanded);
1548 : }
1549 0 : else if(aContent.startsWith("ultra-expanded"))
1550 : {
1551 0 : setFontStretch(FontStretch_ultra_expanded);
1552 : }
1553 : }
1554 0 : break;
1555 : }
1556 : case SVGTokenFontStyle:
1557 : {
1558 0 : if(!aContent.isEmpty())
1559 : {
1560 0 : if(aContent.startsWith("normal"))
1561 : {
1562 0 : setFontStyle(FontStyle_normal);
1563 : }
1564 0 : else if(aContent.startsWith("italic"))
1565 : {
1566 0 : setFontStyle(FontStyle_italic);
1567 : }
1568 0 : else if(aContent.startsWith("oblique"))
1569 : {
1570 0 : setFontStyle(FontStyle_oblique);
1571 : }
1572 : }
1573 0 : break;
1574 : }
1575 : case SVGTokenFontVariant:
1576 : {
1577 0 : if(!aContent.isEmpty())
1578 : {
1579 0 : if(aContent.startsWith("normal"))
1580 : {
1581 0 : setFontVariant(FontVariant_normal);
1582 : }
1583 0 : else if(aContent.startsWith("small-caps"))
1584 : {
1585 0 : setFontVariant(FontVariant_small_caps);
1586 : }
1587 : }
1588 0 : break;
1589 : }
1590 : case SVGTokenFontWeight:
1591 : {
1592 0 : if(!aContent.isEmpty())
1593 : {
1594 0 : if(aContent.startsWith("100"))
1595 : {
1596 0 : setFontWeight(FontWeight_100);
1597 : }
1598 0 : else if(aContent.startsWith("200"))
1599 : {
1600 0 : setFontWeight(FontWeight_200);
1601 : }
1602 0 : else if(aContent.startsWith("300"))
1603 : {
1604 0 : setFontWeight(FontWeight_300);
1605 : }
1606 0 : else if(aContent.startsWith("400") || aContent.startsWith("normal"))
1607 : {
1608 0 : setFontWeight(FontWeight_400);
1609 : }
1610 0 : else if(aContent.startsWith("500"))
1611 : {
1612 0 : setFontWeight(FontWeight_500);
1613 : }
1614 0 : else if(aContent.startsWith("600"))
1615 : {
1616 0 : setFontWeight(FontWeight_600);
1617 : }
1618 0 : else if(aContent.startsWith("700") || aContent.startsWith("bold"))
1619 : {
1620 0 : setFontWeight(FontWeight_700);
1621 : }
1622 0 : else if(aContent.startsWith("800"))
1623 : {
1624 0 : setFontWeight(FontWeight_800);
1625 : }
1626 0 : else if(aContent.startsWith("900"))
1627 : {
1628 0 : setFontWeight(FontWeight_900);
1629 : }
1630 0 : else if(aContent.startsWith("bolder"))
1631 : {
1632 0 : setFontWeight(FontWeight_bolder);
1633 : }
1634 0 : else if(aContent.startsWith("lighter"))
1635 : {
1636 0 : setFontWeight(FontWeight_lighter);
1637 : }
1638 : }
1639 0 : break;
1640 : }
1641 : case SVGTokenDirection:
1642 : {
1643 0 : break;
1644 : }
1645 : case SVGTokenLetterSpacing:
1646 : {
1647 0 : break;
1648 : }
1649 : case SVGTokenTextDecoration:
1650 : {
1651 0 : if(!aContent.isEmpty())
1652 : {
1653 0 : if(aContent.startsWith("none"))
1654 : {
1655 0 : setTextDecoration(TextDecoration_none);
1656 : }
1657 0 : else if(aContent.startsWith("underline"))
1658 : {
1659 0 : setTextDecoration(TextDecoration_underline);
1660 : }
1661 0 : else if(aContent.startsWith("overline"))
1662 : {
1663 0 : setTextDecoration(TextDecoration_overline);
1664 : }
1665 0 : else if(aContent.startsWith("line-through"))
1666 : {
1667 0 : setTextDecoration(TextDecoration_line_through);
1668 : }
1669 0 : else if(aContent.startsWith("blink"))
1670 : {
1671 0 : setTextDecoration(TextDecoration_blink);
1672 : }
1673 : }
1674 0 : break;
1675 : }
1676 : case SVGTokenUnicodeBidi:
1677 : {
1678 0 : break;
1679 : }
1680 : case SVGTokenWordSpacing:
1681 : {
1682 0 : break;
1683 : }
1684 : case SVGTokenTextAnchor:
1685 : {
1686 0 : if(!aContent.isEmpty())
1687 : {
1688 0 : if(aContent.startsWith("start"))
1689 : {
1690 0 : setTextAnchor(TextAnchor_start);
1691 : }
1692 0 : else if(aContent.startsWith("middle"))
1693 : {
1694 0 : setTextAnchor(TextAnchor_middle);
1695 : }
1696 0 : else if(aContent.startsWith("end"))
1697 : {
1698 0 : setTextAnchor(TextAnchor_end);
1699 : }
1700 : }
1701 0 : break;
1702 : }
1703 : case SVGTokenTextAlign:
1704 : {
1705 0 : if(!aContent.isEmpty())
1706 : {
1707 0 : if(aContent.startsWith("left"))
1708 : {
1709 0 : setTextAlign(TextAlign_left);
1710 : }
1711 0 : else if(aContent.startsWith("right"))
1712 : {
1713 0 : setTextAlign(TextAlign_right);
1714 : }
1715 0 : else if(aContent.startsWith("center"))
1716 : {
1717 0 : setTextAlign(TextAlign_center);
1718 : }
1719 0 : else if(aContent.startsWith("justify"))
1720 : {
1721 0 : setTextAlign(TextAlign_justify);
1722 : }
1723 : }
1724 0 : break;
1725 : }
1726 : case SVGTokenColor:
1727 : {
1728 0 : SvgPaint aSvgPaint;
1729 0 : OUString aURL;
1730 :
1731 0 : if(readSvgPaint(aContent, aSvgPaint, aURL, bCaseIndependent))
1732 : {
1733 0 : setColor(aSvgPaint);
1734 : }
1735 0 : break;
1736 : }
1737 : case SVGTokenOpacity:
1738 : {
1739 719 : SvgNumber aNum;
1740 :
1741 719 : if(readSingleNumber(aContent, aNum))
1742 : {
1743 719 : setOpacity(SvgNumber(basegfx::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet()));
1744 : }
1745 719 : break;
1746 : }
1747 : case SVGTokenVisibility:
1748 : {
1749 0 : if(!aContent.isEmpty())
1750 : {
1751 0 : if(aContent.startsWith("visible"))
1752 : {
1753 0 : setVisibility(Visibility_visible);
1754 : }
1755 0 : else if(aContent.startsWith("hidden"))
1756 : {
1757 0 : setVisibility(Visibility_hidden);
1758 : }
1759 0 : else if(aContent.startsWith("collapse"))
1760 : {
1761 0 : setVisibility(Visibility_collapse);
1762 : }
1763 0 : else if(aContent.startsWith("inherit"))
1764 : {
1765 0 : setVisibility(Visibility_inherit);
1766 : }
1767 : }
1768 0 : break;
1769 : }
1770 : case SVGTokenTitle:
1771 : {
1772 0 : setTitle(aContent);
1773 0 : break;
1774 : }
1775 : case SVGTokenDesc:
1776 : {
1777 0 : setDesc(aContent);
1778 0 : break;
1779 : }
1780 : case SVGTokenClipPathProperty:
1781 : {
1782 156 : readLocalUrl(aContent, maClipPathXLink);
1783 156 : break;
1784 : }
1785 : case SVGTokenMask:
1786 : {
1787 171 : readLocalUrl(aContent, maMaskXLink);
1788 171 : break;
1789 : }
1790 : case SVGTokenClipRule:
1791 : {
1792 21 : if(!aContent.isEmpty())
1793 : {
1794 21 : if(aContent.match(commonStrings::aStrNonzero))
1795 : {
1796 0 : maClipRule = FillRule_nonzero;
1797 : }
1798 21 : else if(aContent.match(commonStrings::aStrEvenOdd))
1799 : {
1800 21 : maClipRule = FillRule_evenodd;
1801 : }
1802 : }
1803 21 : break;
1804 : }
1805 : case SVGTokenMarker:
1806 : {
1807 0 : readLocalUrl(aContent, maMarkerEndXLink);
1808 0 : maMarkerStartXLink = maMarkerMidXLink = maMarkerEndXLink;
1809 0 : break;
1810 : }
1811 : case SVGTokenMarkerStart:
1812 : {
1813 0 : readLocalUrl(aContent, maMarkerStartXLink);
1814 0 : break;
1815 : }
1816 : case SVGTokenMarkerMid:
1817 : {
1818 0 : readLocalUrl(aContent, maMarkerMidXLink);
1819 0 : break;
1820 : }
1821 : case SVGTokenMarkerEnd:
1822 : {
1823 0 : readLocalUrl(aContent, maMarkerEndXLink);
1824 0 : break;
1825 : }
1826 : case SVGTokenDisplay:
1827 : {
1828 : // There may be display:none statements inside of style defines, e.g. the following line:
1829 : // style="display:none"
1830 : // taken from a svg example; this needs to be parsed and set at the owning node. Do not call
1831 : // mrOwner.parseAttribute(...) here, this would lead to a recursion
1832 0 : if(!aContent.isEmpty())
1833 : {
1834 0 : mrOwner.setDisplay(getDisplayFromContent(aContent));
1835 : }
1836 0 : break;
1837 : }
1838 : case SVGTokenBaselineShift:
1839 : {
1840 0 : if(!aContent.isEmpty())
1841 : {
1842 0 : SvgNumber aNum;
1843 :
1844 0 : if(aContent.startsWith("sub"))
1845 : {
1846 0 : setBaselineShift(BaselineShift_Sub);
1847 : }
1848 0 : else if(aContent.startsWith("super"))
1849 : {
1850 0 : setBaselineShift(BaselineShift_Super);
1851 : }
1852 0 : else if(readSingleNumber(aContent, aNum))
1853 : {
1854 0 : setBaselineShiftNumber(aNum);
1855 :
1856 0 : if(Unit_percent == aNum.getUnit())
1857 : {
1858 0 : setBaselineShift(BaselineShift_Percentage);
1859 : }
1860 : else
1861 : {
1862 0 : setBaselineShift(BaselineShift_Length);
1863 : }
1864 : }
1865 : else
1866 : {
1867 : // no BaselineShift or inherit (which is automatically)
1868 0 : setBaselineShift(BaselineShift_Baseline);
1869 : }
1870 : }
1871 0 : break;
1872 : }
1873 : default:
1874 : {
1875 25079 : break;
1876 : }
1877 : }
1878 32998 : }
1879 :
1880 : // #i125258# ask if fill is a direct hard attribute (no hierarchy)
1881 312 : bool SvgStyleAttributes::isFillSet() const
1882 : {
1883 312 : if(mbIsClipPathContent)
1884 : {
1885 0 : return false;
1886 : }
1887 312 : else if(maFill.isSet())
1888 : {
1889 0 : return true;
1890 : }
1891 :
1892 312 : return false;
1893 : }
1894 :
1895 0 : const basegfx::BColor* SvgStyleAttributes::getCurrentColor() const
1896 : {
1897 0 : static basegfx::BColor aBlack(0.0, 0.0, 0.0);
1898 0 : const basegfx::BColor *aColor = getColor();
1899 0 : if( aColor )
1900 0 : return aColor;
1901 : else
1902 0 : return &aBlack;
1903 : }
1904 :
1905 13723 : const basegfx::BColor* SvgStyleAttributes::getFill() const
1906 : {
1907 13723 : if(mbIsClipPathContent)
1908 : {
1909 219 : static basegfx::BColor aBlack(0.0, 0.0, 0.0);
1910 219 : return &aBlack;
1911 : }
1912 13504 : else if(maFill.isSet())
1913 : {
1914 5243 : if(maFill.isCurrent())
1915 : {
1916 0 : return getCurrentColor();
1917 : }
1918 5243 : else if(maFill.isOn())
1919 : {
1920 5107 : return &maFill.getBColor();
1921 : }
1922 : }
1923 : else
1924 : {
1925 8261 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1926 :
1927 8261 : if(pSvgStyleAttributes)
1928 : {
1929 8261 : return pSvgStyleAttributes->getFill();
1930 : }
1931 : }
1932 :
1933 136 : return 0;
1934 : }
1935 :
1936 11901 : const basegfx::BColor* SvgStyleAttributes::getStroke() const
1937 : {
1938 11901 : if(mbIsClipPathContent)
1939 : {
1940 156 : return 0;
1941 : }
1942 11745 : else if(maStroke.isSet())
1943 : {
1944 230 : if(maStroke.isCurrent())
1945 : {
1946 0 : return getCurrentColor();
1947 : }
1948 230 : else if(maStroke.isOn())
1949 : {
1950 230 : return &maStroke.getBColor();
1951 : }
1952 : }
1953 : else
1954 : {
1955 11515 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1956 :
1957 11515 : if(pSvgStyleAttributes)
1958 : {
1959 8904 : return pSvgStyleAttributes->getStroke();
1960 : }
1961 : }
1962 :
1963 2611 : return 0;
1964 : }
1965 :
1966 3313 : const basegfx::BColor& SvgStyleAttributes::getStopColor() const
1967 : {
1968 3313 : if(maStopColor.isCurrent())
1969 : {
1970 0 : return *getCurrentColor();
1971 : }
1972 : else
1973 : {
1974 3313 : return maStopColor.getBColor();
1975 : }
1976 : }
1977 :
1978 8464 : const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeFill() const
1979 : {
1980 8464 : if(mbIsClipPathContent)
1981 : {
1982 156 : return 0;
1983 : }
1984 8308 : else if(mpSvgGradientNodeFill)
1985 : {
1986 1244 : return mpSvgGradientNodeFill;
1987 : }
1988 : else
1989 : {
1990 7064 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
1991 :
1992 7064 : if(pSvgStyleAttributes)
1993 : {
1994 5436 : return pSvgStyleAttributes->getSvgGradientNodeFill();
1995 : }
1996 : }
1997 :
1998 1628 : return 0;
1999 : }
2000 :
2001 12549 : const SvgGradientNode* SvgStyleAttributes::getSvgGradientNodeStroke() const
2002 : {
2003 12549 : if(mbIsClipPathContent)
2004 : {
2005 156 : return 0;
2006 : }
2007 12393 : else if(mpSvgGradientNodeStroke)
2008 : {
2009 0 : return mpSvgGradientNodeStroke;
2010 : }
2011 : else
2012 : {
2013 12393 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2014 :
2015 12393 : if(pSvgStyleAttributes)
2016 : {
2017 9552 : return pSvgStyleAttributes->getSvgGradientNodeStroke();
2018 : }
2019 : }
2020 :
2021 2841 : return 0;
2022 : }
2023 :
2024 12639 : const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeFill() const
2025 : {
2026 12639 : if(mbIsClipPathContent)
2027 : {
2028 156 : return 0;
2029 : }
2030 12483 : else if(mpSvgPatternNodeFill)
2031 : {
2032 0 : return mpSvgPatternNodeFill;
2033 : }
2034 : else
2035 : {
2036 12483 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2037 :
2038 12483 : if(pSvgStyleAttributes)
2039 : {
2040 9611 : return pSvgStyleAttributes->getSvgPatternNodeFill();
2041 : }
2042 : }
2043 :
2044 2872 : return 0;
2045 : }
2046 :
2047 12549 : const SvgPatternNode* SvgStyleAttributes::getSvgPatternNodeStroke() const
2048 : {
2049 12549 : if(mbIsClipPathContent)
2050 : {
2051 156 : return 0;
2052 : }
2053 12393 : else if(mpSvgPatternNodeStroke)
2054 : {
2055 0 : return mpSvgPatternNodeStroke;
2056 : }
2057 : else
2058 : {
2059 12393 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2060 :
2061 12393 : if(pSvgStyleAttributes)
2062 : {
2063 9552 : return pSvgStyleAttributes->getSvgPatternNodeStroke();
2064 : }
2065 : }
2066 :
2067 2841 : return 0;
2068 : }
2069 :
2070 524 : SvgNumber SvgStyleAttributes::getStrokeWidth() const
2071 : {
2072 524 : if(mbIsClipPathContent)
2073 : {
2074 0 : return SvgNumber(0.0);
2075 : }
2076 524 : else if(maStrokeWidth.isSet())
2077 : {
2078 432 : return maStrokeWidth;
2079 : }
2080 :
2081 92 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2082 :
2083 92 : if(pSvgStyleAttributes)
2084 : {
2085 64 : return pSvgStyleAttributes->getStrokeWidth();
2086 : }
2087 :
2088 : // default is 1
2089 28 : return SvgNumber(1.0);
2090 : }
2091 :
2092 3313 : SvgNumber SvgStyleAttributes::getStopOpacity() const
2093 : {
2094 3313 : if(maStopOpacity.isSet())
2095 : {
2096 0 : return maStopOpacity;
2097 : }
2098 :
2099 : // default is 1
2100 3313 : return SvgNumber(1.0);
2101 : }
2102 :
2103 12186 : SvgNumber SvgStyleAttributes::getFillOpacity() const
2104 : {
2105 12186 : if(mbIsClipPathContent)
2106 : {
2107 156 : return SvgNumber(1.0);
2108 : }
2109 12030 : else if(maFillOpacity.isSet())
2110 : {
2111 1 : return maFillOpacity;
2112 : }
2113 :
2114 12029 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2115 :
2116 12029 : if(pSvgStyleAttributes)
2117 : {
2118 9294 : return pSvgStyleAttributes->getFillOpacity();
2119 : }
2120 :
2121 : // default is 1
2122 2735 : return SvgNumber(1.0);
2123 : }
2124 :
2125 10896 : FillRule SvgStyleAttributes::getFillRule() const
2126 : {
2127 10896 : if(FillRule_notset != maFillRule)
2128 : {
2129 21 : return maFillRule;
2130 : }
2131 :
2132 10875 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2133 :
2134 10875 : if(pSvgStyleAttributes)
2135 : {
2136 8391 : return pSvgStyleAttributes->getFillRule();
2137 : }
2138 :
2139 : // default is NonZero
2140 2484 : return FillRule_nonzero;
2141 : }
2142 :
2143 862 : const SvgNumberVector& SvgStyleAttributes::getStrokeDasharray() const
2144 : {
2145 862 : if(!maStrokeDasharray.empty())
2146 : {
2147 30 : return maStrokeDasharray;
2148 : }
2149 832 : else if(getStrokeDasharraySet())
2150 : {
2151 : // #121221# is set to empty *by purpose*, do not visit parent styles
2152 0 : return maStrokeDasharray;
2153 : }
2154 :
2155 832 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2156 :
2157 832 : if(pSvgStyleAttributes)
2158 : {
2159 617 : return pSvgStyleAttributes->getStrokeDasharray();
2160 : }
2161 :
2162 : // default empty
2163 215 : return maStrokeDasharray;
2164 : }
2165 :
2166 0 : SvgNumber SvgStyleAttributes::getStrokeDashOffset() const
2167 : {
2168 0 : if(maStrokeDashOffset.isSet())
2169 : {
2170 0 : return maStrokeDashOffset;
2171 : }
2172 :
2173 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2174 :
2175 0 : if(pSvgStyleAttributes)
2176 : {
2177 0 : return pSvgStyleAttributes->getStrokeDashOffset();
2178 : }
2179 :
2180 : // default is 0
2181 0 : return SvgNumber(0.0);
2182 : }
2183 :
2184 767 : StrokeLinecap SvgStyleAttributes::getStrokeLinecap() const
2185 : {
2186 767 : if(maStrokeLinecap != StrokeLinecap_notset)
2187 : {
2188 38 : return maStrokeLinecap;
2189 : }
2190 :
2191 729 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2192 :
2193 729 : if(pSvgStyleAttributes)
2194 : {
2195 537 : return pSvgStyleAttributes->getStrokeLinecap();
2196 : }
2197 :
2198 : // default is StrokeLinecap_butt
2199 192 : return StrokeLinecap_butt;
2200 : }
2201 :
2202 792 : StrokeLinejoin SvgStyleAttributes::getStrokeLinejoin() const
2203 : {
2204 792 : if(maStrokeLinejoin != StrokeLinejoin_notset)
2205 : {
2206 30 : return maStrokeLinejoin;
2207 : }
2208 :
2209 762 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2210 :
2211 762 : if(pSvgStyleAttributes)
2212 : {
2213 562 : return pSvgStyleAttributes->getStrokeLinejoin();
2214 : }
2215 :
2216 : // default is StrokeLinejoin_butt
2217 200 : return StrokeLinejoin_miter;
2218 : }
2219 :
2220 0 : SvgNumber SvgStyleAttributes::getStrokeMiterLimit() const
2221 : {
2222 0 : if(maStrokeMiterLimit.isSet())
2223 : {
2224 0 : return maStrokeMiterLimit;
2225 : }
2226 :
2227 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2228 :
2229 0 : if(pSvgStyleAttributes)
2230 : {
2231 0 : return pSvgStyleAttributes->getStrokeMiterLimit();
2232 : }
2233 :
2234 : // default is 4
2235 0 : return SvgNumber(4.0);
2236 : }
2237 :
2238 882 : SvgNumber SvgStyleAttributes::getStrokeOpacity() const
2239 : {
2240 882 : if(maStrokeOpacity.isSet())
2241 : {
2242 0 : return maStrokeOpacity;
2243 : }
2244 :
2245 882 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2246 :
2247 882 : if(pSvgStyleAttributes)
2248 : {
2249 652 : return pSvgStyleAttributes->getStrokeOpacity();
2250 : }
2251 :
2252 : // default is 1
2253 230 : return SvgNumber(1.0);
2254 : }
2255 :
2256 0 : const SvgStringVector& SvgStyleAttributes::getFontFamily() const
2257 : {
2258 0 : if(!maFontFamily.empty())
2259 : {
2260 0 : return maFontFamily;
2261 : }
2262 :
2263 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2264 :
2265 0 : if(pSvgStyleAttributes)
2266 : {
2267 0 : return pSvgStyleAttributes->getFontFamily();
2268 : }
2269 :
2270 : // default is empty
2271 0 : return maFontFamily;
2272 : }
2273 :
2274 0 : SvgNumber SvgStyleAttributes::getFontSize() const
2275 : {
2276 0 : if(maFontSize.isSet())
2277 : {
2278 : // #122524# Handle Unit_percent realtive to parent FontSize (see SVG1.1
2279 : // spec 10.10 Font selection properties \91font-size\92, lastline (click 'normative
2280 : // definition of the property')
2281 0 : if(Unit_percent == maFontSize.getUnit())
2282 : {
2283 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2284 :
2285 0 : if(pSvgStyleAttributes)
2286 : {
2287 0 : const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSize();
2288 :
2289 : return SvgNumber(
2290 0 : aParentNumber.getNumber() * maFontSize.getNumber() * 0.01,
2291 : aParentNumber.getUnit(),
2292 0 : true);
2293 : }
2294 : }
2295 :
2296 0 : return maFontSize;
2297 : }
2298 :
2299 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2300 :
2301 0 : if(pSvgStyleAttributes)
2302 : {
2303 0 : return pSvgStyleAttributes->getFontSize();
2304 : }
2305 :
2306 : // default is 'medium'
2307 0 : return SvgNumber(12.0);
2308 : }
2309 :
2310 0 : FontStretch SvgStyleAttributes::getFontStretch() const
2311 : {
2312 0 : if(maFontStretch != FontStretch_notset)
2313 : {
2314 0 : if(FontStretch_wider != maFontStretch && FontStretch_narrower != maFontStretch)
2315 : {
2316 0 : return maFontStretch;
2317 : }
2318 : }
2319 :
2320 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2321 :
2322 0 : if(pSvgStyleAttributes)
2323 : {
2324 0 : FontStretch aInherited = pSvgStyleAttributes->getFontStretch();
2325 :
2326 0 : if(FontStretch_wider == maFontStretch)
2327 : {
2328 0 : aInherited = getWider(aInherited);
2329 : }
2330 0 : else if(FontStretch_narrower == maFontStretch)
2331 : {
2332 0 : aInherited = getNarrower(aInherited);
2333 : }
2334 :
2335 0 : return aInherited;
2336 : }
2337 :
2338 : // default is FontStretch_normal
2339 0 : return FontStretch_normal;
2340 : }
2341 :
2342 0 : FontStyle SvgStyleAttributes::getFontStyle() const
2343 : {
2344 0 : if(maFontStyle != FontStyle_notset)
2345 : {
2346 0 : return maFontStyle;
2347 : }
2348 :
2349 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2350 :
2351 0 : if(pSvgStyleAttributes)
2352 : {
2353 0 : return pSvgStyleAttributes->getFontStyle();
2354 : }
2355 :
2356 : // default is FontStyle_normal
2357 0 : return FontStyle_normal;
2358 : }
2359 :
2360 0 : FontWeight SvgStyleAttributes::getFontWeight() const
2361 : {
2362 0 : if(maFontWeight != FontWeight_notset)
2363 : {
2364 0 : if(FontWeight_bolder != maFontWeight && FontWeight_lighter != maFontWeight)
2365 : {
2366 0 : return maFontWeight;
2367 : }
2368 : }
2369 :
2370 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2371 :
2372 0 : if(pSvgStyleAttributes)
2373 : {
2374 0 : FontWeight aInherited = pSvgStyleAttributes->getFontWeight();
2375 :
2376 0 : if(FontWeight_bolder == maFontWeight)
2377 : {
2378 0 : aInherited = getBolder(aInherited);
2379 : }
2380 0 : else if(FontWeight_lighter == maFontWeight)
2381 : {
2382 0 : aInherited = getLighter(aInherited);
2383 : }
2384 :
2385 0 : return aInherited;
2386 : }
2387 :
2388 : // default is FontWeight_400 (FontWeight_normal)
2389 0 : return FontWeight_400;
2390 : }
2391 :
2392 0 : TextAlign SvgStyleAttributes::getTextAlign() const
2393 : {
2394 0 : if(maTextAlign != TextAlign_notset)
2395 : {
2396 0 : return maTextAlign;
2397 : }
2398 :
2399 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2400 :
2401 0 : if(pSvgStyleAttributes)
2402 : {
2403 0 : return pSvgStyleAttributes->getTextAlign();
2404 : }
2405 :
2406 : // default is TextAlign_left
2407 0 : return TextAlign_left;
2408 : }
2409 :
2410 0 : const SvgStyleAttributes* SvgStyleAttributes::getTextDecorationDefiningSvgStyleAttributes() const
2411 : {
2412 0 : if(maTextDecoration != TextDecoration_notset)
2413 : {
2414 0 : return this;
2415 : }
2416 :
2417 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2418 :
2419 0 : if(pSvgStyleAttributes)
2420 : {
2421 0 : return pSvgStyleAttributes->getTextDecorationDefiningSvgStyleAttributes();
2422 : }
2423 :
2424 : // default is 0
2425 0 : return 0;
2426 : }
2427 :
2428 0 : TextDecoration SvgStyleAttributes::getTextDecoration() const
2429 : {
2430 0 : const SvgStyleAttributes* pDefining = getTextDecorationDefiningSvgStyleAttributes();
2431 :
2432 0 : if(pDefining)
2433 : {
2434 0 : return pDefining->maTextDecoration;
2435 : }
2436 : else
2437 : {
2438 : // default is TextDecoration_none
2439 0 : return TextDecoration_none;
2440 : }
2441 : }
2442 :
2443 0 : TextAnchor SvgStyleAttributes::getTextAnchor() const
2444 : {
2445 0 : if(maTextAnchor != TextAnchor_notset)
2446 : {
2447 0 : return maTextAnchor;
2448 : }
2449 :
2450 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2451 :
2452 0 : if(pSvgStyleAttributes)
2453 : {
2454 0 : return pSvgStyleAttributes->getTextAnchor();
2455 : }
2456 :
2457 : // default is TextAnchor_start
2458 0 : return TextAnchor_start;
2459 : }
2460 :
2461 0 : const basegfx::BColor* SvgStyleAttributes::getColor() const
2462 : {
2463 0 : if(maColor.isSet())
2464 : {
2465 0 : if(maColor.isCurrent())
2466 : {
2467 : OSL_ENSURE(false, "Svg error: current color uses current color (!)");
2468 0 : return 0;
2469 : }
2470 0 : else if(maColor.isOn())
2471 : {
2472 0 : return &maColor.getBColor();
2473 : }
2474 : }
2475 : else
2476 : {
2477 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2478 :
2479 0 : if(pSvgStyleAttributes)
2480 : {
2481 0 : return pSvgStyleAttributes->getColor();
2482 : }
2483 : }
2484 :
2485 0 : return 0;
2486 : }
2487 :
2488 11025 : OUString SvgStyleAttributes::getMarkerStartXLink() const
2489 : {
2490 11025 : if(!maMarkerStartXLink.isEmpty())
2491 : {
2492 0 : return maMarkerStartXLink;
2493 : }
2494 :
2495 11025 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2496 :
2497 11025 : if(pSvgStyleAttributes)
2498 : {
2499 8501 : return pSvgStyleAttributes->getMarkerStartXLink();
2500 : }
2501 :
2502 2524 : return OUString();
2503 : }
2504 :
2505 2524 : const SvgMarkerNode* SvgStyleAttributes::accessMarkerStartXLink() const
2506 : {
2507 2524 : if(!mpMarkerStartXLink)
2508 : {
2509 2524 : const OUString aMarker(getMarkerStartXLink());
2510 :
2511 2524 : if(!aMarker.isEmpty())
2512 : {
2513 0 : const_cast< SvgStyleAttributes* >(this)->mpMarkerStartXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerStartXLink()));
2514 2524 : }
2515 : }
2516 :
2517 2524 : return mpMarkerStartXLink;
2518 : }
2519 :
2520 11025 : OUString SvgStyleAttributes::getMarkerMidXLink() const
2521 : {
2522 11025 : if(!maMarkerMidXLink.isEmpty())
2523 : {
2524 0 : return maMarkerMidXLink;
2525 : }
2526 :
2527 11025 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2528 :
2529 11025 : if(pSvgStyleAttributes)
2530 : {
2531 8501 : return pSvgStyleAttributes->getMarkerMidXLink();
2532 : }
2533 :
2534 2524 : return OUString();
2535 : }
2536 :
2537 2524 : const SvgMarkerNode* SvgStyleAttributes::accessMarkerMidXLink() const
2538 : {
2539 2524 : if(!mpMarkerMidXLink)
2540 : {
2541 2524 : const OUString aMarker(getMarkerMidXLink());
2542 :
2543 2524 : if(!aMarker.isEmpty())
2544 : {
2545 0 : const_cast< SvgStyleAttributes* >(this)->mpMarkerMidXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerMidXLink()));
2546 2524 : }
2547 : }
2548 :
2549 2524 : return mpMarkerMidXLink;
2550 : }
2551 :
2552 11025 : OUString SvgStyleAttributes::getMarkerEndXLink() const
2553 : {
2554 11025 : if(!maMarkerEndXLink.isEmpty())
2555 : {
2556 0 : return maMarkerEndXLink;
2557 : }
2558 :
2559 11025 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2560 :
2561 11025 : if(pSvgStyleAttributes)
2562 : {
2563 8501 : return pSvgStyleAttributes->getMarkerEndXLink();
2564 : }
2565 :
2566 2524 : return OUString();
2567 : }
2568 :
2569 2524 : const SvgMarkerNode* SvgStyleAttributes::accessMarkerEndXLink() const
2570 : {
2571 2524 : if(!mpMarkerEndXLink)
2572 : {
2573 2524 : const OUString aMarker(getMarkerEndXLink());
2574 :
2575 2524 : if(!aMarker.isEmpty())
2576 : {
2577 0 : const_cast< SvgStyleAttributes* >(this)->mpMarkerEndXLink = dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerEndXLink()));
2578 2524 : }
2579 : }
2580 :
2581 2524 : return mpMarkerEndXLink;
2582 : }
2583 :
2584 0 : SvgNumber SvgStyleAttributes::getBaselineShiftNumber() const
2585 : {
2586 : // #122524# Handle Unit_percent realtive to parent BaselineShift
2587 0 : if(Unit_percent == maBaselineShiftNumber.getUnit())
2588 : {
2589 0 : const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2590 :
2591 0 : if(pSvgStyleAttributes)
2592 : {
2593 0 : const SvgNumber aParentNumber = pSvgStyleAttributes->getBaselineShiftNumber();
2594 :
2595 : return SvgNumber(
2596 0 : aParentNumber.getNumber() * maBaselineShiftNumber.getNumber() * 0.01,
2597 : aParentNumber.getUnit(),
2598 0 : true);
2599 : }
2600 : }
2601 :
2602 0 : return maBaselineShiftNumber;
2603 : }
2604 : } // end of namespace svgreader
2605 : } // end of namespace svgio
2606 :
2607 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|