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