diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2022-11-11 15:58:11 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-11-11 19:35:55 +0100 |
commit | c725028f15c36fc626d9ad8cdc288eb73c3e2643 (patch) | |
tree | bf5a3698a417e45f4b812183a05f032b18417677 /filter | |
parent | c819aa06069ec3ea4f2f51d26a77f455740b828f (diff) |
SVG export: fix handling of semi-transparent, multi-para shape text
The bugdoc had a shape with two paragraphs. The export of the second paragraph
went wrong: the position & size of the text was larger than expected.
This problem was specific to semi-transparent text.
SVGTextWriter::setTextPosition() is recursive in this case. The non-transparent
case left nCurAction unchanged in the outer setTextPosition() and it returned 1
("text is found"). The semi-transparent case changed nCurAction by +2 and
returned 0 ("text is not found"). This led to all sorts of trouble, including
mismatching Push() and Pop() calls on the output device when replaying the
metafile.
Fix the problem by routing the empty state from the inner setTextPosition() to
the outer one: if we return 1 ("text is found"), then we know that the state is
non-empty. Once the empty state is correct in the outer setTextPosition(), then
even the transparent case also leaves nCurAction unchanged and the whole shape
text has the correct position and size.
I forgot to update this empty state in ther outer setTextPosition() in commit
666f252457bdb4371d15380a0289e107b2dfbe84 (SVG export: fix lost semi-transparent
text on shapes, 2020-07-17).
Change-Id: I7dc93fe13c4510220bf09a388e742799ed042510
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142590
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
Diffstat (limited to 'filter')
-rw-r--r-- | filter/qa/unit/svg.cxx | 53 | ||||
-rw-r--r-- | filter/source/svg/svgwriter.cxx | 5 |
2 files changed, 58 insertions, 0 deletions
diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx index 90de658cf50f..ea31a74447f0 100644 --- a/filter/qa/unit/svg.cxx +++ b/filter/qa/unit/svg.cxx @@ -19,6 +19,8 @@ #include <com/sun/star/drawing/XShape.hpp> #include <com/sun/star/drawing/XDrawPagesSupplier.hpp> #include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/text/ControlCharacter.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> #include <unotools/streamwrap.hxx> #include <unotools/mediadescriptor.hxx> @@ -141,6 +143,57 @@ CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentText) assertXPath(pXmlDoc, "//svg:text[2]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity]", 0); } +CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentMultiParaText) +{ + // Given a shape with semi-transparent, multi-paragraph text: + mxComponent + = loadFromDesktop("private:factory/simpress", "com.sun.star.drawing.DrawingDocument"); + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance("com.sun.star.drawing.TextShape"), uno::UNO_QUERY); + uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XShapes> xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0), + uno::UNO_QUERY); + xDrawPage->add(xShape); + xShape->setSize(awt::Size(10000, 10000)); + uno::Reference<text::XSimpleText> xShapeText(xShape, uno::UNO_QUERY); + uno::Reference<text::XTextCursor> xCursor = xShapeText->createTextCursor(); + xShapeText->insertString(xCursor, "foo", /*bAbsorb=*/false); + xShapeText->insertControlCharacter(xCursor, text::ControlCharacter::APPEND_PARAGRAPH, + /*bAbsorb=*/false); + xShapeText->insertString(xCursor, "bar", /*bAbsorb=*/false); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + xShapeProps->setPropertyValue("CharColor", uno::Any(static_cast<sal_Int32>(0xff0000))); + xShapeProps->setPropertyValue("CharTransparence", uno::Any(static_cast<sal_Int16>(20))); + + // When exporting to SVG: + uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY_THROW); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aStream); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export"); + aMediaDescriptor["OutputStream"] <<= xOut; + xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList()); + aStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Then make sure that the two semi-tranparent paragraphs have the same X position: + xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream); + assertXPath(pXmlDoc, "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[1]", "x", + "250"); + assertXPath(pXmlDoc, + "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[1]/svg:tspan", + "fill-opacity", "0.8"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 250 + // - Actual : 8819 + // i.e. the X position of the second paragraph was wrong. + assertXPath(pXmlDoc, "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[2]", "x", + "250"); + assertXPath(pXmlDoc, + "(//svg:g[@class='TextShape']//svg:tspan[@class='TextPosition'])[2]/svg:tspan", + "fill-opacity", "0.8"); +} + CPPUNIT_TEST_FIXTURE(SvgFilterTest, testShapeNographic) { // Load a document containing a 3D shape. diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index 14e355f3916e..ec976ccb6f12 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -634,6 +634,11 @@ sal_Int32 SVGTextWriter::setTextPosition(const GDIMetaFile& rMtf, size_t& nCurAc { // Text is found in the inner metafile. bConfigured = true; + + // nTextFound == 1 is only possible if the inner setTextPosition() had bEmpty == + // false, adjust our bEmpty accordingly. + bEmpty = false; + mrActionWriter.StartMask(pA->GetPoint(), pA->GetSize(), pA->GetGradient(), nWriteFlags, &maTextOpacity); } |