LibreOffice Module svgio (master) 1
svgstyleattributes.cxx
Go to the documentation of this file.
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 <sal/config.h>
21
22#include <algorithm>
23
28#include <svgnode.hxx>
29#include <svgdocument.hxx>
31#include <svggradientnode.hxx>
37#include <svgclippathnode.hxx>
38#include <svgfilternode.hxx>
39#include <svgmasknode.hxx>
41#include <svgmarkernode.hxx>
42#include <svgpatternnode.hxx>
47#include <o3tl/string_view.hxx>
49
50const int nStyleDepthLimit = 1024;
51
52namespace svgio::svgreader
53{
55 {
56 if(StrokeLinejoin::round == aStrokeLinejoin)
57 {
59 }
60 else if(StrokeLinejoin::bevel == aStrokeLinejoin)
61 {
63 }
64
66 }
67
68 static css::drawing::LineCap StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap)
69 {
70 switch(aStrokeLinecap)
71 {
72 default: /* StrokeLinecap::notset, StrokeLinecap::butt */
73 {
74 return css::drawing::LineCap_BUTT;
75 }
77 {
78 return css::drawing::LineCap_ROUND;
79 }
81 {
82 return css::drawing::LineCap_SQUARE;
83 }
84 }
85 }
86
88 {
89 switch(aSource)
90 {
99 default: break;
100 }
101
102 return aSource;
103 }
104
106 {
107 switch(aSource)
108 {
113 case FontStretch::semi_expanded: aSource = FontStretch::normal; break;
117 default: break;
118 }
119
120 return aSource;
121 }
122
124 {
125 switch(aSource)
126 {
127 case FontWeight::N100: aSource = FontWeight::N200; break;
128 case FontWeight::N200: aSource = FontWeight::N300; break;
129 case FontWeight::N300: aSource = FontWeight::N400; break;
130 case FontWeight::N400: aSource = FontWeight::N500; break;
131 case FontWeight::N500: aSource = FontWeight::N600; break;
132 case FontWeight::N600: aSource = FontWeight::N700; break;
133 case FontWeight::N700: aSource = FontWeight::N800; break;
134 case FontWeight::N800: aSource = FontWeight::N900; break;
135 default: break;
136 }
137
138 return aSource;
139 }
140
142 {
143 switch(aSource)
144 {
145 case FontWeight::N200: aSource = FontWeight::N100; break;
146 case FontWeight::N300: aSource = FontWeight::N200; break;
147 case FontWeight::N400: aSource = FontWeight::N300; break;
148 case FontWeight::N500: aSource = FontWeight::N400; break;
149 case FontWeight::N600: aSource = FontWeight::N500; break;
150 case FontWeight::N700: aSource = FontWeight::N600; break;
151 case FontWeight::N800: aSource = FontWeight::N700; break;
152 case FontWeight::N900: aSource = FontWeight::N800; break;
153 default: break;
154 }
155
156 return aSource;
157 }
158
160 {
162
163 switch(aSource)
164 {
165 case FontWeight::N100: nRetval = WEIGHT_ULTRALIGHT; break;
166 case FontWeight::N200: nRetval = WEIGHT_LIGHT; break;
167 case FontWeight::N300: nRetval = WEIGHT_SEMILIGHT; break;
168 case FontWeight::N400: nRetval = WEIGHT_NORMAL; break;
169 case FontWeight::N500: nRetval = WEIGHT_MEDIUM; break;
170 case FontWeight::N600: nRetval = WEIGHT_SEMIBOLD; break;
171 case FontWeight::N700: nRetval = WEIGHT_BOLD; break;
172 case FontWeight::N800: nRetval = WEIGHT_ULTRABOLD; break;
173 case FontWeight::N900: nRetval = WEIGHT_BLACK; break;
174 default: break;
175 }
176
177 return nRetval;
178 }
179
180 void SvgStyleAttributes::readCssStyle(std::u16string_view rCandidate)
181 {
182 const sal_Int32 nLen(rCandidate.size());
183 sal_Int32 nPos(0);
184
185 while(nPos < nLen)
186 {
187 // get TokenName
188 OUStringBuffer aTokenName;
189 skip_char(rCandidate, u' ', nPos, nLen);
190 copyString(rCandidate, nPos, aTokenName, nLen);
191
192 if (aTokenName.isEmpty())
193 {
194 // if no TokenName advance one by force to avoid death loop, continue
195 OSL_ENSURE(false, "Could not interpret on current position, advancing one byte (!)");
196 nPos++;
197 continue;
198 }
199
200 // get TokenValue
201 OUStringBuffer aTokenValue;
202 skip_char(rCandidate, u' ', u':', nPos, nLen);
203 copyToLimiter(rCandidate, u';', nPos, aTokenValue, nLen);
204 skip_char(rCandidate, u' ', u';', nPos, nLen);
205
206 if (aTokenValue.isEmpty())
207 {
208 // no value - continue
209 continue;
210 }
211
212 // generate OUStrings
213 const OUString aOUTokenName(aTokenName.makeStringAndClear());
214 OUString aOUTokenValue(aTokenValue.makeStringAndClear());
215
216 // check for '!important' CssStyle mark, currently not supported
217 // but needs to be extracted for correct parsing
218 OUString aTokenImportant("!important");
219 const sal_Int32 nIndexTokenImportant(aOUTokenValue.indexOf(aTokenImportant));
220
221 if(-1 != nIndexTokenImportant)
222 {
223 // if there currently just remove it and remove spaces to have the value only
224 OUString aNewOUTokenValue;
225
226 if(nIndexTokenImportant > 0)
227 {
228 // copy content before token
229 aNewOUTokenValue += aOUTokenValue.subView(0, nIndexTokenImportant);
230 }
231
232 if(aOUTokenValue.getLength() > nIndexTokenImportant + aTokenImportant.getLength())
233 {
234 // copy content after token
235 aNewOUTokenValue += aOUTokenValue.subView(nIndexTokenImportant + aTokenImportant.getLength());
236 }
237
238 // remove spaces
239 aOUTokenValue = aNewOUTokenValue.trim();
240 }
241
242 // valid token-value pair, parse it
243 parseStyleAttribute(StrToSVGToken(aOUTokenName, true), aOUTokenValue);
244 }
245 }
246
248 {
250 {
251 return getCssStyleParent();
252 }
253
255 {
257 }
258
259 return nullptr;
260 }
261
265 {
266 if(rSource.empty())
267 return;
268
269 // at this point the primitives in rSource are of type TextSimplePortionPrimitive2D
270 // or TextDecoratedPortionPrimitive2D and have the Fill Color (pAttributes->getFill())
271 // set. When another fill is used and also evtl. stroke is set it gets necessary to
272 // dismantle to geometry and add needed primitives
273 const basegfx::BColor* pFill = getFill();
274 const SvgGradientNode* pFillGradient = getSvgGradientNodeFill();
275 const SvgPatternNode* pFillPattern = getSvgPatternNodeFill();
276 const basegfx::BColor* pStroke = getStroke();
277 const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke();
278 const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke();
279 basegfx::B2DPolyPolygon aMergedArea;
280
281 if(pFillGradient || pFillPattern || pStroke || pStrokeGradient || pStrokePattern)
282 {
283 // text geometry is needed, create
284 // use neutral ViewInformation and create LineGeometryExtractor2D
285 const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
286 drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D);
287
288 // process
289 aExtractor.process(rSource);
290
291 // get results
293 const sal_uInt32 nResultCount(rResult.size());
294 basegfx::B2DPolyPolygonVector aTextFillVector;
295 aTextFillVector.reserve(nResultCount);
296
297 for(sal_uInt32 a(0); a < nResultCount; a++)
298 {
299 const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a];
300
301 if(rCandidate.getIsFilled())
302 {
303 aTextFillVector.push_back(rCandidate.getB2DPolyPolygon());
304 }
305 }
306
307 if(!aTextFillVector.empty())
308 {
309 aMergedArea = basegfx::utils::mergeToSinglePolyPolygon(aTextFillVector);
310 }
311 }
312
313 const bool bStrokeUsed(pStroke || pStrokeGradient || pStrokePattern);
314
315 // add fill. Use geometry even for simple color fill when stroke
316 // is used, else text rendering and the geometry-based stroke will
317 // normally not really match optically due to diverse system text
318 // renderers
319 if(aMergedArea.count() && (pFillGradient || pFillPattern || bStrokeUsed))
320 {
321 // create text fill content based on geometry
322 add_fill(aMergedArea, rTarget, aMergedArea.getB2DRange());
323 }
324 else if(pFill)
325 {
326 // add the already prepared primitives for single color fill
327 rTarget.append(std::move(rSource));
328 }
329
330 // add stroke
331 if(aMergedArea.count() && bStrokeUsed)
332 {
333 // create text stroke content
334 add_stroke(aMergedArea, rTarget, aMergedArea.getB2DRange());
335 }
336 }
337
339 const basegfx::B2DPolyPolygon& rPath,
341 const SvgGradientNode& rFillGradient,
342 const basegfx::B2DRange& rGeoRange) const
343 {
344 // create fill content
346
347 // get the color stops
348 rFillGradient.collectGradientEntries(aSvgGradientEntryVector);
349
350 if(aSvgGradientEntryVector.empty())
351 return;
352
353 basegfx::B2DHomMatrix aGeoToUnit;
354 basegfx::B2DHomMatrix aGradientTransform;
355
356 if(rFillGradient.getGradientTransform())
357 {
358 aGradientTransform = *rFillGradient.getGradientTransform();
359 }
360
361 if (SvgUnits::userSpaceOnUse == rFillGradient.getGradientUnits())
362 {
363 aGeoToUnit.translate(-rGeoRange.getMinX(), -rGeoRange.getMinY());
364 aGeoToUnit.scale(1.0 / rGeoRange.getWidth(), 1.0 / rGeoRange.getHeight());
365 }
366
367 if(SVGToken::LinearGradient == rFillGradient.getType())
368 {
369 basegfx::B2DPoint aStart(0.0, 0.0);
370 basegfx::B2DPoint aEnd(1.0, 0.0);
371
372 if (SvgUnits::userSpaceOnUse == rFillGradient.getGradientUnits())
373 {
374 // all possible units
375 aStart.setX(rFillGradient.getX1().solve(mrOwner, NumberType::xcoordinate));
376 aStart.setY(rFillGradient.getY1().solve(mrOwner, NumberType::ycoordinate));
377 aEnd.setX(rFillGradient.getX2().solve(mrOwner, NumberType::xcoordinate));
378 aEnd.setY(rFillGradient.getY2().solve(mrOwner, NumberType::ycoordinate));
379 }
380 else
381 {
382 // fractions or percent relative to object bounds
383 const SvgNumber X1(rFillGradient.getX1());
384 const SvgNumber Y1(rFillGradient.getY1());
385 const SvgNumber X2(rFillGradient.getX2());
386 const SvgNumber Y2(rFillGradient.getY2());
387
388 aStart.setX(SvgUnit::percent == X1.getUnit() ? X1.getNumber() * 0.01 : X1.getNumber());
389 aStart.setY(SvgUnit::percent == Y1.getUnit() ? Y1.getNumber() * 0.01 : Y1.getNumber());
390 aEnd.setX(SvgUnit::percent == X2.getUnit() ? X2.getNumber() * 0.01 : X2.getNumber());
391 aEnd.setY(SvgUnit::percent == Y2.getUnit() ? Y2.getNumber() * 0.01 : Y2.getNumber());
392 }
393
394 if(!aGeoToUnit.isIdentity())
395 {
396 aStart *= aGeoToUnit;
397 aEnd *= aGeoToUnit;
398 }
399
400 rTarget.push_back(
402 aGradientTransform,
403 rPath,
404 std::move(aSvgGradientEntryVector),
405 aStart,
406 aEnd,
408 rFillGradient.getSpreadMethod()));
409 }
410 else
411 {
412 basegfx::B2DPoint aStart(0.5, 0.5);
413 basegfx::B2DPoint aFocal;
414 double fRadius(0.5);
415 const SvgNumber* pFx = rFillGradient.getFx();
416 const SvgNumber* pFy = rFillGradient.getFy();
417 const bool bFocal(pFx || pFy);
418
419 if (SvgUnits::userSpaceOnUse == rFillGradient.getGradientUnits())
420 {
421 // all possible units
422 aStart.setX(rFillGradient.getCx().solve(mrOwner, NumberType::xcoordinate));
423 aStart.setY(rFillGradient.getCy().solve(mrOwner, NumberType::ycoordinate));
424 fRadius = rFillGradient.getR().solve(mrOwner);
425
426 if(bFocal)
427 {
428 aFocal.setX(pFx ? pFx->solve(mrOwner, NumberType::xcoordinate) : aStart.getX());
429 aFocal.setY(pFy ? pFy->solve(mrOwner, NumberType::ycoordinate) : aStart.getY());
430 }
431 }
432 else
433 {
434 // fractions or percent relative to object bounds
435 const SvgNumber Cx(rFillGradient.getCx());
436 const SvgNumber Cy(rFillGradient.getCy());
437 const SvgNumber R(rFillGradient.getR());
438
439 aStart.setX(SvgUnit::percent == Cx.getUnit() ? Cx.getNumber() * 0.01 : Cx.getNumber());
440 aStart.setY(SvgUnit::percent == Cy.getUnit() ? Cy.getNumber() * 0.01 : Cy.getNumber());
441 fRadius = (SvgUnit::percent == R.getUnit()) ? R.getNumber() * 0.01 : R.getNumber();
442
443 if(bFocal)
444 {
445 aFocal.setX(pFx ? (SvgUnit::percent == pFx->getUnit() ? pFx->getNumber() * 0.01 : pFx->getNumber()) : aStart.getX());
446 aFocal.setY(pFy ? (SvgUnit::percent == pFy->getUnit() ? pFy->getNumber() * 0.01 : pFy->getNumber()) : aStart.getY());
447 }
448 }
449
450 if(!aGeoToUnit.isIdentity())
451 {
452 aStart *= aGeoToUnit;
453 fRadius = (aGeoToUnit * basegfx::B2DVector(fRadius, 0.0)).getLength();
454
455 if(bFocal)
456 {
457 aFocal *= aGeoToUnit;
458 }
459 }
460
461 rTarget.push_back(
463 aGradientTransform,
464 rPath,
465 std::move(aSvgGradientEntryVector),
466 aStart,
467 fRadius,
469 rFillGradient.getSpreadMethod(),
470 bFocal ? &aFocal : nullptr));
471 }
472 }
473
475 const basegfx::B2DPolyPolygon& rPath,
477 const SvgPatternNode& rFillPattern,
478 const basegfx::B2DRange& rGeoRange) const
479 {
480 // prepare fill polyPolygon with given pattern, check for patternTransform
481 if(rFillPattern.getPatternTransform() && !rFillPattern.getPatternTransform()->isIdentity())
482 {
483 // PatternTransform is active; Handle by filling the inverse transformed
484 // path and back-transforming the result
485 basegfx::B2DPolyPolygon aPath(rPath);
486 basegfx::B2DHomMatrix aInv(*rFillPattern.getPatternTransform());
488
489 aInv.invert();
490 aPath.transform(aInv);
491 add_fillPattern(aPath, aNewTarget, rFillPattern, aPath.getB2DRange());
492
493 if(!aNewTarget.empty())
494 {
495 rTarget.push_back(
497 *rFillPattern.getPatternTransform(),
498 std::move(aNewTarget)));
499 }
500 }
501 else
502 {
503 // no patternTransform, create fillPattern directly
504 add_fillPattern(rPath, rTarget, rFillPattern, rGeoRange);
505 }
506 }
507
509 const basegfx::B2DPolyPolygon& rPath,
511 const SvgPatternNode& rFillPattern,
512 const basegfx::B2DRange& rGeoRange) const
513 {
514 // fill polyPolygon with given pattern
516
517 if(rPrimitives.empty())
518 return;
519
520 double fTargetWidth(rGeoRange.getWidth());
521 double fTargetHeight(rGeoRange.getHeight());
522
523 if(fTargetWidth <= 0.0 || fTargetHeight <= 0.0)
524 return;
525
526 // get relative values from pattern
527 double fX(0.0);
528 double fY(0.0);
529 double fW(0.0);
530 double fH(0.0);
531
532 rFillPattern.getValuesRelative(fX, fY, fW, fH, rGeoRange, mrOwner);
533
534 if(fW <= 0.0 || fH <= 0.0)
535 return;
536
537 // build the reference range relative to the rGeoRange
538 const basegfx::B2DRange aReferenceRange(fX, fY, fX + fW, fY + fH);
539
540 // find out how the content is mapped to the reference range
541 basegfx::B2DHomMatrix aMapPrimitivesToUnitRange;
542 const basegfx::B2DRange* pViewBox = rFillPattern.getViewBox();
543
544 if(pViewBox)
545 {
546 // use viewBox/preserveAspectRatio
547 const SvgAspectRatio& rRatio = rFillPattern.getSvgAspectRatio();
548 const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
549
550 if(rRatio.isSet())
551 {
552 // let mapping be created from SvgAspectRatio
553 aMapPrimitivesToUnitRange = rRatio.createMapping(aUnitRange, *pViewBox);
554 }
555 else
556 {
557 // choose default mapping
558 aMapPrimitivesToUnitRange = SvgAspectRatio::createLinearMapping(aUnitRange, *pViewBox);
559 }
560 }
561 else
562 {
563 // use patternContentUnits
564 const SvgUnits aPatternContentUnits(rFillPattern.getPatternContentUnits() ? *rFillPattern.getPatternContentUnits() : SvgUnits::userSpaceOnUse);
565
566 if (SvgUnits::userSpaceOnUse == aPatternContentUnits)
567 {
568 // create relative mapping to unit coordinates
569 aMapPrimitivesToUnitRange.scale(1.0 / (fW * fTargetWidth), 1.0 / (fH * fTargetHeight));
570 }
571 else
572 {
573 aMapPrimitivesToUnitRange.scale(1.0 / fW, 1.0 / fH);
574 }
575 }
576
577 // apply aMapPrimitivesToUnitRange to content when used
579
580 if(!aMapPrimitivesToUnitRange.isIdentity())
581 {
584 aMapPrimitivesToUnitRange,
585 std::move(aPrimitives)));
586
588 }
589
590 // embed in PatternFillPrimitive2D
591 rTarget.push_back(
593 rPath,
594 std::move(aPrimitives),
595 aReferenceRange));
596 }
597
599 const basegfx::B2DPolyPolygon& rPath,
601 const basegfx::B2DRange& rGeoRange) const
602 {
603 const basegfx::BColor* pFill = getFill();
604 const SvgGradientNode* pFillGradient = getSvgGradientNodeFill();
605 const SvgPatternNode* pFillPattern = getSvgPatternNodeFill();
606
607 if(!(pFill || pFillGradient || pFillPattern))
608 return;
609
610 const double fFillOpacity(getFillOpacity().solve(mrOwner));
611
612 if(!basegfx::fTools::more(fFillOpacity, 0.0))
613 return;
614
616
617 if(pFillGradient)
618 {
619 // create fill content with SVG gradient primitive
620 add_fillGradient(rPath, aNewFill, *pFillGradient, rGeoRange);
621 }
622 else if(pFillPattern)
623 {
624 // create fill content with SVG pattern primitive
625 add_fillPatternTransform(rPath, aNewFill, *pFillPattern, rGeoRange);
626 }
627 else // if(pFill)
628 {
629 // create fill content
630 aNewFill.resize(1);
632 rPath,
633 *pFill);
634 }
635
636 if(aNewFill.empty())
637 return;
638
639 if(basegfx::fTools::less(fFillOpacity, 1.0))
640 {
641 // embed in UnifiedTransparencePrimitive2D
642 rTarget.push_back(
644 std::move(aNewFill),
645 1.0 - fFillOpacity));
646 }
647 else
648 {
649 // append
650 rTarget.append(aNewFill);
651 }
652 }
653
655 const basegfx::B2DPolyPolygon& rPath,
657 const basegfx::B2DRange& rGeoRange) const
658 {
659 const basegfx::BColor* pStroke = getStroke();
660 const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke();
661 const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke();
662
663 if(!(pStroke || pStrokeGradient || pStrokePattern))
664 return;
665
667 const double fStrokeOpacity(getStrokeOpacity().solve(mrOwner));
668
669 if(!basegfx::fTools::more(fStrokeOpacity, 0.0))
670 return;
671
672 // get stroke width; SVG does not use 0.0 == hairline, so 0.0 is no line at all
673 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner) : 1.0);
674
675 if(!basegfx::fTools::more(fStrokeWidth, 0.0))
676 return;
677
679
680 // if we have a line with two identical points it is not really a line,
681 // but used by SVG sometimes to paint a single dot.In that case, create
682 // the geometry for a single dot
683 if(1 == rPath.count())
684 {
685 const basegfx::B2DPolygon& aSingle(rPath.getB2DPolygon(0));
686
687 if(2 == aSingle.count() && aSingle.getB2DPoint(0).equal(aSingle.getB2DPoint(1)))
688 {
692 aSingle.getB2DPoint(0),
693 fStrokeWidth * (1.44 * 0.5))),
694 pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0));
695 }
696 }
697
698 if(!aNewLinePrimitive.is())
699 {
700 // get LineJoin, LineCap and stroke array
702 const css::drawing::LineCap aLineCap(StrokeLinecapToDrawingLineCap(getStrokeLinecap()));
703 ::std::vector< double > aDashArray;
704
705 if(!getStrokeDasharray().empty())
706 {
708 }
709
710 // convert svg:stroke-miterlimit to LineAttrute:mfMiterMinimumAngle
711 // The default needs to be set explicitly, because svg default <> Draw default
712 double fMiterMinimumAngle;
713 if (getStrokeMiterLimit().isSet())
714 {
715 fMiterMinimumAngle = 2.0 * asin(1.0/getStrokeMiterLimit().getNumber());
716 }
717 else
718 {
719 fMiterMinimumAngle = 2.0 * asin(0.25); // 1.0/default 4.0
720 }
721
722 // todo: Handle getStrokeDashOffset()
723
724 // prepare line attribute
725 const drawinglayer::attribute::LineAttribute aLineAttribute(
726 pStroke ? *pStroke : basegfx::BColor(0.0, 0.0, 0.0),
727 fStrokeWidth,
728 aB2DLineJoin,
729 aLineCap,
730 fMiterMinimumAngle);
731
732 if(aDashArray.empty())
733 {
735 rPath,
736 aLineAttribute);
737 }
738 else
739 {
740 drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashArray));
741
743 rPath,
744 aLineAttribute,
745 std::move(aStrokeAttribute));
746 }
747 }
748
749 if(pStrokeGradient || pStrokePattern)
750 {
751 // put primitive into Primitive2DReference and Primitive2DSequence
752 const drawinglayer::primitive2d::Primitive2DContainer aSeq { aNewLinePrimitive };
753
754 // use neutral ViewInformation and create LineGeometryExtractor2D
755 const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
756 drawinglayer::processor2d::LineGeometryExtractor2D aExtractor(aViewInformation2D);
757
758 // process
759 aExtractor.process(aSeq);
760
761 // check for fill rsults
762 const basegfx::B2DPolyPolygonVector& rLineFillVector(aExtractor.getExtractedLineFills());
763
764 if(!rLineFillVector.empty())
765 {
766 const basegfx::B2DPolyPolygon aMergedArea(
768 rLineFillVector));
769
770 if(aMergedArea.count())
771 {
772 if(pStrokeGradient)
773 {
774 // create fill content with SVG gradient primitive. Use original GeoRange,
775 // e.g. from circle without LineWidth
776 add_fillGradient(aMergedArea, aNewStroke, *pStrokeGradient, rGeoRange);
777 }
778 else // if(pStrokePattern)
779 {
780 // create fill content with SVG pattern primitive. Use GeoRange
781 // from the expanded data, e.g. circle with extended geo by half linewidth
782 add_fillPatternTransform(aMergedArea, aNewStroke, *pStrokePattern, aMergedArea.getB2DRange());
783 }
784 }
785 }
786 }
787 else // if(pStroke)
788 {
789 aNewStroke.push_back(aNewLinePrimitive);
790 }
791
792 if(aNewStroke.empty())
793 return;
794
795 if(basegfx::fTools::less(fStrokeOpacity, 1.0))
796 {
797 // embed in UnifiedTransparencePrimitive2D
798 rTarget.push_back(
800 std::move(aNewStroke),
801 1.0 - fStrokeOpacity));
802 }
803 else
804 {
805 // append
806 rTarget.append(aNewStroke);
807 }
808 }
809
812 basegfx::B2DHomMatrix& rMarkerTransform,
813 basegfx::B2DRange& rClipRange,
814 const SvgMarkerNode& rMarker) const
815 {
816 // reset return values
817 rMarkerTransform.identity();
818 rClipRange.reset();
819
820 // get marker primitive representation
821 rMarkerPrimitives = rMarker.getMarkerPrimitives();
822
823 if(!rMarkerPrimitives.empty())
824 {
825 basegfx::B2DRange aPrimitiveRange(0.0, 0.0, 1.0, 1.0);
826 const basegfx::B2DRange* pViewBox = rMarker.getViewBox();
827
828 if(pViewBox)
829 {
830 aPrimitiveRange = *pViewBox;
831 }
832
833 if(aPrimitiveRange.getWidth() > 0.0 && aPrimitiveRange.getHeight() > 0.0)
834 {
835 double fTargetWidth(rMarker.getMarkerWidth().isSet() ? rMarker.getMarkerWidth().solve(mrOwner, NumberType::xcoordinate) : 3.0);
836 double fTargetHeight(rMarker.getMarkerHeight().isSet() ? rMarker.getMarkerHeight().solve(mrOwner, NumberType::xcoordinate) : 3.0);
837 const bool bStrokeWidth(SvgMarkerNode::MarkerUnits::strokeWidth == rMarker.getMarkerUnits());
838 const double fStrokeWidth(getStrokeWidth().isSet() ? getStrokeWidth().solve(mrOwner) : 1.0);
839
840 if(bStrokeWidth)
841 {
842 // relative to strokeWidth
843 fTargetWidth *= fStrokeWidth;
844 fTargetHeight *= fStrokeWidth;
845 }
846
847 if(fTargetWidth > 0.0 && fTargetHeight > 0.0)
848 {
849 // create mapping
850 const basegfx::B2DRange aTargetRange(0.0, 0.0, fTargetWidth, fTargetHeight);
851 const SvgAspectRatio& rRatio = rMarker.getSvgAspectRatio();
852
853 if(rRatio.isSet())
854 {
855 // let mapping be created from SvgAspectRatio
856 rMarkerTransform = rRatio.createMapping(aTargetRange, aPrimitiveRange);
857
858 if(rRatio.isMeetOrSlice())
859 {
860 // need to clip
861 rClipRange = aPrimitiveRange;
862 }
863 }
864 else
865 {
866 if(!pViewBox)
867 {
868 if(bStrokeWidth)
869 {
870 // adapt to strokewidth if needed
871 rMarkerTransform.scale(fStrokeWidth, fStrokeWidth);
872 }
873 }
874 else
875 {
876 // choose default mapping
877 rMarkerTransform = SvgAspectRatio::createLinearMapping(aTargetRange, aPrimitiveRange);
878 }
879 }
880
881 // get and apply reference point. Initially it's in marker local coordinate system
882 basegfx::B2DPoint aRefPoint(
883 rMarker.getRefX().isSet() ? rMarker.getRefX().solve(mrOwner, NumberType::xcoordinate) : 0.0,
884 rMarker.getRefY().isSet() ? rMarker.getRefY().solve(mrOwner, NumberType::ycoordinate) : 0.0);
885
886 // apply MarkerTransform to have it in mapped coordinates
887 aRefPoint *= rMarkerTransform;
888
889 // apply by moving RepPoint to (0.0)
890 rMarkerTransform.translate(-aRefPoint.getX(), -aRefPoint.getY());
891
892 return true;
893 }
894 }
895 }
896
897 return false;
898 }
899
901 const basegfx::B2DPolyPolygon& rPath,
903 const basegfx::utils::PointIndexSet* pHelpPointIndices) const
904 {
905 // try to access linked markers
906 const SvgMarkerNode* pStart = accessMarkerStartXLink();
907 const SvgMarkerNode* pMid = accessMarkerMidXLink();
908 const SvgMarkerNode* pEnd = accessMarkerEndXLink();
909
910 if(!(pStart || pMid || pEnd))
911 return;
912
913 const sal_uInt32 nSubPathCount(rPath.count());
914
915 if(!nSubPathCount)
916 return;
917
918 // remember prepared marker; pStart, pMid and pEnd may all be equal when
919 // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end',
920 // see 'case SVGToken::Marker' in this file; thus in this case only one common
921 // marker in primitive form will be prepared
922 const SvgMarkerNode* pPrepared = nullptr;
923
924 // values for the prepared marker, results of prepare_singleMarker
925 drawinglayer::primitive2d::Primitive2DContainer aPreparedMarkerPrimitives;
926 basegfx::B2DHomMatrix aPreparedMarkerTransform;
927 basegfx::B2DRange aPreparedMarkerClipRange;
928
929 for (sal_uInt32 a(0); a < nSubPathCount; a++)
930 {
931 // iterate over sub-paths
932 const basegfx::B2DPolygon& aSubPolygonPath(rPath.getB2DPolygon(a));
933 const sal_uInt32 nSubPolygonPointCount(aSubPolygonPath.count());
934 const bool bSubPolygonPathIsClosed(aSubPolygonPath.isClosed());
935
936 if(nSubPolygonPointCount)
937 {
938 // for each sub-path, create one marker per point (when closed, two markers
939 // need to pe created for the 1st point)
940 const sal_uInt32 nTargetMarkerCount(bSubPolygonPathIsClosed ? nSubPolygonPointCount + 1 : nSubPolygonPointCount);
941
942 for (sal_uInt32 b(0); b < nTargetMarkerCount; b++)
943 {
944 const bool bIsFirstMarker(!a && !b);
945 const bool bIsLastMarker(nSubPathCount - 1 == a && nTargetMarkerCount - 1 == b);
946 const SvgMarkerNode* pNeeded = nullptr;
947
948 if(bIsFirstMarker)
949 {
950 // 1st point in 1st sub-polygon, use pStart
951 pNeeded = pStart;
952 }
953 else if(bIsLastMarker)
954 {
955 // last point in last sub-polygon, use pEnd
956 pNeeded = pEnd;
957 }
958 else
959 {
960 // anything in-between, use pMid
961 pNeeded = pMid;
962 }
963
964 if(pHelpPointIndices && !pHelpPointIndices->empty())
965 {
967 pHelpPointIndices->find(basegfx::utils::PointIndex(a, b)));
968
969 if(aFound != pHelpPointIndices->end())
970 {
971 // this point is a pure helper point; do not create a marker for it
972 continue;
973 }
974 }
975
976 if(!pNeeded)
977 {
978 // no marker needs to be created for this point
979 continue;
980 }
981
982 if(pPrepared != pNeeded)
983 {
984 // if needed marker is not yet prepared, do it now
985 if(prepare_singleMarker(aPreparedMarkerPrimitives, aPreparedMarkerTransform, aPreparedMarkerClipRange, *pNeeded))
986 {
987 pPrepared = pNeeded;
988 }
989 else
990 {
991 // error: could not prepare given marker
992 OSL_ENSURE(false, "OOps, could not prepare given marker as primitives (!)");
993 pPrepared = nullptr;
994 continue;
995 }
996 }
997
998 // prepare complete transform
999 basegfx::B2DHomMatrix aCombinedTransform(aPreparedMarkerTransform);
1000
1001 // get rotation
1004 {
1005 const sal_uInt32 nPointIndex(b % nSubPolygonPointCount);
1006
1007 // get entering and leaving tangents; this will search backward/forward
1008 // in the polygon to find tangents unequal to zero, skipping empty edges
1009 // see basegfx descriptions)
1010 // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker
1011 // and entering tangent for end marker. To achieve this (if wanted) it is possible
1012 // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker.
1013 // This is not done here, see comment 14 in task #1232379#
1014 // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute
1015 basegfx::B2DVector aEntering(
1017 aSubPolygonPath,
1018 nPointIndex));
1019 basegfx::B2DVector aLeaving(
1021 aSubPolygonPath,
1022 nPointIndex));
1023 const bool bEntering(!aEntering.equalZero());
1024 const bool bLeaving(!aLeaving.equalZero());
1025
1026 if(bEntering || bLeaving)
1027 {
1028 basegfx::B2DVector aSum(0.0, 0.0);
1029
1030 if(bEntering)
1031 {
1032 if(bIsFirstMarker && pPrepared->getMarkerOrient() == SvgMarkerNode::MarkerOrient::auto_start_reverse)
1033 aSum -= aEntering.normalize();
1034 else
1035 aSum += aEntering.normalize();
1036 }
1037
1038 if(bLeaving)
1039 {
1040 if(bIsFirstMarker && pPrepared->getMarkerOrient() == SvgMarkerNode::MarkerOrient::auto_start_reverse)
1041 aSum -= aLeaving.normalize();
1042 else
1043 aSum += aLeaving.normalize();
1044 }
1045
1046 if(!aSum.equalZero())
1047 {
1048 const double fAngle(atan2(aSum.getY(), aSum.getX()));
1049
1050 // apply rotation
1051 aCombinedTransform.rotate(fAngle);
1052 }
1053 }
1054 }
1055 else
1056 {
1057 // apply rotation
1058 aCombinedTransform.rotate(pPrepared->getAngle());
1059 }
1060
1061 // get and apply target position
1062 const basegfx::B2DPoint aPoint(aSubPolygonPath.getB2DPoint(b % nSubPolygonPointCount));
1063
1064 aCombinedTransform.translate(aPoint.getX(), aPoint.getY());
1065
1066 // prepare marker
1069 aCombinedTransform,
1070 drawinglayer::primitive2d::Primitive2DContainer(aPreparedMarkerPrimitives)));
1071
1072 if(!aPreparedMarkerClipRange.isEmpty())
1073 {
1074 // marker needs to be clipped, it's bigger as the mapping
1075 basegfx::B2DPolyPolygon aClipPolygon(basegfx::utils::createPolygonFromRect(aPreparedMarkerClipRange));
1076
1077 aClipPolygon.transform(aCombinedTransform);
1079 std::move(aClipPolygon),
1081 }
1082
1083 // add marker
1084 rTarget.push_back(xMarker);
1085 }
1086 }
1087 }
1088 }
1089
1091 const basegfx::B2DPolyPolygon& rPath,
1093 const basegfx::utils::PointIndexSet* pHelpPointIndices) const
1094 {
1095 if(!rPath.count())
1096 {
1097 // no geometry at all
1098 return;
1099 }
1100
1101 const basegfx::B2DRange aGeoRange(rPath.getB2DRange());
1102
1103 if(aGeoRange.isEmpty())
1104 {
1105 // no geometry range
1106 return;
1107 }
1108
1109 const double fOpacity(getOpacity().solve(mrOwner));
1110
1111 if(basegfx::fTools::equalZero(fOpacity))
1112 {
1113 // not visible
1114 return;
1115 }
1116
1117 // check if it's a line
1118 const bool bNoWidth(basegfx::fTools::equalZero(aGeoRange.getWidth()));
1119 const bool bNoHeight(basegfx::fTools::equalZero(aGeoRange.getHeight()));
1120 const bool bIsTwoPointLine(1 == rPath.count()
1121 && !rPath.areControlPointsUsed()
1122 && 2 == rPath.getB2DPolygon(0).count());
1123 const bool bIsLine(bIsTwoPointLine || bNoWidth || bNoHeight);
1124
1125 if(!bIsLine)
1126 {
1127 // create fill
1128 basegfx::B2DPolyPolygon aPath(rPath);
1129
1132 {
1134 {
1136 {
1137 // nonzero is wanted, solve geometrically (see description on basegfx)
1138 // basegfx::utils::createNonzeroConform() is expensive for huge paths
1139 // and is only needed if path will be filled later on
1141 }
1142 }
1143 }
1144
1145 add_fill(aPath, rTarget, aGeoRange);
1146 }
1147
1148 // create stroke
1149 add_stroke(rPath, rTarget, aGeoRange);
1150
1151 // Svg supports markers for path, polygon, polyline and line
1152 if(SVGToken::Path == mrOwner.getType() || // path
1153 SVGToken::Polygon == mrOwner.getType() || // polygon
1154 SVGToken::Polyline == mrOwner.getType() || // polyline
1155 SVGToken::Line == mrOwner.getType() || // line
1156 SVGToken::Style == mrOwner.getType()) // tdf#150323
1157 {
1158 // try to add markers
1159 add_markers(rPath, rTarget, pHelpPointIndices);
1160 }
1161 }
1162
1166 const std::optional<basegfx::B2DHomMatrix>& pTransform) const
1167 {
1168 const double fOpacity(getOpacity().solve(mrOwner));
1169
1170 if(basegfx::fTools::equalZero(fOpacity))
1171 {
1172 return;
1173 }
1174
1175 drawinglayer::primitive2d::Primitive2DContainer aSource(std::move(rSource));
1176
1177 if(basegfx::fTools::less(fOpacity, 1.0))
1178 {
1179 // embed in UnifiedTransparencePrimitive2D
1182 std::move(aSource),
1183 1.0 - fOpacity));
1184
1186 }
1187
1188 if(pTransform)
1189 {
1190 // create embedding group element with transformation. This applies the given
1191 // transformation to the graphical content, but *not* to mask and/or clip (as needed)
1194 *pTransform,
1195 std::move(aSource)));
1196
1198 }
1199
1200 const SvgClipPathNode* pClip = accessClipPathXLink();
1201 while(pClip)
1202 {
1203 // #i124852# transform may be needed when SvgUnits::userSpaceOnUse
1204 pClip->apply(aSource, pTransform);
1205 pClip = pClip->getSvgStyleAttributes()->accessClipPathXLink();
1206 }
1207
1208 if(!aSource.empty()) // test again, applied clipPath may have lead to empty geometry
1209 {
1210 const SvgFilterNode* pFilter = accessFilterXLink();
1211 if(pFilter)
1212 {
1213 pFilter->apply(aSource);
1214 }
1215 }
1216
1217 if(!aSource.empty()) // test again, applied filter may have lead to empty geometry
1218 {
1219
1220 const SvgMaskNode* pMask = accessMaskXLink();
1221 if(pMask)
1222 {
1223 // #i124852# transform may be needed when SvgUnits::userSpaceOnUse
1224 pMask->apply(aSource, pTransform);
1225 }
1226 }
1227
1228 // This is part of the SVG import of self-written SVGs from
1229 // Draw/Impress containing multiple Slides/Pages. To be able
1230 // to later 'break' these to multiple Pages if wanted, embed
1231 // each Page-Content in an identifiable Primitive Grouping
1232 // Object.
1233 // This is the case when the current Node is a GroupNode, has
1234 // class="Page" set, has a parent that also is a GroupNode
1235 // at which class="Slide" is set.
1236 // Multiple Slides/Pages are possible for Draw and Impress.
1238 {
1239 const OUString aOwnerClass(*mrOwner.getClass());
1240
1241 if("Page" == aOwnerClass)
1242 {
1243 const SvgNode* pParent(mrOwner.getParent());
1244
1245 if(nullptr != pParent && SVGToken::G == pParent->getType() && pParent->getClass())
1246 {
1247 const OUString aParentClass(*pParent->getClass());
1248
1249 if("Slide" == aParentClass)
1250 {
1251 // embed to grouping primitive to identify the
1252 // Slide/Page information
1255 std::move(aSource)));
1256
1258 }
1259 }
1260 }
1261 }
1262
1263 if(!aSource.empty()) // test again, applied mask may have lead to empty geometry
1264 {
1265 // append to current target
1266 rTarget.append(aSource);
1267 }
1268 }
1269
1271 : mrOwner(rOwner),
1272 mpCssStyleParent(nullptr),
1273 maStopColor(basegfx::BColor(0.0, 0.0, 0.0), true),
1274 maStrokeLinecap(StrokeLinecap::notset),
1275 maStrokeLinejoin(StrokeLinejoin::notset),
1276 maFontSize(),
1277 maFontStretch(FontStretch::notset),
1278 maFontStyle(FontStyle::notset),
1279 maFontWeight(FontWeight::notset),
1280 maTextAlign(TextAlign::notset),
1281 maTextDecoration(TextDecoration::notset),
1282 maTextAnchor(TextAnchor::notset),
1283 maVisibility(Visibility::notset),
1284 maFillRule(FillRule::notset),
1285 maClipRule(FillRule::notset),
1286 maBaselineShift(BaselineShift::Baseline),
1287 maBaselineShiftNumber(0),
1288 maResolvingParent(29, 0),
1289 mbIsClipPathContent(SVGToken::ClipPathNode == mrOwner.getType()),
1290 mbStrokeDasharraySet(false)
1291 {
1292 const SvgStyleAttributes* pParentStyle = getParentStyle();
1294 {
1295 if(pParentStyle)
1296 {
1298 }
1299 }
1300 }
1301
1303 {
1304 }
1305
1307 SVGToken aSVGToken,
1308 const OUString& aContent)
1309 {
1310 switch(aSVGToken)
1311 {
1312 case SVGToken::Fill:
1313 {
1314 SvgPaint aSvgPaint;
1315 OUString aURL;
1316 SvgNumber aOpacity;
1317
1318 if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity))
1319 {
1320 setFill(aSvgPaint);
1321 if(aOpacity.isSet())
1322 {
1323 setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0)));
1324 }
1325 }
1326 else if(!aURL.isEmpty())
1327 {
1329 }
1330 break;
1331 }
1333 {
1334 SvgNumber aNum;
1335
1336 if(readSingleNumber(aContent, aNum))
1337 {
1338 maFillOpacity = SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet());
1339 }
1340 break;
1341 }
1342 case SVGToken::FillRule:
1343 {
1344 if(!aContent.isEmpty())
1345 {
1347 {
1349 }
1351 {
1353 }
1354 }
1355 break;
1356 }
1357 case SVGToken::Stroke:
1358 {
1359 SvgPaint aSvgPaint;
1360 OUString aURL;
1361 SvgNumber aOpacity;
1362
1363 if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity))
1364 {
1365 maStroke = aSvgPaint;
1366 if(aOpacity.isSet())
1367 {
1368 setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0)));
1369 }
1370 }
1371 else if(!aURL.isEmpty())
1372 {
1374 }
1375 break;
1376 }
1378 {
1379 if(!aContent.isEmpty())
1380 {
1381 SvgNumberVector aVector;
1382
1383 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"none"))
1384 {
1385 // #121221# The special value 'none' needs to be handled
1386 // in the sense that *when* it is set, the parent shall not
1387 // be used. Before this was only dependent on the array being
1388 // empty
1389 mbStrokeDasharraySet = true;
1390 }
1391 else if(readSvgNumberVector(aContent, aVector))
1392 {
1393 maStrokeDasharray = aVector;
1394 }
1395 }
1396 break;
1397 }
1399 {
1400 SvgNumber aNum;
1401
1402 if(readSingleNumber(aContent, aNum))
1403 {
1404 if(aNum.isPositive())
1405 {
1406 maStrokeDashOffset = aNum;
1407 }
1408 }
1409 break;
1410 }
1412 {
1413 if(!aContent.isEmpty())
1414 {
1415 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"butt"))
1416 {
1418 }
1419 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"round"))
1420 {
1422 }
1423 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"square"))
1424 {
1426 }
1427 }
1428 break;
1429 }
1431 {
1432 if(!aContent.isEmpty())
1433 {
1434 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"miter"))
1435 {
1437 }
1438 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"round"))
1439 {
1441 }
1442 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"bevel"))
1443 {
1445 }
1446 }
1447 break;
1448 }
1450 {
1451 SvgNumber aNum;
1452
1453 if(readSingleNumber(aContent, aNum))
1454 {
1456 { //readSingleNumber sets SvgUnit::px as default, if unit is missing. Correct it here.
1458 }
1459 }
1460 break;
1461 }
1463 {
1464
1465 SvgNumber aNum;
1466
1467 if(readSingleNumber(aContent, aNum))
1468 {
1469 maStrokeOpacity = SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet());
1470 }
1471 break;
1472 }
1474 {
1475 SvgNumber aNum;
1476
1477 if(readSingleNumber(aContent, aNum))
1478 {
1479 if(aNum.isPositive())
1480 {
1481 maStrokeWidth = aNum;
1482 }
1483 }
1484 break;
1485 }
1487 {
1488 SvgPaint aSvgPaint;
1489 OUString aURL;
1490 SvgNumber aOpacity;
1491
1492 if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity))
1493 {
1494 maStopColor = aSvgPaint;
1495 if(aOpacity.isSet())
1496 {
1497 setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0)));
1498 }
1499 }
1500 break;
1501 }
1503 {
1504 SvgNumber aNum;
1505
1506 if(readSingleNumber(aContent, aNum))
1507 {
1508 if(aNum.isPositive())
1509 {
1510 maStopOpacity = aNum;
1511 }
1512 }
1513 break;
1514 }
1515 case SVGToken::Font:
1516 {
1517 break;
1518 }
1520 {
1521 SvgStringVector aSvgStringVector;
1522
1523 if(readSvgStringVector(aContent, aSvgStringVector))
1524 {
1525 maFontFamily = aSvgStringVector;
1526 }
1527 break;
1528 }
1529 case SVGToken::FontSize:
1530 {
1531 if(!aContent.isEmpty())
1532 {
1533 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"xx-small"))
1534 {
1536 }
1537 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"x-small"))
1538 {
1540 }
1541 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"small"))
1542 {
1544 }
1545 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"smaller"))
1546 {
1548 }
1549 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"medium"))
1550 {
1552 }
1553 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"larger"))
1554 {
1556 }
1557 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"large"))
1558 {
1560 }
1561 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"x-large"))
1562 {
1564 }
1565 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"xx-large"))
1566 {
1568 }
1569 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"initial"))
1570 {
1572 }
1573 else
1574 {
1575 SvgNumber aNum;
1576
1577 if(readSingleNumber(aContent, aNum))
1578 {
1579 maFontSizeNumber = aNum;
1580 }
1581 }
1582 }
1583 break;
1584 }
1586 {
1587 break;
1588 }
1590 {
1591 if(!aContent.isEmpty())
1592 {
1593 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"normal"))
1594 {
1596 }
1597 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"wider"))
1598 {
1600 }
1601 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"narrower"))
1602 {
1604 }
1605 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"ultra-condensed"))
1606 {
1608 }
1609 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"extra-condensed"))
1610 {
1612 }
1613 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"condensed"))
1614 {
1616 }
1617 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"semi-condensed"))
1618 {
1620 }
1621 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"semi-expanded"))
1622 {
1624 }
1625 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"expanded"))
1626 {
1628 }
1629 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"extra-expanded"))
1630 {
1632 }
1633 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"ultra-expanded"))
1634 {
1636 }
1637 }
1638 break;
1639 }
1641 {
1642 if(!aContent.isEmpty())
1643 {
1644 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"normal"))
1645 {
1647 }
1648 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"italic"))
1649 {
1651 }
1652 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"oblique"))
1653 {
1655 }
1656 }
1657 break;
1658 }
1660 {
1661 break;
1662 }
1664 {
1665 if(!aContent.isEmpty())
1666 {
1667 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"100"))
1668 {
1670 }
1671 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"200"))
1672 {
1674 }
1675 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"300"))
1676 {
1678 }
1679 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"400") || o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"normal"))
1680 {
1682 }
1683 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"500"))
1684 {
1686 }
1687 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"600"))
1688 {
1690 }
1691 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"700") || o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"bold"))
1692 {
1694 }
1695 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"800"))
1696 {
1698 }
1699 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"900"))
1700 {
1702 }
1703 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"bolder"))
1704 {
1706 }
1707 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"lighter"))
1708 {
1710 }
1711 }
1712 break;
1713 }
1715 {
1716 break;
1717 }
1719 {
1720 break;
1721 }
1723 {
1724 if(!aContent.isEmpty())
1725 {
1726 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"none"))
1727 {
1729 }
1730 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"underline"))
1731 {
1733 }
1734 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"overline"))
1735 {
1737 }
1738 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"line-through"))
1739 {
1741 }
1742 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"blink"))
1743 {
1745 }
1746 }
1747 break;
1748 }
1750 {
1751 break;
1752 }
1754 {
1755 break;
1756 }
1758 {
1759 if(!aContent.isEmpty())
1760 {
1761 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"start"))
1762 {
1764 }
1765 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"middle"))
1766 {
1768 }
1769 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"end"))
1770 {
1772 }
1773 }
1774 break;
1775 }
1777 {
1778 if(!aContent.isEmpty())
1779 {
1780 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"left"))
1781 {
1783 }
1784 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"right"))
1785 {
1787 }
1788 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"center"))
1789 {
1791 }
1792 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"justify"))
1793 {
1795 }
1796 }
1797 break;
1798 }
1799 case SVGToken::Color:
1800 {
1801 SvgPaint aSvgPaint;
1802 OUString aURL;
1803 SvgNumber aOpacity;
1804
1805 if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity))
1806 {
1807 maColor = aSvgPaint;
1808 if(aOpacity.isSet())
1809 {
1810 setOpacity(SvgNumber(std::clamp(aOpacity.getNumber(), 0.0, 1.0)));
1811 }
1812 }
1813 break;
1814 }
1815 case SVGToken::Opacity:
1816 {
1817 SvgNumber aNum;
1818
1819 if(readSingleNumber(aContent, aNum))
1820 {
1821 setOpacity(SvgNumber(std::clamp(aNum.getNumber(), 0.0, 1.0), aNum.getUnit(), aNum.isSet()));
1822 }
1823 break;
1824 }
1826 {
1827 if(!aContent.isEmpty())
1828 {
1829 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"visible"))
1830 {
1832 }
1833 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"hidden"))
1834 {
1836 }
1837 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"collapse"))
1838 {
1840 }
1841 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"inherit"))
1842 {
1844 }
1845 }
1846 break;
1847 }
1848 case SVGToken::Title:
1849 {
1850 maTitle = aContent;
1851 break;
1852 }
1853 case SVGToken::Desc:
1854 {
1855 maDesc = aContent;
1856 break;
1857 }
1859 {
1860 readLocalUrl(aContent, maClipPathXLink);
1861 break;
1862 }
1863 case SVGToken::Filter:
1864 {
1865 readLocalUrl(aContent, maFilterXLink);
1866 break;
1867 }
1868 case SVGToken::Mask:
1869 {
1870 readLocalUrl(aContent, maMaskXLink);
1871 break;
1872 }
1873 case SVGToken::ClipRule:
1874 {
1875 if(!aContent.isEmpty())
1876 {
1878 {
1880 }
1882 {
1884 }
1885 }
1886 break;
1887 }
1888 case SVGToken::Marker:
1889 {
1890 // tdf#155819: Using the marker property from a style sheet is equivalent to using all three (start, mid, end).
1892 {
1893 readLocalUrl(aContent, maMarkerEndXLink);
1895 }
1896 break;
1897 }
1899 {
1901 break;
1902 }
1904 {
1905 readLocalUrl(aContent, maMarkerMidXLink);
1906 break;
1907 }
1909 {
1910 readLocalUrl(aContent, maMarkerEndXLink);
1911 break;
1912 }
1913 case SVGToken::Display:
1914 {
1915 // There may be display:none statements inside of style defines, e.g. the following line:
1916 // style="display:none"
1917 // taken from a svg example; this needs to be parsed and set at the owning node. Do not call
1918 // mrOwner.parseAttribute(...) here, this would lead to a recursion
1919 if(!aContent.isEmpty())
1920 {
1922 }
1923 break;
1924 }
1926 {
1927 if(!aContent.isEmpty())
1928 {
1929 SvgNumber aNum;
1930
1931 if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"sub"))
1932 {
1934 }
1935 else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"super"))
1936 {
1938 }
1939 else if(readSingleNumber(aContent, aNum))
1940 {
1941 maBaselineShiftNumber = aNum;
1942
1943 if(SvgUnit::percent == aNum.getUnit())
1944 {
1946 }
1947 else
1948 {
1950 }
1951 }
1952 else
1953 {
1954 // no BaselineShift or inherit (which is automatically)
1956 }
1957 }
1958 break;
1959 }
1960 default:
1961 {
1962 break;
1963 }
1964 }
1965 }
1966
1967 // #i125258# ask if fill is a direct hard attribute (no hierarchy)
1969 {
1971 {
1972 return false;
1973 }
1974 else if(maFill.isSet())
1975 {
1976 return true;
1977 }
1978
1979 return false;
1980 }
1981
1983 {
1984 static basegfx::BColor aBlack(0.0, 0.0, 0.0);
1985 const basegfx::BColor *aColor = getColor();
1986 if( aColor )
1987 return aColor;
1988 else
1989 return &aBlack;
1990 }
1991
1993 {
1994 if(maFill.isSet())
1995 {
1996 if(maFill.isCurrent())
1997 {
1998 return getCurrentColor();
1999 }
2000 else if(maFill.isOn())
2001 {
2002 return &maFill.getBColor();
2003 }
2004 else if(mbIsClipPathContent)
2005 {
2006 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2007
2008 if (pSvgStyleAttributes && maResolvingParent[0] < nStyleDepthLimit)
2009 {
2010 ++maResolvingParent[0];
2011 const basegfx::BColor* pFill = pSvgStyleAttributes->getFill();
2012 --maResolvingParent[0];
2013
2014 return pFill;
2015 }
2016 }
2017 }
2018 else if (maNodeFillURL.isEmpty())
2019 {
2020 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2021
2022 if (pSvgStyleAttributes && maResolvingParent[0] < nStyleDepthLimit)
2023 {
2024 ++maResolvingParent[0];
2025 const basegfx::BColor* pFill = pSvgStyleAttributes->getFill();
2026 --maResolvingParent[0];
2027
2029 {
2030 if (pFill)
2031 {
2032 return pFill;
2033 }
2034 else
2035 {
2036 static basegfx::BColor aBlack(0.0, 0.0, 0.0);
2037 return &aBlack;
2038 }
2039 }
2040 else
2041 {
2042 return pFill;
2043 }
2044 }
2045 }
2046
2047 return nullptr;
2048 }
2049
2051 {
2052 if(maStroke.isSet())
2053 {
2054 if(maStroke.isCurrent())
2055 {
2056 return getCurrentColor();
2057 }
2058 else if(maStroke.isOn())
2059 {
2060 return &maStroke.getBColor();
2061 }
2062 }
2063 else if (maNodeStrokeURL.isEmpty())
2064 {
2065 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2066
2067 if (pSvgStyleAttributes && maResolvingParent[1] < nStyleDepthLimit)
2068 {
2069 ++maResolvingParent[1];
2070 auto ret = pSvgStyleAttributes->getStroke();
2071 --maResolvingParent[1];
2072 return ret;
2073 }
2074 }
2075
2076 return nullptr;
2077 }
2078
2080 {
2082 {
2083 return *getCurrentColor();
2084 }
2085 else
2086 {
2087 return maStopColor.getBColor();
2088 }
2089 }
2090
2092 {
2093 if (!maFill.isSet())
2094 {
2095 if (!maNodeFillURL.isEmpty())
2096 {
2098
2099 if(pNode)
2100 {
2102 {
2103 return static_cast< const SvgGradientNode* >(pNode);
2104 }
2105 }
2106 }
2107 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2108
2109 if (pSvgStyleAttributes && maResolvingParent[2] < nStyleDepthLimit)
2110 {
2111 ++maResolvingParent[2];
2112 auto ret = pSvgStyleAttributes->getSvgGradientNodeFill();
2113 --maResolvingParent[2];
2114 return ret;
2115 }
2116 }
2117
2118 return nullptr;
2119 }
2120
2122 {
2123 if (!maStroke.isSet())
2124 {
2125 if(!maNodeStrokeURL.isEmpty())
2126 {
2128
2129 if(pNode)
2130 {
2132 {
2133 return static_cast< const SvgGradientNode* >(pNode);
2134 }
2135 }
2136 }
2137
2138 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2139
2140 if (pSvgStyleAttributes && maResolvingParent[3] < nStyleDepthLimit)
2141 {
2142 ++maResolvingParent[3];
2143 auto ret = pSvgStyleAttributes->getSvgGradientNodeStroke();
2144 --maResolvingParent[3];
2145 return ret;
2146 }
2147 }
2148
2149 return nullptr;
2150 }
2151
2153 {
2154 if (!maFill.isSet())
2155 {
2156 if (!maNodeFillURL.isEmpty())
2157 {
2159
2160 if(pNode)
2161 {
2162 if(SVGToken::Pattern == pNode->getType())
2163 {
2164 return static_cast< const SvgPatternNode* >(pNode);
2165 }
2166 }
2167 }
2168
2169 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2170
2171 if (pSvgStyleAttributes && maResolvingParent[4] < nStyleDepthLimit)
2172 {
2173 ++maResolvingParent[4];
2174 auto ret = pSvgStyleAttributes->getSvgPatternNodeFill();
2175 --maResolvingParent[4];
2176 return ret;
2177 }
2178 }
2179
2180 return nullptr;
2181 }
2182
2184 {
2185 if (!maStroke.isSet())
2186 {
2187 if(!maNodeStrokeURL.isEmpty())
2188 {
2190
2191 if(pNode)
2192 {
2193 if(SVGToken::Pattern == pNode->getType())
2194 {
2195 return static_cast< const SvgPatternNode* >(pNode);
2196 }
2197 }
2198 }
2199
2200 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2201
2202 if (pSvgStyleAttributes && maResolvingParent[5] < nStyleDepthLimit)
2203 {
2204 ++maResolvingParent[5];
2205 auto ret = pSvgStyleAttributes->getSvgPatternNodeStroke();
2206 --maResolvingParent[5];
2207 return ret;
2208 }
2209 }
2210
2211 return nullptr;
2212 }
2213
2215 {
2216 if(maStrokeWidth.isSet())
2217 {
2218 return maStrokeWidth;
2219 }
2220
2221 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2222
2223 if (pSvgStyleAttributes && maResolvingParent[6] < nStyleDepthLimit)
2224 {
2225 ++maResolvingParent[6];
2226 auto ret = pSvgStyleAttributes->getStrokeWidth();
2227 --maResolvingParent[6];
2228 return ret;
2229 }
2230
2232 {
2233 return SvgNumber(0.0);
2234 }
2235
2236 // default is 1
2237 return SvgNumber(1.0);
2238 }
2239
2241 {
2242 if(maStopOpacity.isSet())
2243 {
2244 return maStopOpacity;
2245 }
2246
2247 // default is 1
2248 return SvgNumber(1.0);
2249 }
2250
2252 {
2253 if(maFillOpacity.isSet())
2254 {
2255 return maFillOpacity;
2256 }
2257
2258 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2259
2260 if (pSvgStyleAttributes && maResolvingParent[7] < nStyleDepthLimit)
2261 {
2262 ++maResolvingParent[7];
2263 auto ret = pSvgStyleAttributes->getFillOpacity();
2264 --maResolvingParent[7];
2265 return ret;
2266 }
2267
2268 // default is 1
2269 return SvgNumber(1.0);
2270 }
2271
2273 {
2274 if(maOpacity.isSet())
2275 {
2276 return maOpacity;
2277 }
2278
2279 // This is called from add_postProcess so only check the parent style
2280 // if it has a local css style, because it's the first in the stack
2282 {
2283 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2284
2285 if (pSvgStyleAttributes && pSvgStyleAttributes->maOpacity.isSet())
2286 {
2287 return pSvgStyleAttributes->maOpacity;
2288 }
2289 }
2290
2291 // default is 1
2292 return SvgNumber(1.0);
2293 }
2294
2296 {
2298 {
2299 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2300
2301 if (pSvgStyleAttributes && maResolvingParent[9] < nStyleDepthLimit)
2302 {
2303 ++maResolvingParent[9];
2304 auto ret = pSvgStyleAttributes->getVisibility();
2305 --maResolvingParent[9];
2306 return ret;
2307 }
2308 //default is Visible
2309 return Visibility::visible;
2310 }
2311
2312 // Visibility correction/exception for self-exported SVGs:
2313 // When Impress exports single or multi-page SVGs, it puts the
2314 // single slides into <g visibility="hidden">. Not sure why
2315 // this happens, but this leads (correctly) to empty imported
2316 // Graphics.
2317 // Thus, if Visibility::hidden is active and owner is a SVGToken::G
2318 // and it's parent is also a SVGToken::G and it has a Class 'SlideGroup'
2319 // set, check if we are an Impress export.
2320 // We are an Impress export if an SVG-Node titled 'ooo:meta_slides'
2321 // exists.
2322 // All together gives:
2325 && nullptr != mrOwner.getDocument().findSvgNodeById("ooo:meta_slides"))
2326 {
2327 const SvgNode* pParent(mrOwner.getParent());
2328
2329 if(nullptr != pParent && SVGToken::G == pParent->getType() && pParent->getClass())
2330 {
2331 const OUString aClass(*pParent->getClass());
2332
2333 if("SlideGroup" == aClass)
2334 {
2335 // if we detect this exception,
2336 // override Visibility::hidden -> Visibility::visible
2337 return Visibility::visible;
2338 }
2339 }
2340 }
2341
2342 return maVisibility;
2343 }
2344
2346 {
2348 {
2349 return maFillRule;
2350 }
2351
2352 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2353
2354 if (pSvgStyleAttributes && maResolvingParent[10] < nStyleDepthLimit)
2355 {
2356 ++maResolvingParent[10];
2357 auto ret = pSvgStyleAttributes->getFillRule();
2358 --maResolvingParent[10];
2359 return ret;
2360 }
2361
2362 // default is NonZero
2363 return FillRule::nonzero;
2364 }
2365
2367 {
2369 {
2370 return maClipRule;
2371 }
2372
2373 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2374
2375 if (pSvgStyleAttributes && maResolvingParent[25] < nStyleDepthLimit)
2376 {
2377 ++maResolvingParent[25];
2378 auto ret = pSvgStyleAttributes->getClipRule();
2379 --maResolvingParent[25];
2380 return ret;
2381 }
2382
2383 // default is NonZero
2384 return FillRule::nonzero;
2385 }
2386
2388 {
2389 if(!maStrokeDasharray.empty())
2390 {
2391 return maStrokeDasharray;
2392 }
2393 else if(mbStrokeDasharraySet)
2394 {
2395 // #121221# is set to empty *by purpose*, do not visit parent styles
2396 return maStrokeDasharray;
2397 }
2398
2399 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2400
2401 if (pSvgStyleAttributes && maResolvingParent[11] < nStyleDepthLimit)
2402 {
2403 ++maResolvingParent[11];
2404 const SvgNumberVector& ret = pSvgStyleAttributes->getStrokeDasharray();
2405 --maResolvingParent[11];
2406 return ret;
2407 }
2408
2409 // default empty
2410 return maStrokeDasharray;
2411 }
2412
2414 {
2416 {
2417 return maStrokeDashOffset;
2418 }
2419
2420 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2421
2422 if (pSvgStyleAttributes && maResolvingParent[12] < nStyleDepthLimit)
2423 {
2424 ++maResolvingParent[12];
2425 auto ret = pSvgStyleAttributes->getStrokeDashOffset();
2426 --maResolvingParent[12];
2427 return ret;
2428 }
2429
2430 // default is 0
2431 return SvgNumber(0.0);
2432 }
2433
2435 {
2437 {
2438 return maStrokeLinecap;
2439 }
2440
2441 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2442
2443 if (pSvgStyleAttributes && maResolvingParent[13] < nStyleDepthLimit)
2444 {
2445 ++maResolvingParent[13];
2446 auto ret = pSvgStyleAttributes->getStrokeLinecap();
2447 --maResolvingParent[13];
2448 return ret;
2449 }
2450
2451 // default is StrokeLinecap::butt
2452 return StrokeLinecap::butt;
2453 }
2454
2456 {
2458 {
2459 return maStrokeLinejoin;
2460 }
2461
2462 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2463
2464 if (pSvgStyleAttributes && maResolvingParent[14] < nStyleDepthLimit)
2465 {
2466 ++maResolvingParent[14];
2467 auto ret = pSvgStyleAttributes->getStrokeLinejoin();
2468 --maResolvingParent[14];
2469 return ret;
2470 }
2471
2472 // default is StrokeLinejoin::butt
2473 return StrokeLinejoin::miter;
2474 }
2475
2477 {
2479 {
2480 return maStrokeMiterLimit;
2481 }
2482
2483 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2484
2485 if (pSvgStyleAttributes && maResolvingParent[15] < nStyleDepthLimit)
2486 {
2487 ++maResolvingParent[15];
2488 auto ret = pSvgStyleAttributes->getStrokeMiterLimit();
2489 --maResolvingParent[15];
2490 return ret;
2491 }
2492
2493 // default is 4
2494 return SvgNumber(4.0, SvgUnit::none);
2495 }
2496
2498 {
2500 {
2501 return maStrokeOpacity;
2502 }
2503
2504 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2505
2506 if (pSvgStyleAttributes && maResolvingParent[16] < nStyleDepthLimit)
2507 {
2508 ++maResolvingParent[16];
2509 auto ret = pSvgStyleAttributes->getStrokeOpacity();
2510 --maResolvingParent[16];
2511 return ret;
2512 }
2513
2514 // default is 1
2515 return SvgNumber(1.0);
2516 }
2517
2519 {
2520 if(!maFontFamily.empty() && !o3tl::equalsIgnoreAsciiCase(o3tl::trim(maFontFamily[0]), u"inherit"))
2521 {
2522 return maFontFamily;
2523 }
2524
2525 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2526
2527 if (pSvgStyleAttributes && maResolvingParent[17] < nStyleDepthLimit)
2528 {
2529 ++maResolvingParent[17];
2530 const SvgStringVector& ret = pSvgStyleAttributes->getFontFamily();
2531 --maResolvingParent[17];
2532 return ret;
2533 }
2534
2535 // default is empty
2536 return maFontFamily;
2537 }
2538
2540 {
2541 // default size is 'medium', i.e. 12 pt, or 16px
2542 constexpr double aDefaultSize = o3tl::convert(12.0, o3tl::Length::pt, o3tl::Length::px);
2543
2545 {
2547 return aDefaultSize;
2548
2549 // #122524# Handle SvgUnit::percent relative to parent FontSize (see SVG1.1
2550 // spec 10.10 Font selection properties \91font-size\92, lastline (click 'normative
2551 // definition of the property')
2553 {
2554 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2555
2556 if(pSvgStyleAttributes)
2557 {
2558 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber();
2559
2560 return SvgNumber(
2561 aParentNumber.getNumber() * maFontSizeNumber.getNumber() * 0.01,
2562 aParentNumber.getUnit(),
2563 true);
2564 }
2565 // if there's no parent style, set the font size based on the default size
2566 // 100% = 16px
2567 return SvgNumber(
2568 maFontSizeNumber.getNumber() * aDefaultSize / 100.0, SvgUnit::px, true);
2569 }
2571 {
2572 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2573
2574 if(pSvgStyleAttributes)
2575 {
2576 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber();
2577
2578 return SvgNumber(
2579 aParentNumber.getNumber() * maFontSizeNumber.getNumber(),
2580 aParentNumber.getUnit(),
2581 true);
2582 }
2583 }
2584
2585 return maFontSizeNumber;
2586 }
2587
2588 //In CSS2, the suggested scaling factor between adjacent indexes is 1.2
2589 switch(maFontSize)
2590 {
2591 case FontSize::notset:
2592 break;
2593 case FontSize::xx_small:
2594 {
2595 return SvgNumber(aDefaultSize / 1.728);
2596 }
2597 case FontSize::x_small:
2598 {
2599 return SvgNumber(aDefaultSize / 1.44);
2600 }
2601 case FontSize::small:
2602 {
2603 return SvgNumber(aDefaultSize / 1.2);
2604 }
2605 case FontSize::smaller:
2606 {
2607 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2608 if(pSvgStyleAttributes)
2609 {
2610 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber();
2611 return SvgNumber(aParentNumber.getNumber() / 1.2, aParentNumber.getUnit());
2612 }
2613 [[fallthrough]];
2614 }
2615 case FontSize::medium:
2616 case FontSize::initial:
2617 {
2618 return SvgNumber(aDefaultSize);
2619 }
2620 case FontSize::large:
2621 {
2622 return SvgNumber(aDefaultSize * 1.2);
2623 }
2624 case FontSize::larger:
2625 {
2626 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2627 if(pSvgStyleAttributes)
2628 {
2629 const SvgNumber aParentNumber = pSvgStyleAttributes->getFontSizeNumber();
2630 return SvgNumber(aParentNumber.getNumber() * 1.2, aParentNumber.getUnit());
2631 }
2632 [[fallthrough]];
2633 }
2634 case FontSize::x_large:
2635 {
2636 return SvgNumber(aDefaultSize * 1.44);
2637 }
2638 case FontSize::xx_large:
2639 {
2640 return SvgNumber(aDefaultSize * 1.728);
2641 }
2642 }
2643
2644 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2645
2646 if(pSvgStyleAttributes)
2647 {
2648 return pSvgStyleAttributes->getFontSizeNumber();
2649 }
2650
2651 return SvgNumber(aDefaultSize);
2652 }
2653
2655 {
2657 {
2659 {
2660 return maFontStretch;
2661 }
2662 }
2663
2664 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2665
2666 if (pSvgStyleAttributes && maResolvingParent[18] < nStyleDepthLimit)
2667 {
2668 ++maResolvingParent[18];
2669 FontStretch aInherited = pSvgStyleAttributes->getFontStretch();
2670 --maResolvingParent[18];
2671
2673 {
2674 aInherited = getWider(aInherited);
2675 }
2677 {
2678 aInherited = getNarrower(aInherited);
2679 }
2680
2681 return aInherited;
2682 }
2683
2684 // default is FontStretch::normal
2685 return FontStretch::normal;
2686 }
2687
2689 {
2691 {
2692 return maFontStyle;
2693 }
2694
2695 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2696
2697 if (pSvgStyleAttributes && maResolvingParent[19] < nStyleDepthLimit)
2698 {
2699 ++maResolvingParent[19];
2700 auto ret = pSvgStyleAttributes->getFontStyle();
2701 --maResolvingParent[19];
2702 return ret;
2703 }
2704
2705 // default is FontStyle::normal
2706 return FontStyle::normal;
2707 }
2708
2710 {
2712 {
2714 {
2715 return maFontWeight;
2716 }
2717 }
2718
2719 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2720
2721 if (pSvgStyleAttributes && maResolvingParent[20] < nStyleDepthLimit)
2722 {
2723 ++maResolvingParent[20];
2724 FontWeight aInherited = pSvgStyleAttributes->getFontWeight();
2725 --maResolvingParent[20];
2726
2728 {
2729 aInherited = getBolder(aInherited);
2730 }
2732 {
2733 aInherited = getLighter(aInherited);
2734 }
2735
2736 return aInherited;
2737 }
2738
2739 // default is FontWeight::N400 (FontWeight::normal)
2740 return FontWeight::N400;
2741 }
2742
2744 {
2746 {
2747 return maTextAlign;
2748 }
2749
2750 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2751
2752 if (pSvgStyleAttributes && maResolvingParent[21] < nStyleDepthLimit)
2753 {
2754 ++maResolvingParent[21];
2755 auto ret = pSvgStyleAttributes->getTextAlign();
2756 --maResolvingParent[21];
2757 return ret;
2758 }
2759
2760 // default is TextAlign::left
2761 return TextAlign::left;
2762 }
2763
2765 {
2767 {
2768 return this;
2769 }
2770
2771 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2772
2773 if (pSvgStyleAttributes && maResolvingParent[22] < nStyleDepthLimit)
2774 {
2775 ++maResolvingParent[22];
2776 auto ret = pSvgStyleAttributes->getTextDecorationDefiningSvgStyleAttributes();
2777 --maResolvingParent[22];
2778 return ret;
2779 }
2780
2781 // default is 0
2782 return nullptr;
2783 }
2784
2786 {
2788
2789 if(pDefining)
2790 {
2791 return pDefining->maTextDecoration;
2792 }
2793 else
2794 {
2795 // default is TextDecoration::none
2796 return TextDecoration::none;
2797 }
2798 }
2799
2801 {
2803 {
2804 return maTextAnchor;
2805 }
2806
2807 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2808
2809 if (pSvgStyleAttributes && maResolvingParent[23] < nStyleDepthLimit)
2810 {
2811 ++maResolvingParent[23];
2812 auto ret = pSvgStyleAttributes->getTextAnchor();
2813 --maResolvingParent[23];
2814 return ret;
2815 }
2816
2817 // default is TextAnchor::start
2818 return TextAnchor::start;
2819 }
2820
2822 {
2823 if(maColor.isSet())
2824 {
2825 if(maColor.isCurrent())
2826 {
2827 OSL_ENSURE(false, "Svg error: current color uses current color (!)");
2828 return nullptr;
2829 }
2830 else if(maColor.isOn())
2831 {
2832 return &maColor.getBColor();
2833 }
2834 }
2835 else
2836 {
2837 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2838
2839 if (pSvgStyleAttributes && maResolvingParent[24] < nStyleDepthLimit)
2840 {
2841 ++maResolvingParent[24];
2842 auto ret = pSvgStyleAttributes->getColor();
2843 --maResolvingParent[24];
2844 return ret;
2845 }
2846 }
2847
2848 return nullptr;
2849 }
2850
2852 {
2853 if(!maClipPathXLink.isEmpty())
2854 {
2855 return maClipPathXLink;
2856 }
2857
2858 // This is called from add_postProcess so only check the parent style
2859 // if it has a local css style, because it's the first in the stack
2861 {
2862 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2863
2864 if (pSvgStyleAttributes)
2865 {
2866 return pSvgStyleAttributes->maClipPathXLink;
2867 }
2868 }
2869
2870 return OUString();
2871 }
2872
2874 {
2875 const OUString aClipPath(getClipPathXLink());
2876
2877 if(!aClipPath.isEmpty())
2878 {
2879 return dynamic_cast< const SvgClipPathNode* >(mrOwner.getDocument().findSvgNodeById(aClipPath));
2880 }
2881 return nullptr;
2882 }
2883
2885 {
2886 if(!maFilterXLink.isEmpty())
2887 {
2888 return maFilterXLink;
2889 }
2890
2891 // This is called from add_postProcess so only check the parent style
2892 // if it has a local css style, because it's the first in the stack
2894 {
2895 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2896
2897 if (pSvgStyleAttributes)
2898 {
2899 return pSvgStyleAttributes->maFilterXLink;
2900 }
2901 }
2902
2903 return OUString();
2904 }
2905
2907 {
2908 const OUString aFilter(getFilterXLink());
2909
2910 if(!aFilter.isEmpty())
2911 {
2912 return dynamic_cast< const SvgFilterNode* >(mrOwner.getDocument().findSvgNodeById(aFilter));
2913 }
2914 return nullptr;
2915 }
2916
2918 {
2919 if(!maMaskXLink.isEmpty())
2920 {
2921 return maMaskXLink;
2922 }
2923
2924 // This is called from add_postProcess so only check the parent style
2925 // if it has a local css style, because it's the first in the stack
2927 {
2928 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2929
2930 if (pSvgStyleAttributes)
2931 {
2932 return pSvgStyleAttributes->maMaskXLink;
2933 }
2934 }
2935
2936 return OUString();
2937 }
2938
2940 {
2941 const OUString aMask(getMaskXLink());
2942
2943 if(!aMask.isEmpty())
2944 {
2945 return dynamic_cast< const SvgMaskNode* >(mrOwner.getDocument().findSvgNodeById(aMask));
2946 }
2947 return nullptr;
2948 }
2949
2951 {
2952 if(!maMarkerStartXLink.isEmpty())
2953 {
2954 return maMarkerStartXLink;
2955 }
2956
2957 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2958
2959 if (pSvgStyleAttributes && maResolvingParent[26] < nStyleDepthLimit)
2960 {
2961 ++maResolvingParent[26];
2962 auto ret = pSvgStyleAttributes->getMarkerStartXLink();
2963 --maResolvingParent[26];
2964 return ret;
2965 }
2966
2967 return OUString();
2968 }
2969
2971 {
2972 const OUString aMarker(getMarkerStartXLink());
2973
2974 if(!aMarker.isEmpty())
2975 {
2976 return dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerStartXLink()));
2977 }
2978 return nullptr;
2979 }
2980
2982 {
2983 if(!maMarkerMidXLink.isEmpty())
2984 {
2985 return maMarkerMidXLink;
2986 }
2987
2988 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
2989
2990 if (pSvgStyleAttributes && maResolvingParent[27] < nStyleDepthLimit)
2991 {
2992 ++maResolvingParent[27];
2993 auto ret = pSvgStyleAttributes->getMarkerMidXLink();
2994 --maResolvingParent[27];
2995 return ret;
2996 }
2997
2998 return OUString();
2999 }
3000
3002 {
3003 const OUString aMarker(getMarkerMidXLink());
3004
3005 if(!aMarker.isEmpty())
3006 {
3007 return dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerMidXLink()));
3008 }
3009 return nullptr;
3010 }
3011
3013 {
3014 if(!maMarkerEndXLink.isEmpty())
3015 {
3016 return maMarkerEndXLink;
3017 }
3018
3019 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
3020
3021 if (pSvgStyleAttributes && maResolvingParent[28] < nStyleDepthLimit)
3022 {
3023 ++maResolvingParent[28];
3024 auto ret = pSvgStyleAttributes->getMarkerEndXLink();
3025 --maResolvingParent[28];
3026 return ret;
3027 }
3028
3029 return OUString();
3030 }
3031
3033 {
3034 const OUString aMarker(getMarkerEndXLink());
3035
3036 if(!aMarker.isEmpty())
3037 {
3038 return dynamic_cast< const SvgMarkerNode* >(mrOwner.getDocument().findSvgNodeById(getMarkerEndXLink()));
3039 }
3040 return nullptr;
3041 }
3042
3044 {
3045 // #122524# Handle SvgUnit::percent relative to parent BaselineShift
3047 {
3048 const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
3049
3050 if (pSvgStyleAttributes && maResolvingParent[8] < nStyleDepthLimit)
3051 {
3052 ++maResolvingParent[8];
3053 const SvgNumber aParentNumber = pSvgStyleAttributes->getBaselineShiftNumber();
3054 --maResolvingParent[8];
3055
3056 return SvgNumber(
3057 aParentNumber.getNumber() * maBaselineShiftNumber.getNumber() * 0.01,
3058 aParentNumber.getUnit(),
3059 true);
3060 }
3061 }
3062
3063 return maBaselineShiftNumber;
3064 }
3065} // end of namespace svgio
3066
3067/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3068
void rotate(double fRadiant)
void translate(double fX, double fY)
void scale(double fX, double fY)
bool isIdentity() const
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void transform(const basegfx::B2DHomMatrix &rMatrix)
B2DRange getB2DRange() const
bool areControlPointsUsed() const
sal_uInt32 count() const
bool isClosed() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
sal_uInt32 count() const
B2DVector & normalize()
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
bool isEmpty() const
TYPE getHeight() const
bool equalZero() const
bool equal(const Tuple2D< TYPE > &rTup) const
TYPE getX() const
void setY(TYPE fY)
TYPE getY() const
void setX(TYPE fX)
void process(const primitive2d::Primitive2DContainer &rSource)
const basegfx::B2DPolyPolygonVector & getExtractedLineFills() const
const TextAsPolygonDataNodeVector & getTarget() const
std::vector< Value >::const_iterator const_iterator
const_iterator find(const Value &x) const
bool empty() const
const_iterator end() const
basegfx::B2DHomMatrix createMapping(const basegfx::B2DRange &rTarget, const basegfx::B2DRange &rSource) const
Definition: svgtools.cxx:206
static basegfx::B2DHomMatrix createLinearMapping(const basegfx::B2DRange &rTarget, const basegfx::B2DRange &rSource)
tooling
Definition: svgtools.cxx:186
virtual const SvgStyleAttributes * getSvgStyleAttributes() const override
void apply(drawinglayer::primitive2d::Primitive2DContainer &rTarget, const std::optional< basegfx::B2DHomMatrix > &pTransform) const
apply contained clipPath to given geometry #i124852# transform may be needed
const SvgNode * findSvgNodeById(const OUString &rStr) const
find a node by its Id
Definition: svgdocument.cxx:56
virtual void apply(drawinglayer::primitive2d::Primitive2DContainer &rTarget) const
SvgNumber getY2() const
y2 content
SvgNumber getX1() const
x1 content
SvgUnits getGradientUnits() const
gradientUnits content
SvgNumber getCy() const
Cy content.
void collectGradientEntries(drawinglayer::primitive2d::SvgGradientEntryVector &aVector) const
collect gradient stop entries
const SvgNumber * getFy() const
Fy content.
SvgNumber getCx() const
Cx content.
SvgNumber getX2() const
x2 content
drawinglayer::primitive2d::SpreadMethod getSpreadMethod() const
SpreadMethod content.
SvgNumber getY1() const
y1 content
std::optional< basegfx::B2DHomMatrix > getGradientTransform() const
transform content, set if found in current context
const SvgNumber * getFx() const
Fx content.
SvgNumber getR() const
R content.
const SvgNumber & getMarkerHeight() const
MarkerHeight content, set if found in current context.
const SvgNumber & getRefX() const
RefX content, set if found in current context.
const SvgAspectRatio & getSvgAspectRatio() const
SvgAspectRatio content.
const drawinglayer::primitive2d::Primitive2DContainer & getMarkerPrimitives() const
get marker primitives buffered, uses decomposeSvgNode internally
const SvgNumber & getMarkerWidth() const
MarkerWidth content, set if found in current context.
const basegfx::B2DRange * getViewBox() const
viewBox content
const SvgNumber & getRefY() const
RefY content, set if found in current context.
double getAngle() const
Angle content, set if found in current context.
MarkerOrient getMarkerOrient() const
MarkerOrient content.
MarkerUnits getMarkerUnits() const
MarkerUnits content.
void apply(drawinglayer::primitive2d::Primitive2DContainer &rTarget, const std::optional< basegfx::B2DHomMatrix > &pTransform) const
apply contained clipPath to given geometry #i124852# transform may be needed
virtual bool supportsParentStyle() const
#i125258# tell if this node is allowed to have a parent style (e.g. defs do not)
Definition: svgnode.cxx:32
SVGToken getType() const
basic data read access
Definition: svgnode.hxx:156
const SvgNode * getParent() const
Definition: svgnode.hxx:158
std::optional< OUString > const & getClass() const
Class access.
Definition: svgnode.hxx:174
virtual const SvgStyleAttributes * getSvgStyleAttributes() const
Definition: svgnode.cxx:37
bool hasLocalCssStyle()
Check if there is a local css style.
Definition: svgnode.hxx:189
const SvgDocument & getDocument() const
Definition: svgnode.hxx:157
void setDisplay(Display eDisplay)
Definition: svgnode.hxx:183
SvgUnit getUnit() const
Definition: SvgNumber.hxx:90
double solve(const InfoProvider &rInfoProvider, NumberType aNumberType=NumberType::length) const
Definition: SvgNumber.cxx:69
double getNumber() const
Definition: SvgNumber.hxx:85
const basegfx::BColor & getBColor() const
Definition: svgpaint.hxx:44
const SvgUnits * getPatternContentUnits() const
PatternContentUnits content.
const SvgAspectRatio & getSvgAspectRatio() const
SvgAspectRatio content.
std::optional< basegfx::B2DHomMatrix > getPatternTransform() const
PatternTransform content.
const basegfx::B2DRange * getViewBox() const
viewBox content
void getValuesRelative(double &rfX, double &rfY, double &rfW, double &rfH, const basegfx::B2DRange &rGeoRange, SvgNode const &rUser) const
global helpers
const drawinglayer::primitive2d::Primitive2DContainer & getPatternPrimitives() const
get pattern primitives buffered, uses decomposeSvgNode internally
const SvgStyleAttributes * getParentStyle() const
OUString maMarkerStartXLink
link to markers. If set, the node can be fetched on demand
void add_fillPatternTransform(const basegfx::B2DPolyPolygon &rPath, drawinglayer::primitive2d::Primitive2DContainer &rTarget, const SvgPatternNode &rFillGradient, const basegfx::B2DRange &rGeoRange) const
void setFontWeight(const FontWeight aFontWeight)
void setStrokeLinejoin(const StrokeLinejoin aStrokeLinejoin)
void add_fill(const basegfx::B2DPolyPolygon &rPath, drawinglayer::primitive2d::Primitive2DContainer &rTarget, const basegfx::B2DRange &rGeoRange) const
const SvgStyleAttributes * getCssStyleParent() const
SvgNumber getFillOpacity() const
fill opacity content
SvgNumber getOpacity() const
Opacity content.
const basegfx::BColor * getCurrentColor() const
Resolve current color (defaults to black if no color is specified)
const basegfx::BColor * getColor() const
Color content.
const basegfx::BColor * getFill() const
void add_fillGradient(const basegfx::B2DPolyPolygon &rPath, drawinglayer::primitive2d::Primitive2DContainer &rTarget, const SvgGradientNode &rFillGradient, const basegfx::B2DRange &rGeoRange) const
internal helpers
SvgNumber getStrokeOpacity() const
StrokeOpacity content.
FillRule getFillRule() const
fill rule content
const SvgStyleAttributes * getTextDecorationDefiningSvgStyleAttributes() const
TextDecoration content.
void setTextAlign(const TextAlign aTextAlign)
void add_fillPattern(const basegfx::B2DPolyPolygon &rPath, drawinglayer::primitive2d::Primitive2DContainer &rTarget, const SvgPatternNode &rFillGradient, const basegfx::B2DRange &rGeoRange) const
const SvgFilterNode * accessFilterXLink() const
StrokeLinejoin getStrokeLinejoin() const
StrokeLinejoin content.
SvgNumber getStrokeMiterLimit() const
StrokeMiterLimit content.
const basegfx::BColor * getStroke() const
stroke content
void add_postProcess(drawinglayer::primitive2d::Primitive2DContainer &rTarget, drawinglayer::primitive2d::Primitive2DContainer &&rSource, const std::optional< basegfx::B2DHomMatrix > &pTransform) const
const SvgStringVector & getFontFamily() const
Font content.
SvgNumber getStrokeDashOffset() const
StrokeDashOffset content.
const SvgPatternNode * getSvgPatternNodeFill() const
access to evtl. set fill pattern
void add_path(const basegfx::B2DPolyPolygon &rPath, drawinglayer::primitive2d::Primitive2DContainer &rTarget, const basegfx::utils::PointIndexSet *pHelpPointIndices) const
SvgNumber getStopOpacity() const
stop opacity content
void setVisibility(const Visibility aVisibility)
void setTextAnchor(const TextAnchor aTextAnchor)
const SvgMaskNode * accessMaskXLink() const
TextAlign getTextAlign() const
TextAlign content.
void setFontStyle(const FontStyle aFontStyle)
SvgNumber getStrokeWidth() const
stroke-width content
void add_stroke(const basegfx::B2DPolyPolygon &rPath, drawinglayer::primitive2d::Primitive2DContainer &rTarget, const basegfx::B2DRange &rGeoRange) const
const SvgMarkerNode * accessMarkerStartXLink() const
void setFontStretch(const FontStretch aFontStretch)
FontStyle getFontStyle() const
FontStyle content.
const basegfx::BColor & getStopColor() const
stop color content
const SvgMarkerNode * accessMarkerEndXLink() const
void setFontSize(const FontSize aFontSize)
FontSize content.
void parseStyleAttribute(SVGToken aSVGToken, const OUString &rContent)
local attribute scanner
StrokeLinecap getStrokeLinecap() const
StrokeLinecap content.
void add_markers(const basegfx::B2DPolyPolygon &rPath, drawinglayer::primitive2d::Primitive2DContainer &rTarget, const basegfx::utils::PointIndexSet *pHelpPointIndices) const
void setStrokeLinecap(const StrokeLinecap aStrokeLinecap)
const SvgMarkerNode * accessMarkerMidXLink() const
const SvgGradientNode * getSvgGradientNodeFill() const
access to evtl. set fill gradient
FontWeight getFontWeight() const
FontWeight content.
void setOpacity(const SvgNumber &rOpacity)
Visibility getVisibility() const
Visibility.
const SvgPatternNode * getSvgPatternNodeStroke() const
access to evtl. set stroke pattern
FontStretch getFontStretch() const
FontStretch content.
void setBaselineShift(const BaselineShift aBaselineShift)
const SvgClipPathNode * accessClipPathXLink() const
const SvgNumberVector & getStrokeDasharray() const
fill StrokeDasharray content
void setTextDecoration(const TextDecoration aTextDecoration)
OUString maClipPathXLink
link to content. If set, the node can be fetched on demand
TextAnchor getTextAnchor() const
TextAnchor content.
FillRule getClipRule() const
clip rule content
bool prepare_singleMarker(drawinglayer::primitive2d::Primitive2DContainer &rMarkerPrimitives, basegfx::B2DHomMatrix &rMarkerTransform, basegfx::B2DRange &rClipRange, const SvgMarkerNode &rMarker) const
void add_text(drawinglayer::primitive2d::Primitive2DContainer &rTarget, drawinglayer::primitive2d::Primitive2DContainer &&rSource) const
helper which does the necessary with a given path
void readCssStyle(std::u16string_view rCandidate)
scan helpers
std::vector< sal_uInt16 > maResolvingParent
const SvgGradientNode * getSvgGradientNodeStroke() const
access to evtl. set stroke gradient
URL aURL
float u
FilterGroup & rTarget
FontWeight
WEIGHT_ULTRALIGHT
WEIGHT_ULTRABOLD
WEIGHT_BOLD
WEIGHT_NORMAL
WEIGHT_LIGHT
WEIGHT_SEMIBOLD
WEIGHT_SEMILIGHT
WEIGHT_MEDIUM
WEIGHT_BLACK
bool solve(Matrix &matrix, int rows, int cols, Vector &result, BaseType minPivot)
uno_Any a
sal_uInt16 nPos
Sequence< sal_Int8 > aSeq
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool less(const T &rfValA, const T &rfValB)
bool moreOrEqual(const T &rfValA, const T &rfValB)
B2DPolyPolygon createNonzeroConform(const B2DPolyPolygon &rCandidate)
double getLength(const B2DPolygon &rCandidate)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
B2DPolygon createPolygonFromCircle(const B2DPoint &rCenter, double fRadius)
B2DVector getTangentLeavingPoint(const B2DPolygon &rCandidate, sal_uInt32 nIndex)
B2DVector getTangentEnteringPoint(const B2DPolygon &rCandidate, sal_uInt32 nIndex)
B2DPolyPolygon mergeToSinglePolyPolygon(const B2DPolyPolygonVector &rInput)
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector
::std::vector< SvgGradientEntry > SvgGradientEntryVector
::std::vector< TextAsPolygonDataNode > TextAsPolygonDataNodeVector
std::basic_string_view< charT, traits > trim(std::basic_string_view< charT, traits > str)
bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
FontStretch getWider(FontStretch aSource)
void skip_char(std::u16string_view rCandidate, sal_Unicode nChar, sal_Int32 &nPos, const sal_Int32 nLen)
Definition: svgtools.cxx:292
static basegfx::B2DLineJoin StrokeLinejoinToB2DLineJoin(StrokeLinejoin aStrokeLinejoin)
SVGToken StrToSVGToken(const OUString &rStr, bool bCaseIndependent)
Definition: svgtoken.cxx:343
::std::vector< double > solveSvgNumberVector(const SvgNumberVector &rInput, const InfoProvider &rInfoProvider)
Definition: svgtools.cxx:1499
FontWeight getLighter(FontWeight aSource)
::std::vector< OUString > SvgStringVector
Definition: svgtools.hxx:124
::FontWeight getVclFontWeight(FontWeight aSource)
bool readSingleNumber(std::u16string_view rCandidate, SvgNumber &aNum)
Definition: svgtools.cxx:1076
void copyToLimiter(std::u16string_view rCandidate, sal_Unicode nLimiter, sal_Int32 &nPos, OUStringBuffer &rTarget, const sal_Int32 nLen)
Definition: svgtools.cxx:380
std::vector< SvgNumber > SvgNumberVector
Definition: SvgNumber.hxx:111
bool readSvgNumberVector(std::u16string_view rCandidate, SvgNumberVector &rSvgNumberVector)
Definition: svgtools.cxx:1174
bool readSvgPaint(const OUString &rCandidate, SvgPaint &rSvgPaint, OUString &rURL, SvgNumber &rOpacity)
Definition: svgtools.cxx:1139
bool readLocalUrl(const OUString &rCandidate, OUString &rURL)
Definition: svgtools.cxx:1102
FontWeight getBolder(FontWeight aSource)
FontStretch getNarrower(FontStretch aSource)
Display getDisplayFromContent(std::u16string_view aContent)
Definition: svgnode.cxx:452
static css::drawing::LineCap StrokeLinecapToDrawingLineCap(StrokeLinecap aStrokeLinecap)
bool readSvgStringVector(std::u16string_view rCandidate, SvgStringVector &rSvgStringVector)
Definition: svgtools.cxx:1319
void copyString(std::u16string_view rCandidate, sal_Int32 &nPos, OUStringBuffer &rTarget, const sal_Int32 nLen)
Definition: svgtools.cxx:360
bool getType(BSTR name, Type &type)
const basegfx::B2DPolyPolygon & getB2DPolyPolygon() const
static constexpr OUStringLiteral aStrEvenOdd
Definition: svgtools.hxx:41
static constexpr OUStringLiteral aStrNonzero
Definition: svgtools.hxx:40
const int nStyleDepthLimit
const sal_uInt8 R