svgio: Add support for clip-rule="evenodd"

Change-Id: I028aa88bdd72b4f87526a3d1edabd612d7686571
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137577
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Xisco Fauli 2022-07-28 18:37:32 +02:00
parent d07f18e0c3
commit 4f61276dae
4 changed files with 78 additions and 11 deletions

View file

@ -338,6 +338,9 @@ namespace svgio::svgreader
/// fill rule content
FillRule getFillRule() const;
/// clip rule content
FillRule getClipRule() const;
/// fill StrokeDasharray content
const SvgNumberVector& getStrokeDasharray() const;

View file

@ -66,6 +66,7 @@ class Test : public test::BootstrapFixture, public XmlTestTools
void testShapeWithClipPath();
void testClipPathUsingClipPath();
void testFillRule();
void testClipRule();
void testi125329();
void testMaskingPath07b();
void test123926();
@ -112,6 +113,7 @@ public:
CPPUNIT_TEST(testShapeWithClipPath);
CPPUNIT_TEST(testClipPathUsingClipPath);
CPPUNIT_TEST(testFillRule);
CPPUNIT_TEST(testClipRule);
CPPUNIT_TEST(testi125329);
CPPUNIT_TEST(testMaskingPath07b);
CPPUNIT_TEST(test123926);
@ -719,6 +721,28 @@ void Test::testFillRule()
assertXPath(pDocument, "/primitive2D/transform/polypolygonstroke/polypolygon/polygon", 2);
}
void Test::testClipRule()
{
Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/ClipRule.svg");
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength()));
drawinglayer::Primitive2dXmlDump dumper;
xmlDocUniquePtr pDocument = dumper.dumpAndParse(Primitive2DContainer(aSequence));
CPPUNIT_ASSERT (pDocument);
// Without the place in place, this test would have failed with
// - Expected: 5
// - Actual : 10
assertXPath(pDocument, "/primitive2D/transform/mask[1]/polypolygon/polygon/point", 5);
assertXPath(pDocument, "/primitive2D/transform/mask[1]/polypolygoncolor", "color", "#0000ff");
assertXPath(pDocument, "/primitive2D/transform/mask[1]/polypolygoncolor/polypolygon/polygon/point", 5);
assertXPath(pDocument, "/primitive2D/transform/mask[2]/polypolygon/polygon/point", 5);
assertXPath(pDocument, "/primitive2D/transform/mask[2]/polypolygoncolor", "color", "#ff0000");
assertXPath(pDocument, "/primitive2D/transform/mask[2]/polypolygoncolor/polypolygon/polygon/point", 5);
}
void Test::testi125329()
{
//Check style inherit from * css element

View file

@ -0,0 +1,18 @@
<svg version="1.1" baseProfile="basic" id="svg-root"
width="100%" height="100%" viewBox="0 0 480 360"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Define star path -->
<defs>
<path d="M50,0 21,90 98,35 2,35 79,90z" id="star" />
</defs>
<clipPath id="emptyStar">
<use xlink:href="#star" clip-rule="evenodd" />
</clipPath>
<rect clip-path="url(#emptyStar)" width="50" height="90" fill="blue" />
<clipPath id="filledStar">
<use xlink:href="#star" clip-rule="evenodd" />
</clipPath>
<rect clip-path="url(#filledStar)" width="50" height="90" x="50" fill="red" />
</svg>

After

Width:  |  Height:  |  Size: 640 B

View file

@ -1125,17 +1125,18 @@ namespace svgio::svgreader
{
// create fill
basegfx::B2DPolyPolygon aPath(rPath);
const bool bNeedToCheckClipRule(SVGToken::Path == mrOwner.getType() || SVGToken::Polygon == mrOwner.getType());
const bool bClipPathIsNonzero(bNeedToCheckClipRule && mbIsClipPathContent && FillRule::nonzero == maClipRule);
const bool bFillRuleIsNonzero(bNeedToCheckClipRule && !mbIsClipPathContent && FillRule::nonzero == getFillRule());
if(bClipPathIsNonzero || bFillRuleIsNonzero)
if(SVGToken::Path == mrOwner.getType() || SVGToken::Polygon == mrOwner.getType())
{
if(getFill() || getSvgGradientNodeFill() || getSvgPatternNodeFill()) {
// nonzero is wanted, solve geometrically (see description on basegfx)
// basegfx::utils::createNonzeroConform() is expensive for huge paths
// and is only needed if path will be filled later on
aPath = basegfx::utils::createNonzeroConform(aPath);
if(FillRule::evenodd != getClipRule() && FillRule::evenodd != getFillRule())
{
if(getFill() || getSvgGradientNodeFill() || getSvgPatternNodeFill())
{
// nonzero is wanted, solve geometrically (see description on basegfx)
// basegfx::utils::createNonzeroConform() is expensive for huge paths
// and is only needed if path will be filled later on
aPath = basegfx::utils::createNonzeroConform(aPath);
}
}
}
@ -1275,10 +1276,10 @@ namespace svgio::svgreader
mpMarkerMidXLink(nullptr),
mpMarkerEndXLink(nullptr),
maFillRule(FillRule::notset),
maClipRule(FillRule::nonzero),
maClipRule(FillRule::notset),
maBaselineShift(BaselineShift::Baseline),
maBaselineShiftNumber(0),
maResolvingParent(31, 0),
maResolvingParent(32, 0),
mbIsClipPathContent(SVGToken::ClipPathNode == mrOwner.getType()),
mbStrokeDasharraySet(false)
{
@ -2348,6 +2349,27 @@ namespace svgio::svgreader
return FillRule::nonzero;
}
FillRule SvgStyleAttributes::getClipRule() const
{
if(FillRule::notset != maClipRule)
{
return maClipRule;
}
const SvgStyleAttributes* pSvgStyleAttributes = getParentStyle();
if (pSvgStyleAttributes && maResolvingParent[31] < nStyleDepthLimit)
{
++maResolvingParent[31];
auto ret = pSvgStyleAttributes->getClipRule();
--maResolvingParent[31];
return ret;
}
// default is NonZero
return FillRule::nonzero;
}
const SvgNumberVector& SvgStyleAttributes::getStrokeDasharray() const
{
if(!maStrokeDasharray.empty())