filter/qa/unit/svg.cxx          |   53 ++++++++++++++++++++++++++++++++++++++++
 filter/source/svg/svgwriter.cxx |    5 +++
 2 files changed, 58 insertions(+)

New commits:
commit d5dcd17c1d29109dd75a61d2ca434b6a5b29a367
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Nov 11 15:58:11 2022 +0100
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Wed Dec 14 13:52:58 2022 +0000

    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 <vmik...@collabora.com>
    Tested-by: Jenkins
    (cherry picked from commit c725028f15c36fc626d9ad8cdc288eb73c3e2643)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142683
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
index 0fc29ecae165..22695acd64f2 100644
--- a/filter/qa/unit/svg.cxx
+++ b/filter/qa/unit/svg.cxx
@@ -21,6 +21,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>
@@ -199,6 +201,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:
+    getComponent()
+        = loadFromDesktop("private:factory/simpress", 
"com.sun.star.drawing.DrawingDocument");
+    uno::Reference<lang::XMultiServiceFactory> xFactory(getComponent(), 
uno::UNO_QUERY);
+    uno::Reference<drawing::XShape> xShape(
+        xFactory->createInstance("com.sun.star.drawing.TextShape"), 
uno::UNO_QUERY);
+    uno::Reference<drawing::XDrawPagesSupplier> 
xDrawPagesSupplier(getComponent(), 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(getComponent(), 
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 88c240702f9b..3a3fc8a5ce8b 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -633,6 +633,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);
                 }

Reply via email to