svgio/inc/svgmarkernode.hxx | 4 svgio/inc/svgstyleattributes.hxx | 15 +- svgio/qa/cppunit/SvgImportTest.cxx | 11 + svgio/qa/cppunit/data/contextStrokeGradient.svg | 24 +++ svgio/source/svgreader/svgmarkernode.cxx | 3 svgio/source/svgreader/svgstyleattributes.cxx | 169 +++++++++++++----------- 6 files changed, 144 insertions(+), 82 deletions(-)
New commits: commit 16684eb4baab740fddc443490b19bf64af2be8e3 Author: Xisco Fauli <xiscofa...@libreoffice.org> AuthorDate: Wed Oct 2 13:07:47 2024 +0200 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Wed Oct 2 14:31:47 2024 +0200 tdf#163212: context-fill/context-stroke can also use gradients Change-Id: Ic6f7f5e0817d8f5fccee64938004aa899bde47dc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174382 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> Tested-by: Jenkins diff --git a/svgio/inc/svgmarkernode.hxx b/svgio/inc/svgmarkernode.hxx index b8fa7c000e68..489387dc07e7 100644 --- a/svgio/inc/svgmarkernode.hxx +++ b/svgio/inc/svgmarkernode.hxx @@ -60,6 +60,8 @@ namespace svgio::svgreader double mfAngle; MarkerOrient maMarkerOrient; + const SvgStyleAttributes* maContextStyleAttibutes; + public: SvgMarkerNode( SvgDocument& rDocument, @@ -106,6 +108,8 @@ namespace svgio::svgreader MarkerOrient getMarkerOrient() const { return maMarkerOrient; } void setMarkerOrient(const MarkerOrient aMarkerOrient) { maMarkerOrient = aMarkerOrient; } + const SvgStyleAttributes* getContextStyleAttributes() const { return maContextStyleAttibutes; } + void setContextStyleAttributes(const SvgStyleAttributes* aContextStyleAttibutes) { maContextStyleAttibutes = aContextStyleAttibutes; } }; } // end of namespace svgio::svgreader diff --git a/svgio/inc/svgstyleattributes.hxx b/svgio/inc/svgstyleattributes.hxx index 6c2b550381a0..9a9eccb55105 100644 --- a/svgio/inc/svgstyleattributes.hxx +++ b/svgio/inc/svgstyleattributes.hxx @@ -248,18 +248,21 @@ namespace svgio::svgreader bool mbStrokeDasharraySet : 1; // tdf#155651 Defines if 'context-fill' is used in fill - bool mbContextFill : 1; + bool mbUseFillFromContextFill : 1; + + // tdf#155651 Defines if 'context-stroke' is used in fill + bool mbUseFillFromContextStroke : 1; + + // tdf#155651 Defines if 'context-fill' is used in stroke + bool mbUseStrokeFromContextFill : 1; // tdf#155651 Defines if 'context-stroke' is used in stroke - bool mbContextStroke : 1; + bool mbUseStrokeFromContextStroke : 1; // tdf#94765 Check id references in gradient/pattern getters OUString maNodeFillURL; OUString maNodeStrokeURL; - const basegfx::BColor* maContextFill; - const basegfx::BColor* maContextStroke; - /// internal helpers void add_fillGradient( const basegfx::B2DPolyPolygon& rPath, @@ -320,6 +323,8 @@ namespace svgio::svgreader void readCssStyle(std::u16string_view rCandidate); const SvgStyleAttributes* getCssStyleOrParentStyle() const; + const SvgMarkerNode* getMarkerParentNode() const; + SvgStyleAttributes(SvgNode& rOwner); ~SvgStyleAttributes(); diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index 8328f8640ed3..80dfddd6416a 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -690,6 +690,17 @@ CPPUNIT_TEST_FIXTURE(Test, testContextStroke) assertXPath(pDocument, "/primitive2D/transform/transform[4]/polypolygonstroke/line"_ostr, "color"_ostr, u"#ff0000"_ustr); } +CPPUNIT_TEST_FIXTURE(Test, testContextStrokeGradient) +{ + xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/contextStrokeGradient.svg"); + + assertXPath(pDocument, "/primitive2D/transform/svglineargradient"_ostr); + assertXPath(pDocument, "/primitive2D/transform/transform[1]/svglineargradient"_ostr); + assertXPath(pDocument, "/primitive2D/transform/transform[2]/svglineargradient"_ostr); + assertXPath(pDocument, "/primitive2D/transform/transform[3]/svglineargradient"_ostr); + assertXPath(pDocument, "/primitive2D/transform/transform[4]/svglineargradient"_ostr); +} + CPPUNIT_TEST_FIXTURE(Test, testMarkerInPresentation) { xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/markerInPresentation.svg"); diff --git a/svgio/qa/cppunit/data/contextStrokeGradient.svg b/svgio/qa/cppunit/data/contextStrokeGradient.svg new file mode 100644 index 000000000000..7de3f70b6a0e --- /dev/null +++ b/svgio/qa/cppunit/data/contextStrokeGradient.svg @@ -0,0 +1,24 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> + <style> + path { + fill: none; + stroke-width: 4px; + marker: url(#diamond); + } + </style> + <defs> + <linearGradient + id="swatch7"> + <stop + style="stop-color:#23a2b0;stop-opacity:1;" + offset="0" + id="stop7" /> + </linearGradient> + </defs> + + <path d="M 10,50 v -20 h 40 v -20" stroke="url(#swatch7)"/> + + <marker id="diamond" markerWidth="12" markerHeight="12" refX="6" refY="6" markerUnits="userSpaceOnUse"> + <circle cx="6" cy="6" r="3" fill="white" stroke="context-stroke" stroke-width="2"/> + </marker> +</svg> diff --git a/svgio/source/svgreader/svgmarkernode.cxx b/svgio/source/svgreader/svgmarkernode.cxx index 2279920634a6..c2e1f2716ab9 100644 --- a/svgio/source/svgreader/svgmarkernode.cxx +++ b/svgio/source/svgreader/svgmarkernode.cxx @@ -33,7 +33,8 @@ namespace svgio::svgreader maMarkerWidth(3), maMarkerHeight(3), mfAngle(0.0), - maMarkerOrient(MarkerOrient::notset) + maMarkerOrient(MarkerOrient::notset), + maContextStyleAttibutes(nullptr) { } diff --git a/svgio/source/svgreader/svgstyleattributes.cxx b/svgio/source/svgreader/svgstyleattributes.cxx index 87b162ad906d..49e35be73cba 100644 --- a/svgio/source/svgreader/svgstyleattributes.cxx +++ b/svgio/source/svgreader/svgstyleattributes.cxx @@ -260,6 +260,23 @@ namespace svgio::svgreader return nullptr; } + const SvgMarkerNode* SvgStyleAttributes::getMarkerParentNode() const + { + if (SVGToken::Marker == mrOwner.getType()) + return static_cast<const SvgMarkerNode *>(&mrOwner); + + const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle(); + if (pSvgStyleAttributes && maResolvingParent[32] < nStyleDepthLimit) + { + ++maResolvingParent[32]; + const SvgMarkerNode* ret = pSvgStyleAttributes->getMarkerParentNode(); + --maResolvingParent[32]; + return ret; + } + + return nullptr; + } + void SvgStyleAttributes::add_text( drawinglayer::primitive2d::Primitive2DContainer& rTarget, drawinglayer::primitive2d::Primitive2DContainer&& rSource) const @@ -601,9 +618,36 @@ namespace svgio::svgreader drawinglayer::primitive2d::Primitive2DContainer& rTarget, const basegfx::B2DRange& rGeoRange) const { - const basegfx::BColor* pFill = getFill(); - const SvgGradientNode* pFillGradient = getSvgGradientNodeFill(); - const SvgPatternNode* pFillPattern = getSvgPatternNodeFill(); + const basegfx::BColor* pFill = nullptr; + const SvgGradientNode* pFillGradient = nullptr; + const SvgPatternNode* pFillPattern = nullptr; + + if (mbUseFillFromContextFill) + { + if (const SvgMarkerNode* pMarker = getMarkerParentNode()) + { + const SvgStyleAttributes* pStyle = pMarker->getContextStyleAttributes(); + pFill = pStyle->getFill(); + pFillGradient = pStyle->getSvgGradientNodeFill(); + pFillPattern = pStyle->getSvgPatternNodeFill(); + } + } + else if (mbUseFillFromContextStroke) + { + if (const SvgMarkerNode* pMarker = getMarkerParentNode()) + { + const SvgStyleAttributes* pStyle = pMarker->getContextStyleAttributes(); + pFill = pStyle->getStroke(); + pFillGradient = pStyle->getSvgGradientNodeStroke(); + pFillPattern = pStyle->getSvgPatternNodeStroke(); + } + } + else + { + pFill = getFill(); + pFillGradient = getSvgGradientNodeFill(); + pFillPattern = getSvgPatternNodeFill(); + } if(!(pFill || pFillGradient || pFillPattern)) return; @@ -670,9 +714,36 @@ namespace svgio::svgreader drawinglayer::primitive2d::Primitive2DContainer& rTarget, const basegfx::B2DRange& rGeoRange) const { - const basegfx::BColor* pStroke = getStroke(); - const SvgGradientNode* pStrokeGradient = getSvgGradientNodeStroke(); - const SvgPatternNode* pStrokePattern = getSvgPatternNodeStroke(); + const basegfx::BColor* pStroke = nullptr; + const SvgGradientNode* pStrokeGradient = nullptr; + const SvgPatternNode* pStrokePattern = nullptr; + + if(mbUseStrokeFromContextFill) + { + if (const SvgMarkerNode* pMarker = getMarkerParentNode()) + { + const SvgStyleAttributes* pStyle = pMarker->getContextStyleAttributes(); + pStroke = pStyle->getFill(); + pStrokeGradient = pStyle->getSvgGradientNodeFill(); + pStrokePattern = pStyle->getSvgPatternNodeFill(); + } + } + else if(mbUseStrokeFromContextStroke) + { + if (const SvgMarkerNode* pMarker = getMarkerParentNode()) + { + const SvgStyleAttributes* pStyle = pMarker->getContextStyleAttributes(); + pStroke = pStyle->getStroke(); + pStrokeGradient = pStyle->getSvgGradientNodeStroke(); + pStrokePattern = pStyle->getSvgPatternNodeStroke(); + } + } + else + { + pStroke = getStroke(); + pStrokeGradient = getSvgGradientNodeStroke(); + pStrokePattern = getSvgPatternNodeStroke(); + } if(!(pStroke || pStrokeGradient || pStrokePattern)) return; @@ -837,15 +908,11 @@ namespace svgio::svgreader rMarkerTransform.identity(); rClipRange.reset(); - // Set the current fill to the marker before calling getMarkerPrimitives, - // which calls decomposeSvgNode to decompose the children of the marker. - // If any of the children uses 'fill="context-fill"', then it will use it - const_cast<SvgStyleAttributes*>(rMarker.getSvgStyleAttributes())->maContextFill = getFill(); - - // Set the current stroke to the marker before calling getMarkerPrimitives, + // Set the current style attibutes to the marker before calling getMarkerPrimitives, // which calls decomposeSvgNode to decompose the children of the marker. - // If any of the children uses 'stroke="context-stroke"', then it will use it - const_cast<SvgStyleAttributes*>(rMarker.getSvgStyleAttributes())->maContextStroke = getStroke(); + // If any children uses 'context-fill' or 'context-stroke', + // then these style attributes will be used in add_fill or add_stroke + const_cast<SvgMarkerNode&>(rMarker).setContextStyleAttributes(this); // get marker primitive representation rMarkerPrimitives = rMarker.getMarkerPrimitives(); @@ -1334,12 +1401,12 @@ namespace svgio::svgreader maBaselineShift(BaselineShift::Baseline), maBaselineShiftNumber(0), maDominantBaseline(DominantBaseline::Auto), - maResolvingParent(35, 0), + maResolvingParent(34, 0), mbStrokeDasharraySet(false), - mbContextFill(false), - mbContextStroke(false), - maContextFill(nullptr), - maContextStroke(nullptr) + mbUseFillFromContextFill(false), + mbUseFillFromContextStroke(false), + mbUseStrokeFromContextFill(false), + mbUseStrokeFromContextStroke(false) { } @@ -1361,11 +1428,11 @@ namespace svgio::svgreader if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-fill")) { - mbContextFill = true; + mbUseFillFromContextFill = true; } else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-stroke")) { - mbContextStroke = true; + mbUseFillFromContextStroke = true; } else if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) { @@ -1414,11 +1481,11 @@ namespace svgio::svgreader if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-stroke")) { - mbContextStroke = true; + mbUseStrokeFromContextStroke = true; } else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"context-fill")) { - mbContextFill = true; + mbUseStrokeFromContextFill = true; } else if(readSvgPaint(aContent, aSvgPaint, aURL, aOpacity)) { @@ -2063,40 +2130,6 @@ namespace svgio::svgreader } } - const basegfx::BColor* SvgStyleAttributes::getContextFill() const - { - if (SVGToken::Marker == mrOwner.getType()) - return maContextFill; - - const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle(); - if (pSvgStyleAttributes && maResolvingParent[33] < nStyleDepthLimit) - { - ++maResolvingParent[33]; - auto ret = pSvgStyleAttributes->getContextFill(); - --maResolvingParent[33]; - return ret; - } - - return nullptr; - } - - const basegfx::BColor* SvgStyleAttributes::getContextStroke() const - { - if (SVGToken::Marker == mrOwner.getType()) - return maContextStroke; - - const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle(); - if (pSvgStyleAttributes && maResolvingParent[32] < nStyleDepthLimit) - { - ++maResolvingParent[32]; - auto ret = pSvgStyleAttributes->getContextStroke(); - --maResolvingParent[32]; - return ret; - } - - return nullptr; - } - bool SvgStyleAttributes::isClipPathContent() const { if (SVGToken::ClipPathNode == mrOwner.getType()) @@ -2165,14 +2198,6 @@ namespace svgio::svgreader } } } - else if (mbContextFill) - { - return getContextFill(); - } - else if (mbContextStroke) - { - return getContextStroke(); - } else if (maNodeFillURL.isEmpty()) { const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle(); @@ -2218,14 +2243,6 @@ namespace svgio::svgreader return &maStroke.getBColor(); } } - else if (mbContextFill) - { - return getContextFill(); - } - else if (mbContextStroke) - { - return getContextStroke(); - } else if (maNodeStrokeURL.isEmpty()) { const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle(); @@ -2444,11 +2461,11 @@ namespace svgio::svgreader const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle(); - if (pSvgStyleAttributes && maResolvingParent[34] < nStyleDepthLimit) + if (pSvgStyleAttributes && maResolvingParent[33] < nStyleDepthLimit) { - ++maResolvingParent[34]; + ++maResolvingParent[33]; auto ret = pSvgStyleAttributes->getOpacity(); - --maResolvingParent[34]; + --maResolvingParent[33]; return ret; }