[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