[Libreoffice-commits] core.git: filter/qa filter/source

Miklos Vajna (via logerrit) logerrit at kemper.freedesktop.org
Fri Jul 17 08:07:51 UTC 2020


 filter/qa/unit/svg.cxx          |   12 +++-
 filter/source/svg/svgwriter.cxx |  113 ++++++++++++++++++++++++++++------------
 filter/source/svg/svgwriter.hxx |   11 +++
 3 files changed, 98 insertions(+), 38 deletions(-)

New commits:
commit 666f252457bdb4371d15380a0289e107b2dfbe84
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri Jul 17 09:23:16 2020 +0200
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Fri Jul 17 10:07:11 2020 +0200

    SVG export: fix lost semi-transparent text on shapes
    
    Extend SVGTextWriter::setTextPosition(), so when it looks for a text
    action in a metafile, it recurses into transparency groups, so the text
    is not lost.
    
    Extract part of SVGActionWriter::ImplWriteMask() into a new StartMask(),
    so we can detect the case when the transparency group has a constant
    alpha, i.e. no complex mask is needed, just an opacity value.
    
    When looking for text, remember if we saw a request for text opacity and
    make the transparency group writing in SVGActionWriter::ImplWriteMask()
    conditional to avoid duplication. This is needed because once we're
    inside <text>, we don't want to write an invalid transparency group via
    <g>, rather we want a fill-opacity on the existing <tspan>.
    
    With this, the SVG export is on par with PDF export for semi-transparent
    shape text.
    
    Change-Id: If43b0ab3446015299acc4b37590358867c5fac5f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/98937
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
    Tested-by: Jenkins

diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
index 4589b417a0c2..5dcb1af0eb90 100644
--- a/filter/qa/unit/svg.cxx
+++ b/filter/qa/unit/svg.cxx
@@ -146,12 +146,18 @@ CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentText)
 
     // We expect 2 groups of class "com.sun.star.drawing.TextShape" that
     // have some svg:text node inside.
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 2
+    // - Actual  : 1
+    // i.e. the 2nd shape lots its text.
 
-    // TODO: fix the bug
+    assertXPath(pXmlDoc, "//svg:g[@class='com.sun.star.drawing.TextShape']//svg:text", 2);
 
-    // assertXPath(pXmlDoc, "//svg:g[@class='com.sun.star.drawing.TextShape']//svg:text", 2);
+    // First shape has semi-transparent text.
+    assertXPath(pXmlDoc, "//svg:text[1]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity='0.8']");
 
-    // TODO: assert we the text has correctly transparent text (20%)
+    // Second shape has normal text.
+    assertXPath(pXmlDoc, "//svg:text[2]/svg:tspan/svg:tspan/svg:tspan[@fill-opacity]", 0);
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index ce446a48d92c..86854235d938 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -454,9 +454,11 @@ void SVGAttributeWriter::setFontFamily()
     }
 }
 
-SVGTextWriter::SVGTextWriter( SVGExport& rExport,  SVGAttributeWriter& rAttributeWriter  )
+SVGTextWriter::SVGTextWriter(SVGExport& rExport, SVGAttributeWriter& rAttributeWriter,
+                             SVGActionWriter& rActionWriter)
 : mrExport( rExport ),
   mrAttributeWriter( rAttributeWriter ),
+  mrActionWriter(rActionWriter),
   mpVDev( nullptr ),
   mbIsTextShapeStarted( false ),
   mrTextShape(),
