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