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 <xiscofauli@libreoffice.org>
Tested-by: Jenkins
This commit is contained in:
Xisco Fauli 2024-10-02 13:07:47 +02:00
parent 4dfffddceb
commit 16684eb4ba
6 changed files with 144 additions and 82 deletions

View file

@ -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

View file

@ -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();

View file

@ -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");

View file

@ -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>

After

Width:  |  Height:  |  Size: 639 B

View file

@ -33,7 +33,8 @@ namespace svgio::svgreader
maMarkerWidth(3),
maMarkerHeight(3),
mfAngle(0.0),
maMarkerOrient(MarkerOrient::notset)
maMarkerOrient(MarkerOrient::notset),
maContextStyleAttibutes(nullptr)
{
}

View file

@ -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,
// 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 '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,
// 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;
}