[Libreoffice-commits] core.git: Branch 'distro/collabora/co-2021' - 13 commits - filter/qa filter/source sd/qa
Szymon KÅos (via logerrit)
logerrit at kemper.freedesktop.org
Wed Mar 31 08:44:19 UTC 2021
filter/qa/unit/svg.cxx | 4
filter/source/svg/presentation_engine.js | 366 +++++++++++++++++-------
filter/source/svg/svgexport.cxx | 356 +++++++++++++++++++++--
filter/source/svg/svgfilter.hxx | 18 +
filter/source/svg/svgwriter.cxx | 122 +++++++-
filter/source/svg/svgwriter.hxx | 7
sd/qa/unit/SVGExportTests.cxx | 223 ++++++++++++++
sd/qa/unit/data/odp/slide-bitmap-background.odp |binary
sd/qa/unit/data/odp/slide-custom-background.odp |binary
sd/qa/unit/data/odp/slide-tile-background.odp |binary
sd/qa/unit/data/odp/text-fields.odp |binary
11 files changed, 963 insertions(+), 133 deletions(-)
New commits:
commit 9b22ec7f858d8d453804121b107d7a221a312933
Author: Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Wed Mar 17 15:57:21 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Wed Mar 31 10:43:30 2021 +0200
Polyfill presentation_engine.js for IE11
IE11 doesn't support:
Array.includes, String.startsWith and Math.trunc
Change-Id: I71c5810ad9230988453f70e880f46869728f49c5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112645
Tested-by: Andras Timar <andras.timar at collabora.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 7f3cb48bcfbf..0babb0083cc8 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -31,6 +31,15 @@ var round = Math.round;
var abs = Math.abs;
var now = Date.now;
+/**
+ * polyfill for IE11
+ */
+if (!Math.trunc) {
+ Math.trunc = function (v) {
+ return v < 0 ? Math.ceil(v) : Math.floor(v);
+ };
+}
+
/**
* set a timeout with a given scope
* @param {Function} fn
@@ -5393,9 +5402,9 @@ function getTextFieldType ( elem )
function isTextFieldByClassName ( sClassName )
{
return sClassName === aDateTimeClassName || sClassName === aFooterClassName
- || sClassName === aHeaderClassName || sClassName.startsWith( aSlideNumberClassName )
- || sClassName.startsWith( aDateClassName ) || sClassName.startsWith( aTimeClassName )
- || sClassName.startsWith( aSlideNameClassName );
+ || sClassName === aHeaderClassName || sClassName.indexOf( aSlideNumberClassName ) == 0
+ || sClassName.indexOf( aDateClassName ) == 0 || sClassName.indexOf( aTimeClassName ) == 0
+ || sClassName.indexOf( aSlideNameClassName ) == 0;
}
/** Class MasterPage
@@ -5819,7 +5828,7 @@ MasterPageView.prototype.createElement = function()
for( ; i < aBackgroundObjectSubGroupIdList.length; ++i )
{
sId = aBackgroundObjectSubGroupIdList[i];
- if( sId.startsWith( aSlideNumberClassName ) )
+ if( sId.indexOf( aSlideNumberClassName ) == 0 )
{
// Slide Number Field
// The cloned element is appended directly to the field group element
@@ -5873,9 +5882,9 @@ MasterPageView.prototype.createElement = function()
aTextFieldHandlerSet, sMasterSlideId );
}
}
- else if( sId.startsWith( aDateClassName )
- || sId.startsWith( aTimeClassName )
- || sId.startsWith( aSlideNameClassName ) )
+ else if( sId.indexOf( aDateClassName ) == 0
+ || sId.indexOf( aTimeClassName ) == 0
+ || sId.indexOf( aSlideNameClassName ) == 0 )
{
this.initTextFieldHandler( sId, aPlaceholderShapeSet,
aTextFieldContentProviderSet, aDefsElement,
commit c2217ff6d0601d99a411438502d5dbd0be587052
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: Wed Mar 31 10:42:35 2021 +0200
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>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111848
Tested-by: Jenkins
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js
index 76bd4d41b12d..7f3cb48bcfbf 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -5589,68 +5589,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 650bd022aebb..e32ad834f1b8 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -2117,14 +2117,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 d798877468f0f483eae533e598f879b5adbdfa16
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: Wed Mar 31 10:42:22 2021 +0200
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>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111846
Tested-by: Jenkins
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx
index 792964cb00d9..bb7600d71626 100644
--- a/filter/qa/unit/svg.cxx
+++ b/filter/qa/unit/svg.cxx
@@ -144,14 +144,14 @@ CPPUNIT_TEST_FIXTURE(SvgFilterTest, testSemiTransparentText)
xmlDocUniquePtr pXmlDoc = parseXmlStream(&aStream);
- // We expect 2 groups of class "com.sun.star.drawing.TextShape" that
+ // We expect 2 groups of class "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.
- assertXPath(pXmlDoc, "//svg:g[@class='com.sun.star.drawing.TextShape']//svg:text", 2);
+ assertXPath(pXmlDoc, "//svg:g[@class='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']");
diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js
index 1ab4e5eac01b..76bd4d41b12d 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -5371,7 +5371,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)
@@ -14725,7 +14725,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 acd2b4ab2f4a..650bd022aebb 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -2582,6 +2582,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 5dfa543f5cca..57ccc67bf6ef 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -1277,7 +1277,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 4f6f56ce4774..dfda2d605da0 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -164,11 +164,11 @@ public:
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2] ), "class", "SlideGroup");
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G ), "class", "Slide");
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[1] ), "class", "TitleText");
- assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[1]/SVG_G/SVG_TEXT ), "class", "TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[1]/SVG_G/SVG_TEXT ), "class", "SVGTextShape");
assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[2]/SVG_G/SVG_TEXT ), "class", "TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_G[2]/SVG_G/SVG_TEXT ), "class", "SVGTextShape");
assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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");
}
@@ -207,19 +207,19 @@ public:
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2] ), "class", "Master_Slide");
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2] ), "class", "BackgroundObjects");
// Current Date Field
- assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[4] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[4] ), "class", "TextShape");
assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[5] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[5] ), "class", "TextShape");
assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6] ), "class", "TextShape");
assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7] ), "class", "TextShape");
assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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 d95fda7769cd9e1f8e095ee5756cf56b50a2d5d2
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: Wed Mar 31 10:42:10 2021 +0200
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>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111847
Tested-by: Jenkins
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index bf0322235b20..4f6f56ce4774 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -16,6 +16,9 @@
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <comphelper/processfactory.hxx>
+#include <unotools/syslocaleoptions.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
#include <regex>
@@ -65,6 +68,29 @@ 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 +305,37 @@ public:
CPPUNIT_ASSERT_EQUAL_MESSAGE("The href attribute for <use> does not match the tiled background id attribute: ", sBackgroundId, sRef);
}
+ void testSVGPlaceholderLocale()
+ {
+ 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");
+
+ xmlDocUniquePtr svgDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(svgDoc);
+
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2] ), "class", "Master_Slide");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2] ), "class", "BackgroundObjects");
+
+ // Slide Name Field
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6] ), "class", "TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7] ), "class", "TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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 +343,7 @@ public:
CPPUNIT_TEST(testSVGExportTextFieldsInMasterPage);
CPPUNIT_TEST(testSVGExportSlideBitmapBackground);
CPPUNIT_TEST(testSVGExportSlideTileBitmapBackground);
+ CPPUNIT_TEST(testSVGPlaceholderLocale);
CPPUNIT_TEST_SUITE_END();
};
commit 4d51c4db9b65555ff8bb6fd2a719814a3dfe53c7
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: Wed Mar 31 10:41:55 2021 +0200
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>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111845
Tested-by: Jenkins
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 56eba1236a32..5dfa543f5cca 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -1138,6 +1138,7 @@ bool SVGTextWriter::nextTextPortion()
#endif
msPageCount = "";
msDateTimeType = "";
+ msTextFieldType = "";
if( xPortionTextRange.is() )
{
#if OSL_DEBUG_LEVEL > 0
@@ -1181,6 +1182,7 @@ bool SVGTextWriter::nextTextPortion()
++pNames;
}
+ msTextFieldType = sFieldName;
#if OSL_DEBUG_LEVEL > 0
sInfo += "text field type: " + sFieldName + "; content: " + xTextField->getPresentation( /* show command: */ false ) + "; ";
#endif
@@ -1717,7 +1719,6 @@ void SVGTextWriter::implWriteTextPortion( const Point& rPos,
if( mbIsPlaceholderShape )
{
mrExport.AddAttribute( XML_NAMESPACE_NONE, "class", "PlaceholderText" );
- mbIsPlaceholderShape = false;
}
addFontAttributes( /* isTexTContainer: */ false );
@@ -1755,6 +1756,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 1fa351f10382..993d1162fcf3 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -231,6 +231,7 @@ class SVGTextWriter final
OUString msHyperlinkIdList;
OUString msPageCount;
OUString msDateTimeType;
+ OUString msTextFieldType;
bool mbIsPlaceholderShape;
static const bool mbIWS = false;
vcl::Font maCurrentFont;
commit 398c470cbe0b0b66a440e643b095615fb8c04471
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: Wed Mar 31 10:41:36 2021 +0200
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>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111844
Tested-by: Jenkins
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index 643f42bb3add..acd2b4ab2f4a 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -519,6 +519,32 @@ 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;
+ }
+}
+
+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 );
@@ -545,6 +571,16 @@ void MetaBitmapActionGetSize( const MetaAction* pAction, Size& rSz )
rSz = OutputDevice::LogicToLogic( rSz, aSourceMode, aTargetMode );
}
+OUString getPatternIdForTiledBackground( std::u16string_view sSlideId, BitmapChecksum nChecksum )
+{
+ return OUString::Concat("bg-pattern.") + sSlideId + "." + OUString::number( nChecksum );
+}
+
+OUString getIdForTiledBackground( std::u16string_view sSlideId, BitmapChecksum nChecksum )
+{
+ return OUString::Concat("bg-") + sSlideId + "." + OUString::number( nChecksum );
+}
+
} // end anonymous namespace
size_t HashBitmap::operator()( const ObjectRepresentation& rObjRep ) const
@@ -940,6 +976,7 @@ bool SVGFilter::implExportDocument()
implExportTextEmbeddedBitmaps();
implExportBackgroundBitmaps();
mpSVGWriter->SetEmbeddedBitmapRefs( &maBitmapActionMap );
+ implExportTiledBackground();
}
// #i124608# export a given object selection, so no MasterPage export at all
@@ -1520,6 +1557,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.
@@ -2361,21 +2469,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 )
{
@@ -2387,10 +2537,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 beb12f2add58..7bdaa9d60f09 100644
--- a/filter/source/svg/svgfilter.hxx
+++ b/filter/source/svg/svgfilter.hxx
@@ -55,6 +55,8 @@ using namespace ::com::sun::star::xml::sax;
// Placeholder tag used into the ImplWriteActions method to filter text placeholder fields
const OUString sPlaceholderTag( "<[:isPlaceholder:]>" );
+// This tag is used for exporting a slide background made of tiled bitmaps
+const OString sTiledBackgroundTag( "SLIDE_BACKGROUND" );
class SVGExport : public SvXMLExport
{
@@ -155,6 +157,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;
@@ -213,6 +224,7 @@ private:
MetaBitmapActionSet mEmbeddedBitmapActionSet;
ObjectMap mEmbeddedBitmapActionMap;
MetaBitmapActionMap maBitmapActionMap;
+ PatternPropertySet maPatterProps;
std::vector< Reference< css::drawing::XDrawPage > > mMasterPageTargets;
Link<EditFieldInfo*,void> maOldFieldHdl;
@@ -233,6 +245,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 23a8ce1c7a96..56eba1236a32 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -3633,6 +3633,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 60222d2dd263..bf0322235b20 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -26,6 +26,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;
@@ -46,6 +48,19 @@ BitmapChecksum getBitmapChecksumFromId(const OUString& sId)
OUString sChecksum = sId.copy( nStart, nCount );
return sChecksum.toUInt64();
}
+
+bool isValidBackgroundPatternId(const OUString& sId)
+{
+ std::regex aRegEx( R"(bg\-pattern\.id\d+\.\d+)" );
+ return std::regex_match(sId.toUtf8().getStr(), aRegEx);
+}
+
+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
@@ -218,31 +233,50 @@ public:
xmlDocUniquePtr 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");
+ bool bIsUrlFormat = sFillUrl.startsWith("url(#") && sFillUrl.endsWith(")");
+ CPPUNIT_ASSERT_MESSAGE("The fill attribute for the <rectangle> element has not a url format .", bIsUrlFormat);
+ // 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 64b4673c35430197374ce8fb19a9127f2f8d1e31
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: Wed Mar 31 10:41:16 2021 +0200
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>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111843
Tested-by: Jenkins
Reviewed-by: Andras Timar <andras.timar at collabora.com>
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 73fd266711c1..60222d2dd263 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -17,14 +17,37 @@
#include <com/sun/star/frame/Desktop.hpp>
#include <comphelper/processfactory.hxx>
+#include <regex>
+
#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
+{
+bool isValidBitmapId(const OUString& sId)
+{
+ std::regex aRegEx("bitmap\\(\\d+\\)");
+ return std::regex_match(sId.toUtf8().getStr(), aRegEx);
+}
+
+BitmapChecksum getBitmapChecksumFromId(const OUString& sId)
+{
+ sal_Int32 nStart = sId.indexOf("(") + 1;
+ sal_Int32 nCount = sId.indexOf(")") - nStart;
+ bool bIsValidRange = nStart > 0 && nCount > 0;
+ CPPUNIT_ASSERT(bIsValidRange);
+ OUString sChecksum = sId.copy( nStart, nCount );
+ return sChecksum.toUInt64();
+}
+}
+
class SdSVGFilterTest : public test::BootstrapFixture, public unotest::MacrosTest, public XmlTestTools
{
uno::Reference<lang::XComponent> mxComponent;
@@ -160,11 +183,75 @@ public:
assertXPathContent(svgDoc, SAL_STRINGIFY( /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");
+
+ xmlDocUniquePtr 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");
+
+ xmlDocUniquePtr 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
commit 9af8afa680ee32855091e313dd4aec1d2835a240
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Fri Jan 22 19:31:26 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Wed Mar 31 10:40:57 2021 +0200
filter: svg: js engine: support for bitmaps in slide background
When a slide background includes one or more bitmaps, they are
exported only once.
This avoid to export the same bitmap more than once when it is
embedded in several backgound slides and to export only one bitmap for
the tile style.
Change-Id: Ia5b75f7805541486b76a81f86907e88ed9d4764a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109835
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111842
Tested-by: Jenkins
Reviewed-by: Andras Timar <andras.timar at collabora.com>
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index f7fb766489a8..643f42bb3add 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -426,6 +426,12 @@ namespace
BitmapChecksum GetBitmapChecksum( const MetaAction* pAction )
{
+ if( !pAction )
+ {
+ OSL_FAIL( "GetBitmapChecksum: passed MetaAction pointer is null." );
+ return 0;
+ }
+
BitmapChecksum nChecksum = 0;
const MetaActionType nType = pAction->GetType();
@@ -434,19 +440,16 @@ BitmapChecksum GetBitmapChecksum( const MetaAction* pAction )
case MetaActionType::BMPSCALE:
{
const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
- if( pA )
- nChecksum = pA->GetBitmap().GetChecksum();
- else
- OSL_FAIL( "GetBitmapChecksum: MetaBmpScaleAction pointer is null." );
+ // The conversion to BitmapEx is needed since a Bitmap object is
+ // converted to BitmapEx before passing it to SVGActionWriter::ImplWriteBmp
+ // where the checksum is checked for matching.
+ nChecksum = BitmapEx( pA->GetBitmap() ).GetChecksum();
}
break;
case MetaActionType::BMPEXSCALE:
{
const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
- if( pA )
- nChecksum = pA->GetBitmapEx().GetChecksum();
- else
- OSL_FAIL( "GetBitmapChecksum: MetaBmpExScaleAction pointer is null." );
+ nChecksum = pA->GetBitmapEx().GetChecksum();
}
break;
default: break;
@@ -454,37 +457,95 @@ BitmapChecksum GetBitmapChecksum( const MetaAction* pAction )
return nChecksum;
}
-} // end anonymous namespace
+MetaAction* CreateMetaBitmapAction( const MetaAction* pAction, const Point& rPt, const Size& rSz )
+{
+ if( !pAction )
+ {
+ OSL_FAIL( "CreateMetaBitmapAction: passed MetaAction pointer is null." );
+ return nullptr;
+ }
+ MetaAction* pResAction = nullptr;
+ const MetaActionType nType = pAction->GetType();
+ switch( nType )
+ {
+ case MetaActionType::BMPSCALE:
+ {
+ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
+ pResAction = new MetaBmpScaleAction( rPt, rSz, pA->GetBitmap() );
+ }
+ break;
+ case MetaActionType::BMPEXSCALE:
+ {
+ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
+ pResAction = new MetaBmpExScaleAction( rPt, rSz, pA->GetBitmapEx() );
+ }
+ break;
+ default: break;
+ }
+ return pResAction;
+}
-static void MetaBitmapActionGetPoint( const MetaAction* pAction, Point& rPt )
+void MetaBitmapActionGetPoint( const MetaAction* pAction, Point& rPt )
{
+ if( !pAction )
+ {
+ OSL_FAIL( "MetaBitmapActionGetPoint: passed MetaAction pointer is null." );
+ return;
+ }
const MetaActionType nType = pAction->GetType();
switch( nType )
{
case MetaActionType::BMPSCALE:
{
const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
- if( pA )
- rPt = pA->GetPoint();
- else
- OSL_FAIL( "MetaBitmapActionGetPoint: MetaBmpScaleAction pointer is null." );
+ rPt = pA->GetPoint();
}
break;
case MetaActionType::BMPEXSCALE:
{
const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
- if( pA )
- rPt = pA->GetPoint();
- else
- OSL_FAIL( "MetaBitmapActionGetPoint: MetaBmpExScaleAction pointer is null." );
+ rPt = pA->GetPoint();
}
break;
default: break;
}
+}
+
+void MetaBitmapActionGetSize( const MetaAction* pAction, Size& rSz )
+{
+ if( !pAction )
+ {
+ OSL_FAIL( "MetaBitmapActionGetSize: passed MetaAction pointer is null." );
+ return;
+ }
+
+ const MetaActionType nType = pAction->GetType();
+ MapMode aSourceMode( MapUnit::MapPixel );
+ MapMode aTargetMode( MapUnit::Map100thMM );
+ switch( nType )
+ {
+ case MetaActionType::BMPSCALE:
+ {
+ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
+ const Bitmap& rBitmap = pA->GetBitmap();
+ rSz = rBitmap.GetSizePixel();
+ }
+ break;
+ case MetaActionType::BMPEXSCALE:
+ {
+ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
+ const BitmapEx& rBitmap = pA->GetBitmapEx();
+ rSz = rBitmap.GetSizePixel();
+ }
+ break;
+ default: break;
+ }
+ rSz = OutputDevice::LogicToLogic( rSz, aSourceMode, aTargetMode );
}
+} // end anonymous namespace
size_t HashBitmap::operator()( const ObjectRepresentation& rObjRep ) const
{
@@ -877,6 +938,8 @@ bool SVGFilter::implExportDocument()
implExportTextShapeIndex();
implEmbedBulletGlyphs();
implExportTextEmbeddedBitmaps();
+ implExportBackgroundBitmaps();
+ mpSVGWriter->SetEmbeddedBitmapRefs( &maBitmapActionMap );
}
// #i124608# export a given object selection, so no MasterPage export at all
@@ -1431,6 +1494,31 @@ void SVGFilter::implEmbedBulletGlyph( sal_Unicode cBullet, const OUString & sPat
}
+void SVGFilter::implExportBackgroundBitmaps()
+{
+ if (maBitmapActionMap.empty())
+ return;
+
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class", "BackgroundBitmaps" );
+ SvXMLElementExport aDefsContainerElem( *mpSVGExport, XML_NAMESPACE_NONE, "defs", true, true );
+
+ OUString sId;
+ for( const auto& rItem : maBitmapActionMap )
+ {
+ BitmapChecksum nChecksum = rItem.first;
+ const GDIMetaFile& aEmbeddedBitmapMtf = *(rItem.second);
+ MetaAction* pBitmapAction = aEmbeddedBitmapMtf.GetAction( 0 );
+ if( pBitmapAction )
+ {
+ sId = "bitmap(" + OUString::number( nChecksum ) + ")";
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sId );
+
+ const Point aPos; // (0, 0)
+ const Size aSize = aEmbeddedBitmapMtf.GetPrefSize();
+ mpSVGWriter->WriteMetaFile( aPos, aSize, aEmbeddedBitmapMtf, 0xffffffff );
+ }
+ }
+}
/** SVGFilter::implExportTextEmbeddedBitmaps
We export bitmaps embedded into text shapes, such as those used by list
@@ -2080,10 +2168,8 @@ bool SVGFilter::implCreateObjects()
// implementation status:
// - hatch stroke color is set to 'none' so the hatch is not visible, why?
// - gradient look is not really awesome, too few colors are used;
- // - stretched bitmap, gradient and hatch are not exported only once
+ // - gradient and hatch are not exported only once
// and then referenced in case more than one slide uses them.
- // - tiled bitmap: an image element is exported for each tile,
- // this is really too expensive!
Reference< XPropertySet > xPropSet( xDrawPage, UNO_QUERY );
if( xPropSet.is() )
{
@@ -2093,8 +2179,7 @@ bool SVGFilter::implCreateObjects()
{
drawing::FillStyle aFillStyle;
bool assigned = ( xBackground->getPropertyValue( "FillStyle" ) >>= aFillStyle );
- if( assigned && aFillStyle != drawing::FillStyle_NONE
- && aFillStyle != drawing::FillStyle_BITMAP )
+ if( assigned && aFillStyle != drawing::FillStyle_NONE )
{
implCreateObjectsFromBackground( xDrawPage );
}
@@ -2276,10 +2361,38 @@ void SVGFilter::implCreateObjectsFromBackground( const Reference< css::drawing::
xExporter->filter( aDescriptor );
aMtf.Read( *aFile.GetStream( StreamMode::READ ) );
+ MetaAction* pAction;
+ sal_uLong nCount = aMtf.GetActionSize();
+ for( sal_uLong nCurAction = 0; nCurAction < nCount; ++nCurAction )
+ {
+ pAction = aMtf.GetAction( nCurAction );
+ const MetaActionType nType = pAction->GetType();
+
+ if( nType == MetaActionType::BMPSCALE || nType == MetaActionType::BMPEXSCALE )
+ {
+ BitmapChecksum nChecksum = GetBitmapChecksum( pAction );
+ if( maBitmapActionMap.find( nChecksum ) == maBitmapActionMap.end() )
+ {
+ Point aPos; // (0, 0)
+ Size aSize;
+ MetaBitmapActionGetSize( pAction, aSize );
+ MetaAction* pBitmapAction = CreateMetaBitmapAction( pAction, aPos, aSize );
+ if( pBitmapAction )
+ {
+ GDIMetaFile* pEmbeddedBitmapMtf = new GDIMetaFile();
+ pEmbeddedBitmapMtf->AddAction( pBitmapAction );
+ pEmbeddedBitmapMtf->SetPrefSize( aSize );
+ pEmbeddedBitmapMtf->SetPrefMapMode( MapMode(MapUnit::Map100thMM) );
+
+ maBitmapActionMap[ nChecksum ].reset( pEmbeddedBitmapMtf );
+ }
+ }
+ }
+ }
+
(*mpObjects)[ rxDrawPage ] = ObjectRepresentation( rxDrawPage, aMtf );
}
-
OUString SVGFilter::implGetClassFromShape( const Reference< css::drawing::XShape >& rxShape )
{
OUString aRet;
diff --git a/filter/source/svg/svgfilter.hxx b/filter/source/svg/svgfilter.hxx
index b06889a1a489..beb12f2add58 100644
--- a/filter/source/svg/svgfilter.hxx
+++ b/filter/source/svg/svgfilter.hxx
@@ -152,6 +152,9 @@ struct EqualityBitmap
const ObjectRepresentation& rObjRep2 ) const;
};
+// This must match the same type definition in svgwriter.hxx
+typedef std::unordered_map< BitmapChecksum, std::unique_ptr< GDIMetaFile > > MetaBitmapActionMap;
+
class SVGFontExport;
class SVGActionWriter;
class EditFieldInfo;
@@ -209,6 +212,7 @@ private:
mTextShapeIdListMap;
MetaBitmapActionSet mEmbeddedBitmapActionSet;
ObjectMap mEmbeddedBitmapActionMap;
+ MetaBitmapActionMap maBitmapActionMap;
std::vector< Reference< css::drawing::XDrawPage > > mMasterPageTargets;
Link<EditFieldInfo*,void> maOldFieldHdl;
@@ -228,6 +232,7 @@ private:
void implEmbedBulletGlyphs();
void implEmbedBulletGlyph( sal_Unicode cBullet, const OUString & sPathData );
void implExportTextEmbeddedBitmaps();
+ void implExportBackgroundBitmaps();
void implGenerateScript();
bool implExportDocument();
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index b6dad1a91243..23a8ce1c7a96 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -1501,7 +1501,12 @@ void SVGTextWriter::implWriteEmbeddedBitmaps()
case MetaActionType::BMPSCALE:
{
const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
- nChecksum = pA->GetBitmap().GetChecksum();
+ // The conversion to BitmapEx is needed since at the point
+ // where the bitmap is actually exported a Bitmap object is
+ // converted to BitmapEx before computing the checksum used
+ // to generate the <image> element id.
+ // (See GetBitmapChecksum in svgexport.cxx)
+ nChecksum = BitmapEx( pA->GetBitmap() ).GetChecksum();
aPt = pA->GetPoint();
aSz = pA->GetSize();
}
@@ -1773,7 +1778,8 @@ SVGActionWriter::SVGActionWriter( SVGExport& rExport, SVGFontExport& rFontExport
maTextWriter(rExport, maAttributeWriter, *this),
mpVDev(VclPtr<VirtualDevice>::Create()),
mbClipAttrChanged( false ),
- mbIsPlaceholderShape( false )
+ mbIsPlaceholderShape( false ),
+ mpEmbeddedBitmapsMap( nullptr )
{
mpVDev->EnableOutput( false );
maTargetMapMode = MapMode(MapUnit::Map100thMM);
@@ -1917,7 +1923,6 @@ OUString SVGActionWriter::GetPathString( const tools::PolyPolygon& rPolyPoly, bo
return aPathData.makeStringAndClear();
}
-
BitmapChecksum SVGActionWriter::GetChecksum( const MetaAction* pAction )
{
GDIMetaFile aMtf;
@@ -1926,6 +1931,13 @@ BitmapChecksum SVGActionWriter::GetChecksum( const MetaAction* pAction )
return aMtf.GetChecksum();
}
+void SVGActionWriter::SetEmbeddedBitmapRefs( const MetaBitmapActionMap* pEmbeddedBitmapsMap )
+{
+ if (pEmbeddedBitmapsMap)
+ mpEmbeddedBitmapsMap = pEmbeddedBitmapsMap;
+ else
+ OSL_FAIL( "SVGActionWriter::SetEmbeddedBitmapRefs: passed pointer is null" );
+}
void SVGActionWriter::ImplWriteLine( const Point& rPt1, const Point& rPt2,
const Color* pLineColor )
@@ -2816,6 +2828,42 @@ void SVGActionWriter::ImplWriteBmp( const BitmapEx& rBmpEx,
{
if( !rBmpEx )
return;
+ if( mpEmbeddedBitmapsMap && !mpEmbeddedBitmapsMap->empty())
+ {
+ BitmapChecksum nChecksum = rBmpEx.GetChecksum();
+ if( mpEmbeddedBitmapsMap->find( nChecksum ) != mpEmbeddedBitmapsMap->end() )
+ {
+ // <use transform="translate(?) scale(?)" xlink:ref="?" >
+ OUString sTransform;
+
+ Point aPoint;
+ ImplMap( rPt, aPoint );
+ if( aPoint.X() != 0 || aPoint.Y() != 0 )
+ sTransform = "translate(" + OUString::number( aPoint.X() ) + ", " + OUString::number( aPoint.Y() ) + ")";
+
+ Size aSize;
+ ImplMap( rSz, aSize );
+
+ MapMode aSourceMode( MapUnit::MapPixel );
+ Size aPrefSize = OutputDevice::LogicToLogic( rSrcSz, aSourceMode, maTargetMapMode );
+ Fraction aFractionX( aSize.Width(), aPrefSize.Width() );
+ Fraction aFractionY( aSize.Height(), aPrefSize.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() )
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrTransform, sTransform );
+
+ // referenced bitmap template
+ OUString sRefId = "#bitmap(" + OUString::number( nChecksum ) + ")";
+ mrExport.AddAttribute( XML_NAMESPACE_NONE, aXMLAttrXLinkHRef, sRefId );
+
+ SvXMLElementExport aRefElem( mrExport, XML_NAMESPACE_NONE, "use", true, true );
+ return;
+ }
+ }
BitmapEx aBmpEx( rBmpEx );
const tools::Rectangle aBmpRect( Point(), rBmpEx.GetSizePixel() );
diff --git a/filter/source/svg/svgwriter.hxx b/filter/source/svg/svgwriter.hxx
index b7d03d3ee8c6..1fa351f10382 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -57,6 +57,8 @@ using namespace ::com::sun::star::xml::sax;
#define SVGWRITER_WRITE_TEXT 0x00000002
#define SVGWRITER_NO_SHAPE_COMMENTS 0x01000000
+// This must match the same type definition in svgexport.hxx
+typedef std::unordered_map< BitmapChecksum, std::unique_ptr< GDIMetaFile > > MetaBitmapActionMap;
struct SVGState
{
@@ -315,6 +317,7 @@ private:
MapMode maTargetMapMode;
bool mbClipAttrChanged;
bool mbIsPlaceholderShape;
+ const MetaBitmapActionMap* mpEmbeddedBitmapsMap;
tools::Long ImplMap( sal_Int32 nVal ) const;
@@ -370,6 +373,8 @@ public:
const OUString* pElementId = nullptr,
const Reference< css::drawing::XShape >* pXShape = nullptr,
const GDIMetaFile* pTextEmbeddedBitmapMtf = nullptr );
+
+ void SetEmbeddedBitmapRefs( const MetaBitmapActionMap* pEmbeddedBitmapsMap );
void StartMask(const Point& rDestPt, const Size& rDestSize, const Gradient& rGradient,
sal_uInt32 nWriteFlags, OUString* pTextStyle = nullptr);
};
commit c891b4570f52acbf9167a8705e43dbe91ef2773a
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Mon Jan 18 12:36:42 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Wed Mar 31 10:40:34 2021 +0200
filter: svg: js engine: text fields support: unit test
Change-Id: I80a7e7906fb2a82d955562a137b208497d4c0d9c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109543
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109892
Tested-by: Jenkins
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 76aad6a0e493..73fd266711c1 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -123,7 +123,7 @@ public:
1);
}
- void testSVGExporSlidetCustomBackground()
+ void testSVGExportSlideCustomBackground()
{
executeExport("slide-custom-background.odp");
@@ -133,10 +133,38 @@ public:
assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_DEFS ), "class", "SlideBackground");
}
+ void testSVGExportTextFieldsInMasterPage()
+ {
+ executeExport("text-fields.odp");
+
+ xmlDocUniquePtr svgDoc = parseXml(maTempFile);
+ CPPUNIT_ASSERT(svgDoc);
+
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2] ), "class", "Master_Slide");
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2] ), "class", "BackgroundObjects");
+ // Current Date Field
+ assertXPath(svgDoc, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[4] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[5] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[6] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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, SAL_STRINGIFY( /SVG_SVG/SVG_DEFS[9]/SVG_G[2]/SVG_G[2]/SVG_G[7] ), "class", "com.sun.star.drawing.TextShape");
+ assertXPath(svgDoc, SAL_STRINGIFY( /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, SAL_STRINGIFY( /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);
- CPPUNIT_TEST(testSVGExporSlidetCustomBackground);
+ CPPUNIT_TEST(testSVGExportSlideCustomBackground);
+ CPPUNIT_TEST(testSVGExportTextFieldsInMasterPage);
CPPUNIT_TEST_SUITE_END();
};
diff --git a/sd/qa/unit/data/odp/text-fields.odp b/sd/qa/unit/data/odp/text-fields.odp
new file mode 100644
index 000000000000..3c5d057ba2f0
Binary files /dev/null and b/sd/qa/unit/data/odp/text-fields.odp differ
commit e35d33510b5945c58b835e9a2021a35f493df14c
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Sun Jan 17 23:38:57 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Wed Mar 31 10:40:17 2021 +0200
filter: svg: js engine: further improving text fields handling
Added support for slide name text field on master page
Change-Id: I969bd3b2d030cf117f4c7022716b55152538f846
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109497
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109871
Tested-by: Jenkins
diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js
index c1a6a4ef988e..1ab4e5eac01b 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -4434,6 +4434,7 @@ var aOOOAttrUsePositionedChars = 'use-positioned-chars';
var aOOOAttrSlide = 'slide';
var aOOOAttrMaster = 'master';
+var aOOOAttrDisplayName = 'display-name';
var aOOOAttrSlideDuration = 'slide-duration';
var aOOOAttrHasTransition = 'has-transition';
var aOOOAttrHasCustomBackground = 'has-custom-background';
@@ -4460,6 +4461,7 @@ var aFooterClassName = 'Footer';
var aHeaderClassName = 'Header';
var aDateClassName = 'Date';
var aTimeClassName = 'Time';
+var aSlideNameClassName='SlideName';
// Creating a namespace dictionary.
var NSS = {};
@@ -5030,6 +5032,8 @@ function MetaSlide( sMetaSlideId, aMetaDoc )
else
this.nSlideNumber= -1;
+ this.slideName = this.element.getAttributeNS( NSS['ooo'], aOOOAttrDisplayName );
+
// Each slide element is double wrapped by <g> elements.
// The outer <g> element is responsible for
// the slide element visibility. In fact the visibility attribute has
@@ -5099,6 +5103,7 @@ function MetaSlide( sMetaSlideId, aMetaDoc )
this.aTextFieldContentProviderSet[aHeaderClassName] = this.initFixedTextFieldContentProvider( aOOOAttrHeaderField );
this.aTextFieldContentProviderSet[aDateClassName] = this.theMetaDoc.aCurrentDateProvider;
this.aTextFieldContentProviderSet[aTimeClassName] = this.theMetaDoc.aCurrentTimeProvider;
+ this.aTextFieldContentProviderSet[aSlideNameClassName] = new FixedTextProvider( this.slideName );
// We init the slide duration when automatic slide transition is enabled
this.fDuration = this.initSlideDuration();
@@ -5254,7 +5259,7 @@ initDateTimeFieldContentProvider : function( aOOOAttrDateTimeField )
var sClassName = getClassAttribute( aTextFieldElem );
if( sClassName == 'FixedDateTimeField' )
{
- aTextField = new FixedTextProvider( aTextFieldElem );
+ aTextField = new FixedTextByElementProvider( aTextFieldElem );
this.bIsDateTimeVariable = false;
}
else if( sClassName == 'VariableDateTimeField' )
@@ -5284,7 +5289,7 @@ initFixedTextFieldContentProvider : function( aOOOAttribute )
{
var aTextFieldElem = document.getElementById( sTextFieldId );
this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ]
- = new FixedTextProvider( aTextFieldElem );
+ = new FixedTextByElementProvider( aTextFieldElem );
}
return this.theMetaDoc.aTextFieldContentProviderSet[ nIndex ];
},
@@ -5378,6 +5383,8 @@ function getTextFieldType ( elem )
sFieldType = aDateClassName;
else if (sContent === '<time>')
sFieldType = aTimeClassName;
+ else if (sContent === '<slide-name>')
+ sFieldType = aSlideNameClassName;
}
}
return sFieldType;
@@ -5387,7 +5394,8 @@ function isTextFieldByClassName ( sClassName )
{
return sClassName === aDateTimeClassName || sClassName === aFooterClassName
|| sClassName === aHeaderClassName || sClassName.startsWith( aSlideNumberClassName )
- || sClassName.startsWith( aDateClassName ) || sClassName.startsWith( aTimeClassName );
+ || sClassName.startsWith( aDateClassName ) || sClassName.startsWith( aTimeClassName )
+ || sClassName.startsWith( aSlideNameClassName );
}
/** Class MasterPage
@@ -5834,13 +5842,9 @@ MasterPageView.prototype.createElement = function()
aTextFieldHandlerSet, sMasterSlideId );
}
}
- else if( sId.startsWith( aDateClassName ) )
- {
- this.initTextFieldHandler( sId, aPlaceholderShapeSet,
- aTextFieldContentProviderSet, aDefsElement,
- aTextFieldHandlerSet, sMasterSlideId );
- }
- else if( sId.startsWith( aTimeClassName ) )
+ else if( sId.startsWith( aDateClassName )
+ || sId.startsWith( aTimeClassName )
+ || sId.startsWith( aSlideNameClassName ) )
{
this.initTextFieldHandler( sId, aPlaceholderShapeSet,
aTextFieldContentProviderSet, aDefsElement,
@@ -6072,25 +6076,32 @@ SlideNumberFieldHandler.prototype.update = function( nPageNumber )
* The svg element that contains the text content for one or more
* master slide text field.
*/
-function TextFieldContentProvider( aTextFieldContentElement )
+function TextFieldContentProvider()
{
- // This id is used as key for the theMetaDoc.aTextFieldHandlerSet object.
- if( aTextFieldContentElement )
- this.sId = aTextFieldContentElement.getAttribute( 'id' );
+ this.sId = TextFieldContentProvider.getUniqueId();
}
+/*** private methods ***/
+
+TextFieldContentProvider.CURR_UNIQUE_ID = 0;
+
+TextFieldContentProvider.getUniqueId = function()
+{
+ ++TextFieldContentProvider.CURR_UNIQUE_ID;
+ return TextFieldContentProvider.CURR_UNIQUE_ID;
+};
+
/** Class FixedTextProvider
* This class handles text field with a fixed text.
* The text content is provided by the 'text' property.
*
- * @param aTextFieldContentElement
- * The svg element that contains the text content for one or more
- * master slide text field.
+ * @param aText
+ * a string containing the text to be substituted.
*/
-function FixedTextProvider( aTextFieldContentElement )
+function FixedTextProvider( aText )
{
- FixedTextProvider.superclass.constructor.call( this, aTextFieldContentElement );
- this.text = aTextFieldContentElement.textContent;
+ FixedTextProvider.superclass.constructor.call( this );
+ this.text = aText;
}
extend( FixedTextProvider, TextFieldContentProvider );
@@ -6108,6 +6119,20 @@ FixedTextProvider.prototype.update = function( aFixedTextField )
aFixedTextField.setTextContent( this.text );
};
+/** Class FixedTextByElementProvider
+ * This class handles text field with a fixed text.
+ * The text content is provided by the 'text' property.
+ *
+ * @param aTextFieldContentElement
+ * The svg element that contains the text content for one or more
+ * master slide text field.
+ */
+function FixedTextByElementProvider( aTextFieldContentElement )
+{
+ FixedTextByElementProvider.superclass.constructor.call( this, aTextFieldContentElement.textContent );
+}
+extend( FixedTextByElementProvider, FixedTextProvider );
+
/** Class CurrentDateTimeProvider
* Provide the text content to a date/time field by generating the current
* date/time in the format specified by the 'dateTimeFormat' property.
@@ -6124,7 +6149,6 @@ function CurrentDateTimeProvider( aTextFieldContentElement, sDateTimeFormat )
else
{
this.dateTimeFormat = sDateTimeFormat;
- this.sId = 'DateTimeProvider.' + sDateTimeFormat;
}
}
extend( CurrentDateTimeProvider, TextFieldContentProvider );
@@ -6165,7 +6189,7 @@ CurrentDateTimeProvider.prototype.createDateTimeText = function()
*/
function SlideNumberProvider( nInitialSlideNumber, sPageNumberingType )
{
- SlideNumberProvider.superclass.constructor.call( this, null );
+ SlideNumberProvider.superclass.constructor.call( this );
this.nInitialSlideNumber = nInitialSlideNumber;
this.pageNumberingType = sPageNumberingType;
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index 40c82be27dfd..f7fb766489a8 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -92,6 +92,7 @@ constexpr char16_t aOOOElemTextField[] = u"" NSPREFIX "text_field";
const char aOOOAttrSlide[] = NSPREFIX "slide";
const char aOOOAttrMaster[] = NSPREFIX "master";
const char aOOOAttrHasCustomBackground[] = NSPREFIX "has-custom-background";
+const char aOOOAttrDisplayName[] = NSPREFIX "display-name";
const char aOOOAttrBackgroundVisibility[] = NSPREFIX "background-visibility";
const char aOOOAttrMasterObjectsVisibility[] = NSPREFIX "master-objects-visibility";
const char aOOOAttrSlideDuration[] = NSPREFIX "slide-duration";
@@ -1147,6 +1148,12 @@ void SVGFilter::implGenerateMetaData()
if( xPropSet.is() )
{
+ OUString sDisplayName;
+ if (xPropSet->getPropertyValue("LinkDisplayName") >>= sDisplayName)
+ {
+ mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, aOOOAttrDisplayName, sDisplayName);
+ }
+
bool bBackgroundVisibility = true; // default: visible
bool bBackgroundObjectsVisibility = true; // default: visible
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index 248f6146bebb..b6dad1a91243 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -1210,7 +1210,8 @@ bool SVGTextWriter::nextTextPortion()
}
}
if( sFieldName == "DateTime" || sFieldName == "Header"
- || sFieldName == "Footer" || sFieldName == "PageNumber" )
+ || sFieldName == "Footer" || sFieldName == "PageNumber"
+ || sFieldName == "PageName" )
{
mbIsPlaceholderShape = true;
}
commit b92fb5b99c6f4929f45bee2439ed5bcacb6593c5
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Sun Jan 17 23:36:53 2021 +0100
Commit: Andras Timar <andras.timar at collabora.com>
CommitDate: Wed Mar 31 10:39:56 2021 +0200
filter: svg: js engine: improving text fields handling
Added support for slide number and current date, current time fields
inserted by the user on slides or master pages.
Change-Id: If21b06c58e8fdcc240a540ee6fa87f48a6eb86af
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109496
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Ashod Nakashian <ash at collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109870
Tested-by: Jenkins
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js
index 848f2dd84226..c1a6a4ef988e 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -4458,6 +4458,8 @@ var aSlideNumberClassName = 'Slide_Number';
var aDateTimeClassName = 'Date/Time';
var aFooterClassName = 'Footer';
var aHeaderClassName = 'Header';
+var aDateClassName = 'Date';
+var aTimeClassName = 'Time';
// Creating a namespace dictionary.
var NSS = {};
@@ -4909,6 +4911,8 @@ function MetaDocument()
this.aTextFieldHandlerSet = {};
this.aTextFieldContentProviderSet = [];
this.aSlideNumberProvider = new SlideNumberProvider( this.nStartSlideNumber + 1, this.sPageNumberingType );
+ this.aCurrentDateProvider = new CurrentDateTimeProvider( null, '<date>' );
+ this.aCurrentTimeProvider = new CurrentDateTimeProvider( null, '<time>' );
// We create a map with key an id and value the svg element containing
// the animations performed on the slide with such an id.
@@ -5064,6 +5068,9 @@ function MetaSlide( sMetaSlideId, aMetaDoc )
this.backgroundId = this.backgroundElement.getAttribute( 'id' );
}
+ // We initialize text fields
+ this.initPlaceholderElements();
+
// We initialize the MasterPage object that provides direct access to
// the target master page element.
this.masterPage = this.initMasterPage();
@@ -5090,6 +5097,8 @@ function MetaSlide( sMetaSlideId, aMetaDoc )
this.aTextFieldContentProviderSet[aDateTimeClassName] = this.initDateTimeFieldContentProvider( aOOOAttrDateTimeField );
this.aTextFieldContentProviderSet[aFooterClassName] = this.initFixedTextFieldContentProvider( aOOOAttrFooterField );
this.aTextFieldContentProviderSet[aHeaderClassName] = this.initFixedTextFieldContentProvider( aOOOAttrHeaderField );
+ this.aTextFieldContentProviderSet[aDateClassName] = this.theMetaDoc.aCurrentDateProvider;
+ this.aTextFieldContentProviderSet[aTimeClassName] = this.theMetaDoc.aCurrentTimeProvider;
// We init the slide duration when automatic slide transition is enabled
this.fDuration = this.initSlideDuration();
@@ -5160,6 +5169,23 @@ updateMasterPageView : function()
},
/*** private methods ***/
+
+// It handles a text field inserted on a slide, not on a master page.
+initPlaceholderElements : function()
+{
+ var aPlaceholderList = getElementsByClassName(this.pageElement , 'PlaceholderText' );
+ var i = 0;
+ for( ; i < aPlaceholderList.length; ++i )
+ {
+ var aPlaceholderElem = aPlaceholderList[i];
+ var sContent = aPlaceholderElem.textContent;
+ if( sContent === '<date>' )
+ aPlaceholderElem.textContent = new Date().toLocaleDateString();
+ else if( sContent === '<time>' )
+ aPlaceholderElem.textContent = new Date().toLocaleTimeString();
+ }
+},
+
initMasterPage : function()
{
var sMasterPageId = this.element.getAttributeNS( NSS['ooo'], aOOOAttrMaster );
@@ -5336,6 +5362,34 @@ getSlideAnimationsRoot : function()
}; // end MetaSlide prototype
+function getTextFieldType ( elem )
+{
+ var sFieldType = null;
+ var sClass = elem.getAttribute('class');
+ if( sClass.endsWith( 'TextShape' ) )
+ {
+ var aPlaceholderElement = getElementByClassName( elem, 'PlaceholderText' );
+ if (aPlaceholderElement)
+ {
+ var sContent = aPlaceholderElement.textContent
+ if (sContent === '<number>')
+ sFieldType = aSlideNumberClassName;
+ else if (sContent === '<date>')
+ sFieldType = aDateClassName;
+ else if (sContent === '<time>')
+ sFieldType = aTimeClassName;
+ }
+ }
+ return sFieldType;
+}
+
+function isTextFieldByClassName ( sClassName )
+{
+ return sClassName === aDateTimeClassName || sClassName === aFooterClassName
+ || sClassName === aHeaderClassName || sClassName.startsWith( aSlideNumberClassName )
+ || sClassName.startsWith( aDateClassName ) || sClassName.startsWith( aTimeClassName );
+}
+
/** Class MasterPage
* This class gives direct access to a master page element and to the following
* elements included in the master page:
@@ -5398,6 +5452,7 @@ function MasterPage( sMasterPageId, aMetaSlide )
// The background objects group element that contains every element presents
// on the master page except the background element.
this.backgroundObjects = getElementByClassName( this.element, 'BackgroundObjects' );
+ this.aBackgroundObjectSubGroupIdList = [];
if( this.backgroundObjects )
{
this.backgroundObjectsId = this.backgroundObjects.getAttribute( 'id' );
@@ -5411,13 +5466,26 @@ function MasterPage( sMasterPageId, aMetaSlide )
var nSubGroupId = 1;
var sClass;
var sId = '';
- this.aBackgroundObjectSubGroupIdList = [];
var i = 0;
for( ; i < aBackgroundObjectList.length; ++i )
{
- sClass = aBackgroundObjectList[i].getAttribute( 'class' );
- if( !sClass || ( ( sClass !== aDateTimeClassName ) && ( sClass !== aFooterClassName )
- && ( sClass !== aHeaderClassName ) && ( sClass !== aSlideNumberClassName ) ) )
+ var aObject = aBackgroundObjectList[i];
+ sClass = null;
+ var sFieldType = getTextFieldType( aObject );
+ if( sFieldType && aObject.firstElementChild )
+ {
+ var sObjId = aObject.firstElementChild.getAttribute( 'id' );
+ if( sObjId )
+ {
+ sClass = sFieldType + '.' + sObjId;
+ aObject.setAttribute('class', sClass);
+ }
+ }
+ if( !sClass )
+ {
+ sClass = aBackgroundObjectList[i].getAttribute('class');
+ }
+ if( !sClass || !isTextFieldByClassName( sClass ) )
{
if( nCount === 0 )
{
@@ -5461,10 +5529,14 @@ MasterPage.prototype =
initPlaceholderShapes : function()
{
- this.aPlaceholderShapeSet[ aSlideNumberClassName ] = new PlaceholderShape( this, aSlideNumberClassName );
- this.aPlaceholderShapeSet[ aDateTimeClassName ] = new PlaceholderShape( this, aDateTimeClassName );
- this.aPlaceholderShapeSet[ aFooterClassName ] = new PlaceholderShape( this, aFooterClassName );
- this.aPlaceholderShapeSet[ aHeaderClassName ] = new PlaceholderShape( this, aHeaderClassName );
+ var sClassName;
+ var i = 0;
+ for( ; i < this.aBackgroundObjectSubGroupIdList.length; ++i )
+ {
+ sClassName = this.aBackgroundObjectSubGroupIdList[i];
+ if( isTextFieldByClassName( sClassName ) )
+ this.aPlaceholderShapeSet[ sClassName ] = new PlaceholderShape( this, sClassName );
+ }
}
}; // end MasterPage prototype
@@ -5708,22 +5780,25 @@ MasterPageView.prototype.createElement = function()
for( ; i < aBackgroundObjectSubGroupIdList.length; ++i )
{
sId = aBackgroundObjectSubGroupIdList[i];
- if( sId === aSlideNumberClassName )
+ if( sId.startsWith( aSlideNumberClassName ) )
{
// Slide Number Field
// The cloned element is appended directly to the field group element
// since there is no slide number field content shared between two slide
// (because the slide number of two slide is always different).
- if( aPlaceholderShapeSet[aSlideNumberClassName] &&
- aPlaceholderShapeSet[aSlideNumberClassName].isValid() &&
- this.aMetaSlide.nIsPageNumberVisible &&
+ var nIsPageNumberVisible = sId === aSlideNumberClassName ? this.aMetaSlide.nIsPageNumberVisible : true;
+ if( aPlaceholderShapeSet[sId] &&
+ aPlaceholderShapeSet[sId].isValid() &&
+ nIsPageNumberVisible &&
aTextFieldContentProviderSet[aSlideNumberClassName] )
{
- this.aSlideNumberFieldHandler =
- new SlideNumberFieldHandler( aPlaceholderShapeSet[aSlideNumberClassName],
- aTextFieldContentProviderSet[aSlideNumberClassName] );
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list