@@ -592,7 +594,8 @@ bool SVGTextWriter::implGetTextPositionFromBitmap( const MetaAction* pAction, Po
  *     0 if no text found and end of text shape is reached
  *     1 if text found!
  */
-sal_Int32 SVGTextWriter::setTextPosition( const GDIMetaFile& rMtf, sal_uLong& nCurAction )
+sal_Int32 SVGTextWriter::setTextPosition(const GDIMetaFile& rMtf, sal_uLong& nCurAction,
+                                         sal_uInt32 nWriteFlags)
 {
     Point aPos;
     sal_uLong nCount = rMtf.GetActionSize();
@@ -628,6 +631,22 @@ sal_Int32 SVGTextWriter::setTextPosition( const GDIMetaFile& rMtf, sal_uLong& nC
             }
             break;
 
+            case MetaActionType::FLOATTRANSPARENT:
+            {
+                const MetaFloatTransparentAction* pA
+                    = static_cast<const MetaFloatTransparentAction*>(pAction);
+                GDIMetaFile aTmpMtf(pA->GetGDIMetaFile());
+                sal_uLong nTmpAction = 0;
+                if (setTextPosition(aTmpMtf, nTmpAction, nWriteFlags) == 1)
+                {
+                    // Text is found in the inner metafile.
+                    bConfigured = true;
+                    mrActionWriter.StartMask(pA->GetPoint(), pA->GetSize(), pA->GetGradient(),
+                                             nWriteFlags, &maTextOpacity);
+                }
+            }
+            break;
+
             case MetaActionType::STRETCHTEXT:
             {
                 bConfigured = implGetTextPosition<MetaStretchTextAction>( pAction, aPos, bEmpty );
@@ -1253,6 +1272,7 @@ void SVGTextWriter::endTextShape()
     mrParagraphEnumeration.clear();
     mrCurrentTextParagraph.clear();
     mpTextShapeElem.reset();
+    maTextOpacity.clear();
     mbIsTextShapeStarted = false;
     // these need to be invoked after the <text> element has been closed
     implExportHyperlinkIds();
@@ -1329,6 +1349,7 @@ void SVGTextWriter::endTextPosition()
     mpTextPositionElem.reset();
 }
 
+bool SVGTextWriter::hasTextOpacity() { return !maTextOpacity.isEmpty(); }
 
 void SVGTextWriter::implExportHyperlinkIds()
 {
@@ -1666,6 +1687,11 @@ void SVGTextWriter::implWriteTextPortion( const Point& rPos,
 
     addFontAttributes( /* isTexTContainer: */ false );
 
+    if (!maTextOpacity.isEmpty())
+    {
+        mrExport.AddAttribute(XML_NAMESPACE_NONE, "fill-opacity", maTextOpacity);
+    }
+
     mrAttributeWriter.AddPaintAttr( COL_TRANSPARENT, aTextColor );
 
     // <a> tag for link should be the innermost tag, inside <tspan>
@@ -1701,7 +1727,7 @@ SVGActionWriter::SVGActionWriter( SVGExport& rExport, SVGFontExport& rFontExport
     maContextHandler(),
     mrCurrentState( maContextHandler.getCurrentState() ),
     maAttributeWriter( rExport, rFontExport, mrCurrentState ),
-    maTextWriter( rExport, maAttributeWriter ),
+    maTextWriter(rExport, maAttributeWriter, *this),
     mpVDev(VclPtr<VirtualDevice>::Create()),
     mbClipAttrChanged( false ),
     mbIsPlaceholderShape( false )
@@ -2357,39 +2383,26 @@ Color SVGActionWriter::ImplGetGradientColor( const Color& rStartColor,
     return Color( static_cast<sal_uInt8>(nNewRed), static_cast<sal_uInt8>(nNewGreen), static_cast<sal_uInt8>(nNewBlue) );
 }
 
-
-void SVGActionWriter::ImplWriteMask( GDIMetaFile& rMtf,
-                                     const Point& rDestPt,
-                                     const Size& rDestSize,
-                                     const Gradient& rGradient,
-                                     sal_uInt32 nWriteFlags )
+void SVGActionWriter::StartMask(const Point& rDestPt, const Size& rDestSize,
+                                const Gradient& rGradient, sal_uInt32 nWriteFlags,
+                                OUString* pTextFillOpacity)
 {
-    Point          aSrcPt( rMtf.GetPrefMapMode().GetOrigin() );
-    const Size     aSrcSize( rMtf.GetPrefSize() );
-    const double   fScaleX = aSrcSize.Width() ? static_cast<double>(rDestSize.Width()) / aSrcSize.Width() : 1.0;
-    const double   fScaleY = aSrcSize.Height() ? static_cast<double>(rDestSize.Height()) / aSrcSize.Height() : 1.0;
-    long           nMoveX, nMoveY;
-
-    if( fScaleX != 1.0 || fScaleY != 1.0 )
-    {
-        rMtf.Scale( fScaleX, fScaleY );
-        aSrcPt.setX( FRound( aSrcPt.X() * fScaleX ) );
-        aSrcPt.setY( FRound( aSrcPt.Y() * fScaleY ) );
-    }
-
-    nMoveX = rDestPt.X() - aSrcPt.X();
-    nMoveY = rDestPt.Y() - aSrcPt.Y();
-
-    if( nMoveX || nMoveY )
-        rMtf.Move( nMoveX, nMoveY );
-
     OUString aStyle;
     if (rGradient.GetStartColor() == rGradient.GetEndColor())
     {
         // Special case: constant alpha value.
         const Color& rColor = rGradient.GetStartColor();
         const double fOpacity = 1.0 - static_cast<double>(rColor.GetLuminance()) / 255;
-        aStyle = "opacity: " + OUString::number(fOpacity);
+        if (pTextFillOpacity)
+        {
+            // Don't write anything, return what is a value suitable for <tspan fill-opacity="...">.
+            *pTextFillOpacity = OUString::number(fOpacity);
+            return;
+        }
+        else
+        {
+            aStyle = "opacity: " + OUString::number(fOpacity);
+        }
     }
     else
     {
@@ -2420,9 +2433,40 @@ void SVGActionWriter::ImplWriteMask( GDIMetaFile& rMtf,
         aStyle = "mask:url(#" + aMaskId + ")";
     }
     mrExport.AddAttribute(XML_NAMESPACE_NONE, aXMLAttrStyle, aStyle);
+}
+
+void SVGActionWriter::ImplWriteMask(GDIMetaFile& rMtf, const Point& rDestPt, const Size& rDestSize,
+                                    const Gradient& rGradient, sal_uInt32 nWriteFlags)
+{
+    Point aSrcPt(rMtf.GetPrefMapMode().GetOrigin());
+    const Size aSrcSize(rMtf.GetPrefSize());
+    const double fScaleX
+        = aSrcSize.Width() ? static_cast<double>(rDestSize.Width()) / aSrcSize.Width() : 1.0;
+    const double fScaleY
+        = aSrcSize.Height() ? static_cast<double>(rDestSize.Height()) / aSrcSize.Height() : 1.0;
+    long nMoveX, nMoveY;
 
+    if (fScaleX != 1.0 || fScaleY != 1.0)
     {
-        SvXMLElementExport aElemG( mrExport, XML_NAMESPACE_NONE, aXMLElemG, true, true );
+        rMtf.Scale(fScaleX, fScaleY);
+        aSrcPt.setX(FRound(aSrcPt.X() * fScaleX));
+        aSrcPt.setY(FRound(aSrcPt.Y() * fScaleY));
+    }
+
+    nMoveX = rDestPt.X() - aSrcPt.X();
+    nMoveY = rDestPt.Y() - aSrcPt.Y();
+
+    if (nMoveX || nMoveY)
+        rMtf.Move(nMoveX, nMoveY);
+
+    {
+        std::unique_ptr<SvXMLElementExport> pElemG;
+        if (!maTextWriter.hasTextOpacity())
+        {
+            StartMask(rDestPt, rDestSize, rGradient, nWriteFlags);
+            pElemG.reset(
+                new SvXMLElementExport(mrExport, XML_NAMESPACE_NONE, aXMLElemG, true, true));
+        }
 
         mpVDev->Push();
         ImplWriteActions( rMtf, nWriteFlags, nullptr );
@@ -3404,7 +3448,8 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
                             sal_Int32 nTextFound = -1;
                             while( ( nTextFound < 0 ) && ( nCurAction < nCount ) )
                             {
-                                nTextFound = maTextWriter.setTextPosition( rMtf, nCurAction );
+                                nTextFound
+                                    = maTextWriter.setTextPosition(rMtf, nCurAction, nWriteFlags);
                             }
                             // We found some text in the current text shape.
                             if( nTextFound > 0 )
@@ -3439,7 +3484,8 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
                             sal_Int32 nTextFound = -1;
                             while( ( nTextFound < 0 ) && ( nCurAction < nCount ) )
                             {
-                                nTextFound = maTextWriter.setTextPosition( rMtf, nCurAction );
+                                nTextFound
+                                    = maTextWriter.setTextPosition(rMtf, nCurAction, nWriteFlags);
                             }
                             // We found a paragraph with some text in the
                             // current text shape.
@@ -3472,7 +3518,8 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
                             sal_Int32 nTextFound = -2;
                             while( ( nTextFound < -1 ) && ( nCurAction < nCount ) )
                             {
-                                nTextFound = maTextWriter.setTextPosition( rMtf, nCurAction );
+                                nTextFound
+                                    = maTextWriter.setTextPosition(rMtf, nCurAction, nWriteFlags);
                             }
                             // We found a line with some text in the current
                             // paragraph.
diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx
index b3070d758ab2..fea556060619 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -202,6 +202,7 @@ class SVGTextWriter final
   private:
     SVGExport&                                  mrExport;
     SVGAttributeWriter&                         mrAttributeWriter;
+    SVGActionWriter& mrActionWriter;
     VclPtr<VirtualDevice>                       mpVDev;
     bool                                        mbIsTextShapeStarted;
     Reference<XText>                            mrTextShape;
@@ -215,6 +216,7 @@ class SVGTextWriter final
     std::unique_ptr<SvXMLElementExport>         mpTextShapeElem;
     std::unique_ptr<SvXMLElementExport>         mpTextParagraphElem;
     std::unique_ptr<SvXMLElementExport>         mpTextPositionElem;
+    OUString maTextOpacity;
     sal_Int32                                   mnLeftTextPortionLength;
     Point                                       maTextPos;
     long int                                    mnTextWidth;
@@ -234,10 +236,12 @@ class SVGTextWriter final
     vcl::Font                                   maParentFont;
 
   public:
-    explicit SVGTextWriter( SVGExport& rExport, SVGAttributeWriter& rAttributeWriter );
+    explicit SVGTextWriter(SVGExport& rExport, SVGAttributeWriter& rAttributeWriter,
+            SVGActionWriter& mrActionWriter);
     ~SVGTextWriter();
 
-    sal_Int32 setTextPosition( const GDIMetaFile& rMtf, sal_uLong& nCurAction );
+    sal_Int32 setTextPosition(const GDIMetaFile& rMtf, sal_uLong& nCurAction,
+                              sal_uInt32 nWriteFlags);
     void setTextProperties( const GDIMetaFile& rMtf, sal_uLong nCurAction );
     void addFontAttributes( bool bIsTextContainer );
 
@@ -252,6 +256,7 @@ class SVGTextWriter final
     void endTextParagraph();
     void startTextPosition( bool bExportX = true, bool bExportY = true);
     void endTextPosition();
+    bool hasTextOpacity();
     void implExportHyperlinkIds();
     void implWriteBulletChars();
     template< typename MetaBitmapActionType >
@@ -366,6 +371,8 @@ public:
                                            const OUString* pElementId = nullptr,
                                            const Reference< css::drawing::XShape >* pXShape = nullptr,
                                            const GDIMetaFile* pTextEmbeddedBitmapMtf = nullptr );
+    void StartMask(const Point& rDestPt, const Size& rDestSize, const Gradient& rGradient,
+                   sal_uInt32 nWriteFlags, OUString* pTextStyle = nullptr);
 };
 
 


More information about the Libreoffice-commits mailing list