[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.2' - 6 commits - filter/source sd/qa
Marco Cecchetti (via logerrit)
logerrit at kemper.freedesktop.org
Mon Feb 22 19:05:30 UTC 2021
filter/source/svg/presentation_engine.js | 129 ++++++++++------
filter/source/svg/svgexport.cxx | 192 +++++++++++++++++++++++-
filter/source/svg/svgfilter.hxx | 13 +
filter/source/svg/svgwriter.cxx | 30 +++
filter/source/svg/svgwriter.hxx | 1
sd/qa/unit/SVGExportTests.cxx | 190 ++++++++++++++++++++++-
sd/qa/unit/data/odp/slide-bitmap-background.odp |binary
sd/qa/unit/data/odp/slide-tile-background.odp |binary
8 files changed, 493 insertions(+), 62 deletions(-)
New commits:
commit 5ad541462aec381bb6a9d86db5ed20ecb6ddb496
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Fri Feb 19 16:04:07 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Feb 22 20:04:48 2021 +0100
filter: svg: js engine: misplaced text: improving text field handling
Change-Id: I8b5f9a39b3cd3fcfdae0d088eae0a875cf9404ee
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111065
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Andras Timar <andras.timar at collabora.com>
diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js
index 0d4fc767c4ad..24fd4f53d2a7 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -5575,68 +5575,99 @@ PlaceholderShape.prototype.isValid = function()
*/
PlaceholderShape.prototype.init = function()
{
-
var aTextFieldElement = getElementByClassName( this.masterPage.backgroundObjects, this.className );
if( aTextFieldElement )
{
- var aPlaceholderElement = getElementByClassName( aTextFieldElement, 'PlaceholderText' );
- if( aPlaceholderElement )
+ var aTextElem = getElementByClassName( aTextFieldElement, 'SVGTextShape' );
+ if( aTextElem )
{
- // Each text field element has an invisible rectangle that can be
- // regarded as the text field bounding box.
- // We exploit such a feature and the exported text adjust attribute
- // value in order to set up correctly the position and text
- // adjustment for the placeholder element.
- var aSVGRectElem = getElementByClassName( aTextFieldElement, 'BoundingBox' );
- if( aSVGRectElem )
+ var aPlaceholderElement = getElementByClassName(aTextElem, 'PlaceholderText');
+ if( aPlaceholderElement )
{
- var aRect = new Rectangle( aSVGRectElem );
- var sTextAdjust = getOOOAttribute( aTextFieldElement, aOOOAttrTextAdjust ) || 'left';
- var sTextAnchor, sX;
- if( sTextAdjust == 'left' )
- {
- sTextAnchor = 'start';
- sX = String( aRect.left );
- }
- else if( sTextAdjust == 'right' )
- {
- sTextAnchor = 'end';
- sX = String( aRect.right );
- }
- else if( sTextAdjust == 'center' )
+ // SVG 1.1 does not support text wrapping wrt a rectangle.
+ // When a text shape contains a placeholder, setting up the position
+ // of each text line doesn't work since the position is computed
+ // before replacing the placeholder text.
+ // Anyway each text shape has an invisible rectangle that can be
+ // regarded as the text shape bounding box.
+ // We exploit such a feature and the exported text adjust attribute
+ // value in order to set up correctly the position and text
+ // adjustment for the text shape content.
+ // We assume that once the real value has been substituted to
+ // the placeholder the resulting content is no more than a single line.
+ // So we remove from <tspan> elements used for setting up the
+ // position of text lines (class TextPosition) the 'x' and 'y' attribute.
+ // In the general case we would need to implement a function
+ // which is able to compute at which words the text shape content has
+ // to be wrapped.
+ var aSVGRectElem = getElementByClassName( aTextFieldElement, 'BoundingBox' );
+ if( aSVGRectElem )
{
- sTextAnchor = 'middle';
- var nMiddle = ( aRect.left + aRect.right ) / 2;
- sX = String( parseInt( String( nMiddle ) ) );
+ var aRect = new Rectangle( aSVGRectElem );
+ var sTextAdjust = getOOOAttribute( aTextFieldElement, aOOOAttrTextAdjust );
+ // the bbox of the text shape is indeed a bit larger, there is a bit of internal padding
+ var nMargin = 250; // 1000th mm
+ var sTextAnchor, sX;
+ if( sTextAdjust == 'left' )
+ {
+ sTextAnchor = 'start';
+ sX = String( Math.trunc( aRect.left + nMargin ) );
+ }
+ else if( sTextAdjust == 'right' )
+ {
+ sTextAnchor = 'end';
+ sX = String( Math.trunc( aRect.right - nMargin ) );
+ }
+ else if( sTextAdjust == 'center' )
+ {
+ sTextAnchor = 'middle';
+ var nMiddle = ( aRect.left + aRect.right ) / 2;
+ sX = String( parseInt( String( nMiddle ) ) );
+ }
+ if( sTextAnchor )
+ {
+ aTextElem.setAttribute( 'text-anchor', sTextAnchor );
+ if( sX )
+ aTextElem.setAttribute( 'x', sX );
+
+ var aTSpanElements = getElementsByClassName( aTextElem, 'TextPosition' );
+ if( aTSpanElements )
+ {
+ var i = 0;
+ for( ; i < aTSpanElements.length; ++i )
+ {
+ var aTSpanElem = aTSpanElements[i];
+ aTSpanElem.removeAttribute( 'x' );
+ if( i !== 0 )
+ aTSpanElem.removeAttribute( 'y' );
+ }
+ }
+ }
}
- if( sTextAnchor )
- aPlaceholderElement.setAttribute( 'text-anchor', sTextAnchor );
- if( sX )
- aPlaceholderElement.setAttribute( 'x', sX );
- }
- // date/time fields were not exported correctly when positioned chars are used
- if( this.masterPage.metaSlide.theMetaDoc.bIsUsePositionedChars )
- {
- // We remove all text lines but the first one used as placeholder.
- var aTextLineGroupElem = aPlaceholderElement.parentNode.parentNode;
- if( aTextLineGroupElem )
+ // date/time fields were not exported correctly when positioned chars are used
+ if( this.masterPage.metaSlide.theMetaDoc.bIsUsePositionedChars )
{
- // Just to be sure it is the element we are looking for.
- var sFontFamilyAttr = aTextLineGroupElem.getAttribute( 'font-family' );
- if( sFontFamilyAttr )
+ // We remove all text lines but the first one used as placeholder.
+ var aTextLineGroupElem = aPlaceholderElement.parentNode.parentNode;
+ if( aTextLineGroupElem )
{
- var aChildSet = getElementChildren( aTextLineGroupElem );
- if( aChildSet.length > 1 )
- var i = 1;
- for( ; i < aChildSet.length; ++i )
+ // Just to be sure it is the element we are looking for.
+ var sFontFamilyAttr = aTextLineGroupElem.getAttribute( 'font-family' );
+ if( sFontFamilyAttr )
{
- aTextLineGroupElem.removeChild( aChildSet[i] );
+ var aChildSet = getElementChildren( aTextLineGroupElem );
+ if( aChildSet.length > 1 )
+ var i = 1;
+ for( ; i < aChildSet.length; ++i )
+ {
+ aTextLineGroupElem.removeChild( aChildSet[i] );
+ }
}
}
}
+ this.textElement = aPlaceholderElement;
}
- this.textElement = aPlaceholderElement;
}
this.element = aTextFieldElement;
}
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index dec88345b43d..1f71feafe93a 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -2135,14 +2135,18 @@ bool SVGFilter::implExportShape( const Reference< css::drawing::XShape >& rxShap
bool bIsPageNumber = ( aShapeClass == "Slide_Number" );
bool bIsFooter = ( aShapeClass == "Footer" );
bool bIsDateTime = ( aShapeClass == "Date/Time" );
- if( bIsPageNumber || bIsDateTime || bIsFooter )
+ bool bTextField = bIsPageNumber || bIsFooter || bIsDateTime;
+ if( bTextField )
{
// to notify to the SVGActionWriter::ImplWriteActions method
// that we are dealing with a placeholder shape
pElementId = &sPlaceholderTag;
mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "visibility", "hidden" );
+ }
+ if( bTextField || ( aShapeClass == "TextShape" ) )
+ {
sal_uInt16 nTextAdjust = sal_uInt16(ParagraphAdjust_LEFT);
OUString sTextAdjust;
xShapePropSet->getPropertyValue( "ParaAdjust" ) >>= nTextAdjust;
commit aa03f345bd00334e8fdaaafba4e2ea69470e381d
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Wed Feb 17 23:46:23 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Feb 22 20:04:48 2021 +0100
filter: svg: unit test for placeholder locale
We set the language to it-IT and check that the exported placeholder
text is still <number> instead of <numero>
Change-Id: I7ec7e25e53075da38cb87d81e9f8268b37121bfe
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111115
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Andras Timar <andras.timar at collabora.com>
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 14b159e63529..269c75cedbde 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -14,7 +14,9 @@
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <comphelper/processfactory.hxx>
-#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
+#include <unotools/syslocaleoptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
#include <boost/preprocessor/stringize.hpp>
@@ -66,6 +68,29 @@ static bool isValidTiledBackgroundId(const OUString& sId)
class SdSVGFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
{
+ class Resetter
+ {
+ private:
+ std::function<void ()> m_Func;
+
+ public:
+ Resetter(std::function<void ()> const& rFunc)
+ : m_Func(rFunc)
+ {
+ }
+ ~Resetter()
+ {
+ try
+ {
+ m_Func();
+ }
+ catch (...) // has to be reliable
+ {
+ CPPUNIT_FAIL("resetter failed with exception");
+ }
+ }
+ };
+
uno::Reference<lang::XComponent> mxComponent;
utl::TempFile maTempFile;
@@ -279,6 +304,37 @@ public:
CPPUNIT_ASSERT_EQUAL_MESSAGE("The href attribute for <use> does not match the tiled background id attribute: ", sBackgroundId, sRef);
}
+ void testSVGPlaceholderLocale()
+ {
+ static const OUString aLangISO("it-IT");
+ SvtSysLocaleOptions aSysLocaleOptions;
+ aSysLocaleOptions.SetLocaleConfigString(aLangISO);
+ aSysLocaleOptions.SetUILocaleConfigString(aLangISO);
+
+ auto aSavedSettings = Application::GetSettings();
+ Resetter aResetter([&]() { Application::SetSettings(aSavedSettings); });
+ AllSettings aSettings(aSavedSettings);
+ aSettings.SetLanguageTag(aLangISO, true);
+ Application::SetSettings(aSettings);
+
+ executeExport("text-fields.odp");
+
+ xmlDocPtr svgDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(svgDoc);
+
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2] ), "class", "Master_Slide");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2] ), "class", "BackgroundObjects");
+
+ // Slide Name Field
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6] ), "class", "TextShape");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "class", "PlaceholderText");
+ assertXPathContent(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "<slide-name>");
+ // Slide Number Field
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7] ), "class", "TextShape");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "class", "PlaceholderText");
+ assertXPathContent(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "<number>");
+ }
+
CPPUNIT_TEST_SUITE(SdSVGFilterTest);
CPPUNIT_TEST(testSVGExportTextDecorations);
CPPUNIT_TEST(testSVGExportJavascriptURL);
@@ -286,6 +342,7 @@ public:
CPPUNIT_TEST(testSVGExportTextFieldsInMasterPage);
CPPUNIT_TEST(testSVGExportSlideBitmapBackground);
CPPUNIT_TEST(testSVGExportSlideTileBitmapBackground);
+ CPPUNIT_TEST(testSVGPlaceholderLocale);
CPPUNIT_TEST_SUITE_END();
};
commit 27f43e1ee175806837a833a1495f2b4e59cd33c6
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Wed Feb 17 13:21:07 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Feb 22 20:04:48 2021 +0100
filter: svg: export: renaming class attributes related to TextShape
TextShape => SVGTextShape
com.sun.star.drawing.TextShape => TextShape
Change-Id: I4bbb465e0f65aa328527ac3022c0b68546fb5db6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111224
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Andras Timar <andras.timar at collabora.com>
diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js
index f6a42c4c0223..0d4fc767c4ad 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -5357,7 +5357,7 @@ function getTextFieldType ( elem )
{
var sFieldType = null;
var sClass = elem.getAttribute('class');
- if( sClass.endsWith( 'TextShape' ) )
+ if( sClass == 'TextShape' )
{
var aPlaceholderElement = getElementByClassName( elem, 'PlaceholderText' );
if (aPlaceholderElement)
@@ -14711,7 +14711,7 @@ function AnimatedTextElement( aElement, aEventMultiplexer )
}
var aTextShapeElement = aElement.parentNode;
sTextType = aTextShapeElement.getAttribute( 'class' );
- if( sTextType !== 'TextShape' )
+ if( sTextType !== 'SVGTextShape' )
{
log( 'AnimatedTextElement: element parent is not a text shape.' );
return;
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index a7a08f486df9..dec88345b43d 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -2600,6 +2600,8 @@ OUString SVGFilter::implGetClassFromShape( const Reference< css::drawing::XShape
aRet = "Graphic";
else if( aShapeType.lastIndexOf( "drawing.OLE2Shape" ) != -1 )
aRet = "OLE2";
+ else if( aShapeType.lastIndexOf( "drawing.TextShape" ) != -1 )
+ aRet = "TextShape";
else if( aShapeType.lastIndexOf( "presentation.HeaderShape" ) != -1 )
aRet = "Header";
else if( aShapeType.lastIndexOf( "presentation.FooterShape" ) != -1 )
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index f10906147777..07c234f036a1 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -1252,7 +1252,7 @@ void SVGTextWriter::startTextShape()
{
mbIsTextShapeStarted = true;
maParentFont = vcl::Font();
- mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", "TextShape" );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", "SVGTextShape" );
// if text is rotated, set transform matrix at text element
const vcl::Font& rFont = mpVDev->GetFont();
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 4a45cc4edf29..14b159e63529 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -139,11 +139,11 @@ public:
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2] ), "class", "SlideGroup");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G ), "class", "Slide");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[1] ), "class", "TitleText");
- assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[1]/SVG_G/SVG_TEXT ), "class", "TextShape");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[1]/SVG_G/SVG_TEXT ), "class", "SVGTextShape");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[1]/SVG_G/SVG_TEXT/SVG_TSPAN ), "class", "TextParagraph");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[1]/SVG_G/SVG_TEXT/SVG_TSPAN ), "text-decoration", "underline");
- assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[2]/SVG_G/SVG_TEXT ), "class", "TextShape");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[2]/SVG_G/SVG_TEXT ), "class", "SVGTextShape");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[2]/SVG_G/SVG_TEXT/SVG_TSPAN ), "class", "TextParagraph");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[2]/SVG_G/SVG_TEXT/SVG_TSPAN ), "text-decoration", "line-through");
}
@@ -182,19 +182,19 @@ public:
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2] ), "class", "Master_Slide");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2] ), "class", "BackgroundObjects");
// Current Date Field
- assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[4] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[4] ), "class", "TextShape");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[4]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "class", "PlaceholderText");
assertXPathContent(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[4]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "<date>");
// Current Time Field
- assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[5] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[5] ), "class", "TextShape");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[5]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "class", "PlaceholderText");
assertXPathContent(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[5]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "<time>");
// Slide Name Field
- assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6] ), "class", "TextShape");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "class", "PlaceholderText");
assertXPathContent(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "<slide-name>");
// Slide Number Field
- assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7] ), "class", "TextShape");
assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "class", "PlaceholderText");
assertXPathContent(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "<number>");
}
commit ecdbba7cf2326c77fd0ec3102554e3d1e8f97452
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Mon Feb 15 17:57:00 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Feb 22 15:49:21 2021 +0100
filter: svg: text field: placeholder localization issue
The text content for a placeholder is localized,
so in case a French locale is used, the placeholder
for a PageNumber text field is <numéro> instead of
<number>.
Change-Id: If1d31fee98d044775995b5b80567296f78d2a6c8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110944
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Ashod Nakashian <ash at collabora.com>
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index a96efaaec878..f10906147777 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -1112,6 +1112,7 @@ bool SVGTextWriter::nextTextPortion()
#endif
msPageCount = "";
msDateTimeType = "";
+ msTextFieldType = "";
if( xPortionTextRange.is() )
{
#if OSL_DEBUG_LEVEL > 0
@@ -1155,6 +1156,7 @@ bool SVGTextWriter::nextTextPortion()
++pNames;
}
+ msTextFieldType = sFieldName;
#if OSL_DEBUG_LEVEL > 0
sInfo += "text field type: " + sFieldName + "; content: " + xTextField->getPresentation( /* show command: */ false ) + "; ";
#endif
@@ -1690,7 +1692,6 @@ void SVGTextWriter::implWriteTextPortion( const Point& rPos,
if( mbIsPlaceholderShape )
{
mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", "PlaceholderText" );
- mbIsPlaceholderShape = false;
}
addFontAttributes( /* isTexTContainer: */ false );
@@ -1723,6 +1724,19 @@ void SVGTextWriter::implWriteTextPortion( const Point& rPos,
SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS );
mrExport.GetDocHandler()->characters( msDateTimeType );
}
+ else if( mbIsPlaceholderShape && rText.startsWith("<") && rText.endsWith(">") )
+ {
+ OUString sContent;
+ if( msTextFieldType == "PageNumber" )
+ sContent = "<number>";
+ else if( msTextFieldType == "PageName" )
+ sContent = "<slide-name>";
+ else
+ sContent = rText;
+
+ SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS );
+ mrExport.GetDocHandler()->characters( sContent );
+ }
else
{
SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS );
diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx
index c5725d63e491..11df01208168 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -252,6 +252,7 @@ class SVGTextWriter final
OUString msHyperlinkIdList;
OUString msPageCount;
OUString msDateTimeType;
+ OUString msTextFieldType;
bool mbIsPlaceholderShape;
static const bool mbIWS = false;
vcl::Font maCurrentFont;
commit 7ab136407252014273c9ba193f0bf9ea104c9db2
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Tue Feb 2 14:05:46 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Feb 22 15:49:03 2021 +0100
filter: svg: export tiled background by exploiting svg:pattern element
By exporting a tiled bitmap background by exploiting the <pattern>
element we get performance improvement when the background is made of
a big number of tiles.
The unit test for the tiled background case has been updated.
Change-Id: I80a4eebd081d2c59ec7d9906fc9c616692f7e0fa
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110319
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Ashod Nakashian <ash at collabora.com>
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index a2357b605b18..a7a08f486df9 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -512,6 +512,32 @@ static void MetaBitmapActionGetSize( const MetaAction* pAction, Size& rSz )
OSL_FAIL( "MetaBitmapActionGetSize: passed MetaAction pointer is null." );
return;
}
+ const MetaActionType nType = pAction->GetType();
+ switch( nType )
+ {
+ case MetaActionType::BMPSCALE:
+ {
+ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
+ rSz = pA->GetSize();
+ }
+ break;
+ case MetaActionType::BMPEXSCALE:
+ {
+ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
+ rSz = pA->GetSize();
+ }
+ break;
+ default: break;
+ }
+}
+
+static void MetaBitmapActionGetOrigSize( const MetaAction* pAction, Size& rSz )
+{
+ if( !pAction )
+ {
+ OSL_FAIL( "MetaBitmapActionGetOrigSize: passed MetaAction pointer is null." );
+ return;
+ }
const MetaActionType nType = pAction->GetType();
MapMode aSourceMode( MapUnit::MapPixel );
@@ -538,6 +564,16 @@ static void MetaBitmapActionGetSize( const MetaAction* pAction, Size& rSz )
rSz = OutputDevice::LogicToLogic( rSz, aSourceMode, aTargetMode );
}
+static OUString getPatternIdForTiledBackground( const OUString& sSlideId, BitmapChecksum nChecksum )
+{
+ return "bg-pattern." + sSlideId + "." + OUString::number( nChecksum );
+}
+
+static OUString getIdForTiledBackground( const OUString& sSlideId, BitmapChecksum nChecksum )
+{
+ return "bg-" + sSlideId + "." + OUString::number( nChecksum );
+}
+
} // end anonymous namespace
size_t HashBitmap::operator()( const ObjectRepresentation& rObjRep ) const
@@ -945,6 +981,7 @@ bool SVGFilter::implExportDocument()
implExportTextEmbeddedBitmaps();
implExportBackgroundBitmaps();
mpSVGWriter->SetEmbeddedBitmapRefs( &maBitmapActionMap );
+ implExportTiledBackground();
}
// #i124608# export a given object selection, so no MasterPage export at all
@@ -1530,6 +1567,77 @@ void SVGFilter::implExportBackgroundBitmaps()
}
}
+void SVGFilter::implExportTiledBackground()
+{
+ if( maPatterProps.empty() )
+ return;
+
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class", "BackgroundPatterns" );
+ SvXMLElementExport aDefsContainerElem( *mpSVGExport, XML_NAMESPACE_NONE, "defs", true, true );
+
+ for( const auto& [ rSlideId, rData ] : maPatterProps )
+ {
+ auto aBitmapActionIt = maBitmapActionMap.find( rData.aBitmapChecksum );
+ if( aBitmapActionIt != maBitmapActionMap.end() )
+ {
+ // pattern element attributes
+ const OUString sPatternId = getPatternIdForTiledBackground( rSlideId, rData.aBitmapChecksum );
+ // <pattern> <use>
+ {
+ // pattern element attributes
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sPatternId );
+
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "x", OUString::number( rData.aPos.X() ) );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "y", OUString::number( rData.aPos.Y() ) );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "width", OUString::number( rData.aSize.Width() ) );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "height", OUString::number( rData.aSize.Height() ) );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "patternUnits", "userSpaceOnUse" );
+
+ SvXMLElementExport aPatternElem( *mpSVGExport, XML_NAMESPACE_NONE, "pattern", true, true );
+
+ // use element attributes
+ const Size& aOrigSize = aBitmapActionIt->second->GetPrefSize();
+ OUString sTransform;
+ Fraction aFractionX( rData.aSize.Width(), aOrigSize.Width() );
+ Fraction aFractionY( rData.aSize.Height(), aOrigSize.Height() );
+ double scaleX = rtl_math_round( double(aFractionX), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected );
+ double scaleY = rtl_math_round( double(aFractionY), 3, rtl_math_RoundingMode::rtl_math_RoundingMode_Corrected );
+ if( !rtl_math_approxEqual( scaleX, 1.0 ) || !rtl_math_approxEqual( scaleY, 1.0 ) )
+ sTransform += " scale(" + OUString::number( double(aFractionX) ) + ", " + OUString::number( double(aFractionY) ) + ")";
+
+ if( !sTransform.isEmpty() )
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "transform", sTransform );
+
+ // referenced bitmap
+ OUString sRefId = "#bitmap(" + OUString::number( rData.aBitmapChecksum ) + ")";
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "xlink:href", sRefId );
+
+ SvXMLElementExport aUseElem( *mpSVGExport, XML_NAMESPACE_NONE, "use", true, true );
+ } // </use> </pattern>
+
+ // <g> <rect>
+ {
+ // group
+ const OUString sBgId = getIdForTiledBackground( rSlideId, rData.aBitmapChecksum );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sBgId );
+
+ SvXMLElementExport aGroupElem( *mpSVGExport, XML_NAMESPACE_NONE, "g", true, true );
+
+ // rectangle
+ const OUString sUrl = "url(#" + sPatternId + ")";
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "x", "0" );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "y", "0" );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "width", OUString::number( rData.aSlideSize.Width() ) );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "height", OUString::number( rData.aSlideSize.Height() ) );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "stroke", "none" );
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "fill", sUrl );
+
+ SvXMLElementExport aRectElem( *mpSVGExport, XML_NAMESPACE_NONE, "rect", true, true );
+ } // </g> </rect>
+ }
+ }
+}
+
/** SVGFilter::implExportTextEmbeddedBitmaps
We export bitmaps embedded into text shapes, such as those used by list
items with image style, only once in a specific defs element.
@@ -2379,21 +2487,63 @@ void SVGFilter::implCreateObjectsFromBackground( const Reference< css::drawing::
xExporter->filter( aDescriptor );
aMtf.Read( *aFile.GetStream( StreamMode::READ ) );
- MetaAction* pAction;
+ bool bIsBitmap = false;
+ bool bIsTiled = false;
+
+ // look for background type
+ Reference< XPropertySet > xPropSet( rxDrawPage, UNO_QUERY );
+ if( xPropSet.is() )
+ {
+ Reference< XPropertySet > xBackground;
+ xPropSet->getPropertyValue( "Background" ) >>= xBackground;
+ if( xBackground.is() )
+ {
+ drawing::FillStyle aFillStyle;
+ if( xBackground->getPropertyValue( "FillStyle" ) >>= aFillStyle )
+ {
+ if( aFillStyle == drawing::FillStyle::FillStyle_BITMAP )
+ {
+ bIsBitmap = true;
+ xBackground->getPropertyValue( "FillBitmapTile" ) >>= bIsTiled;
+
+ // we do not handle tiled background with a row or column offset
+ sal_Int32 nFillBitmapOffsetX = 0, nFillBitmapOffsetY = 0;
+ xBackground->getPropertyValue( "FillBitmapOffsetX" ) >>= nFillBitmapOffsetX;
+ xBackground->getPropertyValue( "FillBitmapOffsetY" ) >>= nFillBitmapOffsetY;
+ bIsTiled = bIsTiled && ( nFillBitmapOffsetX == 0 && nFillBitmapOffsetY == 0 );
+ }
+ }
+ }
+ }
+
+ if( !bIsBitmap )
+ {
+ (*mpObjects)[ rxDrawPage ] = ObjectRepresentation( rxDrawPage, aMtf );
+ return;
+ }
+
+ GDIMetaFile aTiledMtf;
+ bool bBitmapFound = false;
+ MetaAction* pAction;
sal_uLong nCount = aMtf.GetActionSize();
for( sal_uLong nCurAction = 0; nCurAction < nCount; ++nCurAction )
{
pAction = aMtf.GetAction( nCurAction );
const MetaActionType nType = pAction->GetType();
+ // collect bitmap
if( nType == MetaActionType::BMPSCALE || nType == MetaActionType::BMPEXSCALE )
{
+ if( bBitmapFound )
+ continue;
+ bBitmapFound = true; // the subsequent bitmaps are still the same just translated
+
BitmapChecksum nChecksum = GetBitmapChecksum( pAction );
if( maBitmapActionMap.find( nChecksum ) == maBitmapActionMap.end() )
{
Point aPos; // (0, 0)
Size aSize;
- MetaBitmapActionGetSize( pAction, aSize );
+ MetaBitmapActionGetOrigSize( pAction, aSize );
MetaAction* pBitmapAction = CreateMetaBitmapAction( pAction, aPos, aSize );
if( pBitmapAction )
{
@@ -2405,10 +2555,38 @@ void SVGFilter::implCreateObjectsFromBackground( const Reference< css::drawing::
maBitmapActionMap[ nChecksum ].reset( pEmbeddedBitmapMtf );
}
}
+
+ if( bIsTiled )
+ {
+ // collect data for <pattern> and <rect>
+ const OUString & sPageId = implGetValidIDFromInterface( rxDrawPage );
+ Point aPos;
+ MetaBitmapActionGetPoint( pAction, aPos );
+ Size aSize;
+ MetaBitmapActionGetSize( pAction, aSize );
+
+ sal_Int32 nSlideWidth = 0, nSlideHeight = 0;
+ xPropSet->getPropertyValue( "Width" ) >>= nSlideWidth;
+ xPropSet->getPropertyValue( "Height" ) >>= nSlideHeight;
+
+ maPatterProps[ sPageId ] = { nChecksum, aPos, aSize, { nSlideWidth, nSlideHeight } };
+
+ // create meta comment action that is used to exporting
+ // a <use> element which points to the group element representing the background
+ const OUString sBgId = getIdForTiledBackground( sPageId, nChecksum );
+ OString sComment = sTiledBackgroundTag + " " + sBgId.toUtf8();
+ MetaCommentAction* pCommentAction = new MetaCommentAction( sComment );
+ if( pCommentAction )
+ aTiledMtf.AddAction( pCommentAction );
+ }
+ }
+ else if( bIsTiled && nType != MetaActionType::CLIPREGION )
+ {
+ aTiledMtf.AddAction( pAction );
}
}
- (*mpObjects)[ rxDrawPage ] = ObjectRepresentation( rxDrawPage, aMtf );
+ (*mpObjects)[ rxDrawPage ] = ObjectRepresentation( rxDrawPage, bIsTiled ? aTiledMtf : aMtf );
}
OUString SVGFilter::implGetClassFromShape( const Reference< css::drawing::XShape >& rxShape )
diff --git a/filter/source/svg/svgfilter.hxx b/filter/source/svg/svgfilter.hxx
index 64dc619dc739..3f7979ecf766 100644
--- a/filter/source/svg/svgfilter.hxx
+++ b/filter/source/svg/svgfilter.hxx
@@ -66,6 +66,8 @@ using namespace ::com::sun::star::xml::sax;
// Placeholder tag used into the ImplWriteActions method to filter text placeholder fields
static const OUString sPlaceholderTag( "<[:isPlaceholder:]>" );
+// This tag is used for exporting a slide background made of tiled bitmaps
+static const OString sTiledBackgroundTag( "SLIDE_BACKGROUND" );
class SVGExport : public SvXMLExport
{
@@ -174,6 +176,15 @@ struct EqualityBitmap
// This must match the same type definition in svgwriter.hxx
typedef std::unordered_map< BitmapChecksum, std::unique_ptr< GDIMetaFile > > MetaBitmapActionMap;
+struct PatternData
+{
+ BitmapChecksum aBitmapChecksum;
+ Point aPos;
+ Size aSize;
+ Size aSlideSize;
+};
+typedef std::map<OUString, PatternData> PatternPropertySet;
+
class SVGFontExport;
class SVGActionWriter;
class EditFieldInfo;
@@ -234,6 +245,7 @@ private:
MetaBitmapActionSet mEmbeddedBitmapActionSet;
ObjectMap mEmbeddedBitmapActionMap;
MetaBitmapActionMap maBitmapActionMap;
+ PatternPropertySet maPatterProps;
std::vector< Reference< css::drawing::XDrawPage > > mMasterPageTargets;
Link<EditFieldInfo*,void> maOldFieldHdl;
@@ -254,6 +266,7 @@ private:
void implEmbedBulletGlyph( sal_Unicode cBullet, const OUString & sPathData );
void implExportTextEmbeddedBitmaps();
void implExportBackgroundBitmaps();
+ void implExportTiledBackground();
void implGenerateScript();
bool implExportDocument();
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 4f4bea2dc98b..a96efaaec878 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -3568,6 +3568,18 @@ void SVGActionWriter::ImplWriteActions( const GDIMetaFile& rMtf,
}
}
}
+ else if( pA->GetComment().startsWithIgnoreAsciiCase( sTiledBackgroundTag ) )
+ {
+ // In the tile case the background is rendered through a rectangle
+ // filled by exploiting an exported pattern element.
+ // Both the pattern and the rectangle are embedded in a <defs> element.
+ // The comment content has the following format: "SLIDE_BACKGROUND <background-id>"
+ const OString& sComment = pA->GetComment();
+ OUString sRefId = "#" + OUString::fromUtf8( sComment.getToken(1, ' ') );
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, sRefId );
+
+ SvXMLElementExport aRefElem( mrExport, XML_NAMESPACE_NONE, "use", true, true );
+ }
}
break;
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index d14f7e146893..4a45cc4edf29 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -28,6 +28,8 @@
#define SVG_DEFS *[name()='defs']
#define SVG_IMAGE *[name()='image']
#define SVG_USE *[name()='use']
+#define SVG_PATTERN *[name()='pattern']
+#define SVG_RECT *[name()='rect']
using namespace css;
@@ -47,6 +49,19 @@ static BitmapChecksum getBitmapChecksumFromId(const OUString& sId)
OUString sChecksum = sId.copy( nStart, nCount );
return sChecksum.toUInt64();
}
+
+static bool isValidBackgroundPatternId(const OUString& sId)
+{
+ std::regex aRegEx( R"(bg\-pattern\.id\d+\.\d+)" );
+ return std::regex_match(sId.toUtf8().getStr(), aRegEx);
+}
+
+static bool isValidTiledBackgroundId(const OUString& sId)
+{
+ std::regex aRegEx( R"(bg\-id\d+\.\d+)" );
+ return std::regex_match(sId.toUtf8().getStr(), aRegEx);
+}
+
}
class SdSVGFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
@@ -219,31 +234,49 @@ public:
xmlDocPtr svgDoc = parseXml(maTempFile);
CPPUNIT_ASSERT(svgDoc);
+ // check the bitmap
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9] ), "class", "BackgroundBitmaps");
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_IMAGE ), 1);
+ // check the pattern and background rectangle
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10] ), "class", "BackgroundPatterns");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_PATTERN ), 1);
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_PATTERN/SVG_USE ), 1);
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_G/SVG_RECT ), 1);
+
+
+ // check that <pattern><use> is pointing to the correct <image>
OUString sImageId = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_IMAGE ), "id");
CPPUNIT_ASSERT_MESSAGE(OString("The exported bitmap has not a valid id: " + sImageId.toUtf8()).getStr(), isValidBitmapId(sImageId));
BitmapChecksum nChecksum = getBitmapChecksumFromId(sImageId);
CPPUNIT_ASSERT_MESSAGE(OString("The exported bitmap has not a valid checksum: " + sImageId.toUtf8()).getStr(), nChecksum != 0);
- // tiles case
- constexpr unsigned int nNumberOfTiles = 37;
+ OUString sRef = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_PATTERN/SVG_USE ), "href");
+ CPPUNIT_ASSERT_MESSAGE("The <pattern><use> element has not a valid href attribute: starting '#' not present.", sRef.startsWith("#"));
+ sRef = sRef.copy(1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The href attribute for <pattern><use> does not match the <image> id attribute: ", sImageId, sRef);
+
+ OUString sPatternId = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_PATTERN ), "id");
+ CPPUNIT_ASSERT_MESSAGE(OString("The exported pattern has not a valid id: " + sPatternId.toUtf8()).getStr(), isValidBackgroundPatternId(sPatternId));
+
+ OUString sFillUrl = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_G/SVG_RECT ), "fill");
+ CPPUNIT_ASSERT_MESSAGE("The fill attribute for the <rectangle> element has not a url format .", sFillUrl.startsWith("url(#") && sFillUrl.endsWith(")"));
+ // remove "url(#" and ")"
+ sFillUrl = sFillUrl.copy(5, sFillUrl.getLength() - 6);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The fill url for <rectangle> does not match the <pattern> id attribute: ", sPatternId, sFillUrl);
+
+ OUString sBackgroundId = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[10]/SVG_G ), "id");
+ CPPUNIT_ASSERT_MESSAGE(OString("The exported tiled background has not a valid id: " + sBackgroundId.toUtf8()).getStr(), isValidTiledBackgroundId(sBackgroundId));
+
+ // check <use> element that point to the tiled background
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS ), "class", "SlideBackground");
- assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_G/SVG_USE ), nNumberOfTiles);
-
- for (unsigned int i = 1; i <= nNumberOfTiles; ++i)
- {
- OString sIndex = OStringLiteral("[") + OString::number(i) + OStringLiteral("]");
- OUString sRef = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_G/SVG_USE ) + sIndex, "href");
- CPPUNIT_ASSERT_MESSAGE("The <use> element has not a valid href attribute: starting '#' not present.", sRef.startsWith("#"));
- sRef = sRef.copy(1);
- CPPUNIT_ASSERT_MESSAGE(OString("The <use> element does not point to a valid bitmap id: " + sRef.toUtf8()).getStr(), isValidBitmapId(sRef));
-
- BitmapChecksum nUseChecksum = getBitmapChecksumFromId(sRef);
- CPPUNIT_ASSERT_EQUAL_MESSAGE("The bitmap checksum used in <use> does not match the expected one: ", nChecksum, nUseChecksum);
- }
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_USE ), 1);
+
+ sRef = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_USE ), "href");
+ CPPUNIT_ASSERT_MESSAGE("The <use> element has not a valid href attribute: starting '#' not present.", sRef.startsWith("#"));
+ sRef = sRef.copy(1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The href attribute for <use> does not match the tiled background id attribute: ", sBackgroundId, sRef);
}
CPPUNIT_TEST_SUITE(SdSVGFilterTest);
commit 2c58d033cd3c4b26e40dca292e1e806b8cfcdbe4
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Tue Jan 26 09:36:44 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Mon Feb 22 15:45:00 2021 +0100
filter: svg: js engine: unit test: slide background: exporting bitmaps
Two unit tests:
1 - a slide background with a single bitmap
2 - a slide background with bitmap tiles
Change-Id: Iffdb9ea958ba07391dfbdcfd6e925a9461e2af84
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109932
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Ashod Nakashian <ash at collabora.com>
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 394a591ccbbf..d14f7e146893 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -18,15 +18,37 @@
#include <boost/preprocessor/stringize.hpp>
+#include <regex>
+
#define MAKE_PATH_STRING( path ) BOOST_PP_STRINGIZE( path )
#define SVG_SVG *[name()='svg']
#define SVG_G *[name()='g']
#define SVG_TEXT *[name()='text']
#define SVG_TSPAN *[name()='tspan']
#define SVG_DEFS *[name()='defs']
+#define SVG_IMAGE *[name()='image']
+#define SVG_USE *[name()='use']
using namespace css;
+namespace
+{
+static bool isValidBitmapId(const OUString& sId)
+{
+ std::regex aRegEx("bitmap\\(\\d+\\)");
+ return std::regex_match(sId.toUtf8().getStr(), aRegEx);
+}
+
+static BitmapChecksum getBitmapChecksumFromId(const OUString& sId)
+{
+ sal_Int32 nStart = sId.indexOf("(") + 1;
+ sal_Int32 nCount = sId.indexOf(")") - nStart;
+ CPPUNIT_ASSERT(nStart > 0 && nCount > 0);
+ OUString sChecksum = sId.copy( nStart, nCount );
+ return sChecksum.toUInt64();
+}
+}
+
class SdSVGFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
{
uno::Reference<lang::XComponent> mxComponent;
@@ -162,11 +184,75 @@ public:
assertXPathContent(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7]/SVG_G/SVG_TEXT/SVG_TSPAN/SVG_TSPAN/SVG_TSPAN ), "<number>");
}
+ void testSVGExportSlideBitmapBackground()
+ {
+ executeExport("slide-bitmap-background.odp");
+
+ xmlDocPtr svgDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(svgDoc);
+
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9] ), "class", "BackgroundBitmaps");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_IMAGE ), 1);
+
+ OUString sImageId = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_IMAGE ), "id");
+ CPPUNIT_ASSERT_MESSAGE(OString("The exported bitmap has not a valid id: " + sImageId.toUtf8()).getStr(), isValidBitmapId(sImageId));
+
+ BitmapChecksum nChecksum = getBitmapChecksumFromId(sImageId);
+ CPPUNIT_ASSERT_MESSAGE(OString("The exported bitmap has not a valid checksum: " + sImageId.toUtf8()).getStr(), nChecksum != 0);
+
+ // single image case
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS ), "class", "SlideBackground");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_G/SVG_USE ), 1);
+ OUString sRef = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_G/SVG_USE ), "href");
+ CPPUNIT_ASSERT_MESSAGE("The <use> element has not a valid href attribute: starting '#' not present.", sRef.startsWith("#"));
+ sRef = sRef.copy(1);
+ CPPUNIT_ASSERT_MESSAGE(OString("The <use> element does not point to a valid bitmap id: " + sRef.toUtf8()).getStr(), isValidBitmapId(sRef));
+
+ BitmapChecksum nUseChecksum = getBitmapChecksumFromId(sRef);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The bitmap checksum used in <use> does not match the expected one: ", nChecksum, nUseChecksum);
+ }
+
+ void testSVGExportSlideTileBitmapBackground()
+ {
+ executeExport("slide-tile-background.odp");
+
+ xmlDocPtr svgDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(svgDoc);
+
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9] ), "class", "BackgroundBitmaps");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_IMAGE ), 1);
+
+ OUString sImageId = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_IMAGE ), "id");
+ CPPUNIT_ASSERT_MESSAGE(OString("The exported bitmap has not a valid id: " + sImageId.toUtf8()).getStr(), isValidBitmapId(sImageId));
+
+ BitmapChecksum nChecksum = getBitmapChecksumFromId(sImageId);
+ CPPUNIT_ASSERT_MESSAGE(OString("The exported bitmap has not a valid checksum: " + sImageId.toUtf8()).getStr(), nChecksum != 0);
+
+ // tiles case
+ constexpr unsigned int nNumberOfTiles = 37;
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS ), "class", "SlideBackground");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_G/SVG_USE ), nNumberOfTiles);
+
+ for (unsigned int i = 1; i <= nNumberOfTiles; ++i)
+ {
+ OString sIndex = OStringLiteral("[") + OString::number(i) + OStringLiteral("]");
+ OUString sRef = getXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G[1]/SVG_G/SVG_G/SVG_G/SVG_DEFS/SVG_G/SVG_G/SVG_USE ) + sIndex, "href");
+ CPPUNIT_ASSERT_MESSAGE("The <use> element has not a valid href attribute: starting '#' not present.", sRef.startsWith("#"));
+ sRef = sRef.copy(1);
+ CPPUNIT_ASSERT_MESSAGE(OString("The <use> element does not point to a valid bitmap id: " + sRef.toUtf8()).getStr(), isValidBitmapId(sRef));
+
+ BitmapChecksum nUseChecksum = getBitmapChecksumFromId(sRef);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The bitmap checksum used in <use> does not match the expected one: ", nChecksum, nUseChecksum);
+ }
+ }
+
CPPUNIT_TEST_SUITE(SdSVGFilterTest);
CPPUNIT_TEST(testSVGExportTextDecorations);
CPPUNIT_TEST(testSVGExportJavascriptURL);
CPPUNIT_TEST(testSVGExportSlideCustomBackground);
CPPUNIT_TEST(testSVGExportTextFieldsInMasterPage);
+ CPPUNIT_TEST(testSVGExportSlideBitmapBackground);
+ CPPUNIT_TEST(testSVGExportSlideTileBitmapBackground);
CPPUNIT_TEST_SUITE_END();
};
diff --git a/sd/qa/unit/data/odp/slide-bitmap-background.odp b/sd/qa/unit/data/odp/slide-bitmap-background.odp
new file mode 100644
index 000000000000..46ea62be5a3a
Binary files /dev/null and b/sd/qa/unit/data/odp/slide-bitmap-background.odp differ
diff --git a/sd/qa/unit/data/odp/slide-tile-background.odp b/sd/qa/unit/data/odp/slide-tile-background.odp
new file mode 100644
index 000000000000..d926b555f457
Binary files /dev/null and b/sd/qa/unit/data/odp/slide-tile-background.odp differ
More information about the Libreoffice-commits
mailing list