filter/qa/unit/data/semi-transparent-text-bullet.odg |binary filter/qa/unit/svg.cxx | 29 +++++++++++++++++++ filter/source/svg/svgwriter.cxx | 16 ++++++++++ filter/source/svg/svgwriter.hxx | 1 4 files changed, 46 insertions(+)
New commits: commit 0a89d65e6bb7be293c1a7b4615a08292701694dc Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Wed Sep 4 11:05:56 2024 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Sep 4 16:07:34 2024 +0200 tdf#162782 SVG export: fix handling of semi-transparent text inside a list Open the bugdoc, try to export as SVG, results in an assertion failure in debug builds, the produced XML would not be well-formed. Commit 666f252457bdb4371d15380a0289e107b2dfbe84 (SVG export: fix lost semi-transparent text on shapes, 2020-07-17) added support for text opacity on shapes, but this assumes that the entire shape has the same opacity, while this shape has 3 paragraphs and only the middle one has an opacity set, at a text span level. Additionally, it's a bullet, so the text (for the bullet, has no transparency set) starts before the transparency metafile action would start. This means that the existing logic won't realize that opacity should be exported using the fill-opacity attribute instead of a <g> element. Fix the problem by checking for the isTextShapeStarted() case in SVGActionWriter::ImplWriteMask(): if we're already inside text, then we always want to map a transparency mask to the fill-opacity attribute instead of a <g> element. Leave the shape-level code at SVGTextWriter::setTextPosition() unchanged, that continues to deal with per-shape text opacity. Change-Id: I8cb0ca2e839fba911a75e1925cf79145f69af151 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172856 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/filter/qa/unit/data/semi-transparent-text-bullet.odg b/filter/qa/unit/data/semi-transparent-text-bullet.odg new file mode 100644 index 000000000000..df54dbfb6857 Binary files /dev/null and b/filter/qa/unit/data/semi-transparent-text-bullet.odg differ diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx index 3f1fde79374e..a7b055c8dbe8 100644 --- a/filter/qa/unit/svg.cxx +++ b/filter/qa/unit/svg.cxx @@ -312,6 +312,35 @@ CPPUNIT_TEST_FIXTURE(SvgFilterTest, textInImage) // - Actual : 0 } +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentTextBullet) +{ + // Given a document with 3 paragraphs, paragraph 2 is a bullet with 80% opacity. + loadFromFile(u"semi-transparent-text-bullet.odg"); + + // When exporting to SVG: + save(u"draw_svg_Export"_ustr); + + // Then make sure the result is correct: + xmlDocUniquePtr pXmlDoc = parseExportedFile(); + // We have 3 paragraphs. + assertXPath(pXmlDoc, "//svg:g[@class='TextShape']//svg:text/svg:tspan"_ostr, 3); + // Paragraph 1 has no spans with text opacity set. + assertXPath( + pXmlDoc, + "(//svg:g[@class='TextShape']//svg:text/svg:tspan)[1]/svg:tspan/svg:tspan[@fill-opacity='0.8']"_ostr, + 0); + // Paragraph 2 has a span with text opacity set to 80%. + assertXPath( + pXmlDoc, + "(//svg:g[@class='TextShape']//svg:text/svg:tspan)[2]/svg:tspan/svg:tspan[@fill-opacity='0.8']"_ostr, + 1); + // Paragraph 3 has no spans with text opacity set. + assertXPath( + pXmlDoc, + "(//svg:g[@class='TextShape']//svg:text/svg:tspan)[3]/svg:tspan/svg:tspan[@fill-opacity='0.8']"_ostr, + 0); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index ac179f78372f..92825412250a 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -1391,6 +1391,8 @@ void SVGTextWriter::endTextPosition() bool SVGTextWriter::hasTextOpacity() const { return !maTextOpacity.isEmpty(); } +OUString& SVGTextWriter::getTextOpacity() { return maTextOpacity; } + void SVGTextWriter::implExportHyperlinkIds() { if( !msHyperlinkIdList.isEmpty() ) @@ -2546,6 +2548,15 @@ void SVGActionWriter::ImplWriteMask(GDIMetaFile& rMtf, const Point& rDestPt, con if (nMoveX || nMoveY) rMtf.Move(nMoveX, nMoveY); + std::optional<OUString> oTextOpacity; + if (maTextWriter.isTextShapeStarted()) + { + // We're inside <text>, then try to use the fill-opacity attribute instead of a <g> element + // to express transparency to ensure well-formed output. + oTextOpacity = maTextWriter.getTextOpacity(); + StartMask(rDestPt, rDestSize, rGradient, nWriteFlags, pColorStops, &maTextWriter.getTextOpacity()); + } + { std::unique_ptr<SvXMLElementExport> pElemG; if (!maTextWriter.hasTextOpacity()) @@ -2559,6 +2570,11 @@ void SVGActionWriter::ImplWriteMask(GDIMetaFile& rMtf, const Point& rDestPt, con ImplWriteActions( rMtf, nWriteFlags, u""_ustr ); mpVDev->Pop(); } + + if (oTextOpacity) + { + maTextWriter.getTextOpacity() = *oTextOpacity; + } } diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx index 5a9266b0c5fd..d87e2e67d4df 100644 --- a/filter/source/svg/svgwriter.hxx +++ b/filter/source/svg/svgwriter.hxx @@ -258,6 +258,7 @@ class SVGTextWriter final void startTextPosition( bool bExportX = true, bool bExportY = true); void endTextPosition(); bool hasTextOpacity() const; + OUString& getTextOpacity(); void implExportHyperlinkIds(); void implWriteBulletChars(); template< typename MetaBitmapActionType >