[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.2' - 4 commits - filter/source sd/qa

Marco Cecchetti (via logerrit) logerrit at kemper.freedesktop.org
Wed Jan 20 06:49:00 UTC 2021


 filter/source/svg/presentation_engine.js        |  202 +++++++++++++++++++-----
 filter/source/svg/svgexport.cxx                 |   73 ++++++--
 filter/source/svg/svgwriter.cxx                 |   35 ++++
 filter/source/svg/svgwriter.hxx                 |    1 
 sd/qa/unit/SVGExportTests.cxx                   |   14 +
 sd/qa/unit/data/odp/slide-custom-background.odp |binary
 6 files changed, 264 insertions(+), 61 deletions(-)

New commits:
commit 10db92590353caeb515dd650a32eb09f352eea98
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: Tue Jan 19 15:16:08 2021 +0100

    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>

diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js
index 4dd876defba9..d0d0a0b60db7 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 = {};
@@ -4895,6 +4897,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.
@@ -5050,6 +5054,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();
@@ -5076,6 +5083,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();
@@ -5146,6 +5155,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 );
@@ -5322,6 +5348,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:
@@ -5384,6 +5438,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' );
@@ -5397,13 +5452,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 )
                     {
@@ -5447,10 +5515,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
@@ -5694,22 +5766,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] );
-                    this.aSlideNumberFieldHandler.update( this.aMetaSlide.nSlideNumber );
-                    this.aSlideNumberFieldHandler.appendTo( this.aBackgroundObjectsElement );
+                    var aSlideNumberFieldHandler =
+                        new SlideNumberFieldHandler( aPlaceholderShapeSet[sId],
+                            aTextFieldContentProviderSet[aSlideNumberClassName] );
+                    aSlideNumberFieldHandler.update( this.aMetaSlide.nSlideNumber );
+                    aSlideNumberFieldHandler.appendTo( this.aBackgroundObjectsElement );
+                    if ( sId === aSlideNumberClassName )
+                        this.aSlideNumberFieldHandler = aSlideNumberFieldHandler;
                 }
             }
             else if( sId === aDateTimeClassName )
@@ -5745,6 +5820,18 @@ MasterPageView.prototype.createElement = function()
                                                    aTextFieldHandlerSet, sMasterSlideId );
                 }
             }
+            else if( sId.startsWith( aDateClassName ) )
+            {
+                this.initTextFieldHandler( sId, aPlaceholderShapeSet,
+                                           aTextFieldContentProviderSet, aDefsElement,
+                                           aTextFieldHandlerSet, sMasterSlideId );
+            }
+            else if( sId.startsWith( aTimeClassName ) )
+            {
+                this.initTextFieldHandler( sId, aPlaceholderShapeSet,
+                                           aTextFieldContentProviderSet, aDefsElement,
+                                           aTextFieldHandlerSet, sMasterSlideId );
+            }
             else
             {
                 // init BackgroundObjectSubGroup elements
@@ -5766,23 +5853,25 @@ MasterPageView.prototype.createElement = function()
 };
 
 MasterPageView.prototype.initTextFieldHandler =
-    function( sClassName, aPlaceholderShapeSet, aTextFieldContentProviderSet,
+    function( sId, aPlaceholderShapeSet, aTextFieldContentProviderSet,
               aDefsElement, aTextFieldHandlerSet, sMasterSlideId )
 {
     var sRefId = null;
     var aTextFieldHandler = null;
-    var aPlaceholderShape = aPlaceholderShapeSet[sClassName];
+    var sClassName = sId.split('.')[0];
+    var aPlaceholderShape = aPlaceholderShapeSet[sId];
+    var aTextFieldContentProvider = aTextFieldContentProviderSet[sClassName];
     if( aPlaceholderShape  && aPlaceholderShape.isValid()
-        && aTextFieldContentProviderSet[sClassName] )
+        && aTextFieldContentProvider )
     {
-        var sTextFieldContentProviderId = aTextFieldContentProviderSet[sClassName].sId;
+        var sTextFieldContentProviderId = aTextFieldContentProvider.sId;
         // We create only one single TextFieldHandler object (and so one only
         // text field clone) per master slide and text content.
         if ( !aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ] )
         {
             aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ] =
                 new TextFieldHandler( aPlaceholderShape,
-                                      aTextFieldContentProviderSet[sClassName] );
+                                      aTextFieldContentProvider );
             aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ];
             aTextFieldHandler.update();
             aTextFieldHandler.appendTo( aDefsElement );
@@ -5794,7 +5883,7 @@ MasterPageView.prototype.initTextFieldHandler =
         sRefId = aTextFieldHandler.sId;
     }
     else if( aPlaceholderShape && aPlaceholderShape.element && aPlaceholderShape.element.firstElementChild
-        && !aPlaceholderShape.textElement && !aTextFieldContentProviderSet[sClassName] )
+        && !aPlaceholderShape.textElement && !aTextFieldContentProvider )
     {
         sRefId = aPlaceholderShape.element.firstElementChild.getAttribute('id');
     }
@@ -6013,10 +6102,16 @@ FixedTextProvider.prototype.update = function( aFixedTextField )
  *      The svg element that contains the date/time format for one or more
  *      master slide date/time field.
  */
-function CurrentDateTimeProvider( aTextFieldContentElement )
+function CurrentDateTimeProvider( aTextFieldContentElement, sDateTimeFormat )
 {
     CurrentDateTimeProvider.superclass.constructor.call( this, aTextFieldContentElement );
-    this.dateTimeFormat = getOOOAttribute( aTextFieldContentElement, aOOOAttrDateTimeFormat );
+    if( aTextFieldContentElement )
+        this.dateTimeFormat = getOOOAttribute( aTextFieldContentElement, aOOOAttrDateTimeFormat );
+    else
+    {
+        this.dateTimeFormat = sDateTimeFormat;
+        this.sId = 'DateTimeProvider.' + sDateTimeFormat;
+    }
 }
 extend( CurrentDateTimeProvider, TextFieldContentProvider );
 
@@ -6031,17 +6126,22 @@ extend( CurrentDateTimeProvider, TextFieldContentProvider );
  */
 CurrentDateTimeProvider.prototype.update = function( aDateTimeField )
 {
-    var sText = this.createDateTimeText( this.dateTimeFormat );
+    var sText = this.createDateTimeText();
     aDateTimeField.setTextContent( sText );
 };
 
 /*** private methods ***/
 
-CurrentDateTimeProvider.prototype.createDateTimeText = function( /*sDateTimeFormat*/ )
+CurrentDateTimeProvider.prototype.createDateTimeText = function()
 {
     // TODO handle date/time format
-    var aDate = new Date();
-    var sDate = aDate.toLocaleString();
+    var sDate;
+    if( this.dateTimeFormat === '<date>' )
+        sDate = new Date().toLocaleDateString();
+    else if( this.dateTimeFormat === '<time>' )
+        sDate = new Date().toLocaleTimeString();
+    else
+        sDate = new Date().toLocaleDateString();
     return sDate;
 };
 
diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx
index e4a7c4bac7a7..2e4c00b7fbf8 100644
--- a/filter/source/svg/svgwriter.cxx
+++ b/filter/source/svg/svgwriter.cxx
@@ -1091,9 +1091,9 @@ bool SVGTextWriter::nextTextPortion()
 {
     mrCurrentTextPortion.clear();
     mbIsURLField = false;
-    mbIsPlaceholderShape = false;
     if( mrTextPortionEnumeration.is() && mrTextPortionEnumeration->hasMoreElements() )
     {
+        mbIsPlaceholderShape = false;
 #if OSL_DEBUG_LEVEL > 0
         OUString sInfo;
 #endif
@@ -1111,6 +1111,7 @@ bool SVGTextWriter::nextTextPortion()
             }
 #endif
             msPageCount = "";
+            msDateTimeType = "";
             if( xPortionTextRange.is() )
             {
 #if OSL_DEBUG_LEVEL > 0
@@ -1157,6 +1158,31 @@ bool SVGTextWriter::nextTextPortion()
 #if OSL_DEBUG_LEVEL > 0
                         sInfo += "text field type: " + sFieldName + "; content: " + xTextField->getPresentation( /* show command: */ false ) + "; ";
 #endif
+                        // This case handle Date or Time text field inserted by the user
+                        // on both page/master page. It doesn't handle the standard Date/Time field.
+                        if( sFieldName == "DateTime" )
+                        {
+                            Reference<XPropertySet> xTextFieldPropSet(xTextField, UNO_QUERY);
+                            if( xTextFieldPropSet.is() )
+                            {
+                                Reference<XPropertySetInfo> xPropSetInfo = xTextFieldPropSet->getPropertySetInfo();
+                                if( xPropSetInfo.is() )
+                                {
+                                    // The standard Date/Time field has no property.
+                                    // Trying to get a property value on such field would cause a runtime exception.
+                                    // So the hasPropertyByName check is needed.
+                                    bool bIsFixed = true;
+                                    if( xPropSetInfo->hasPropertyByName("IsFixed") && ( ( xTextFieldPropSet->getPropertyValue( "IsFixed" ) ) >>= bIsFixed ) && !bIsFixed )
+                                    {
+                                        bool bIsDate;
+                                        if( xPropSetInfo->hasPropertyByName("IsDate") && ( ( xTextFieldPropSet->getPropertyValue( "IsDate" ) ) >>= bIsDate ) )
+                                        {
+                                            msDateTimeType = OUString::createFromAscii( bIsDate ? "<date>" : "<time>" );
+                                        }
+                                    }
+                                }
+                            }
+                        }
                         if( sFieldName == "DateTime" || sFieldName == "Header"
                                 || sFieldName == "Footer" || sFieldName == "PageNumber" )
                         {
@@ -1684,6 +1710,13 @@ void SVGTextWriter::implWriteTextPortion( const Point& rPos,
         SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS );
         mrExport.GetDocHandler()->characters( msPageCount );
     }
+    // This case handle Date or Time text field inserted by the user
+    // on both page/master page. It doesn't handle the standard Date/Time field.
+    else if ( !msDateTimeType.isEmpty() )
+    {
+        SvXMLElementExport aSVGTspanElem( mrExport, XML_NAMESPACE_NONE, aXMLElemTspan, mbIWS, mbIWS );
+        mrExport.GetDocHandler()->characters( msDateTimeType );
+    }
     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 e8396f16b886..541a39eaa843 100644
--- a/filter/source/svg/svgwriter.hxx
+++ b/filter/source/svg/svgwriter.hxx
@@ -249,6 +249,7 @@ class SVGTextWriter final
     OUString                                    msUrl;
     OUString                                    msHyperlinkIdList;
     OUString                                    msPageCount;
+    OUString                                    msDateTimeType;
     bool                                        mbIsPlaceholderShape;
     static const bool                           mbIWS = false;
     vcl::Font                                   maCurrentFont;
commit 8d29f3ab72ec91ab7fad55379a14afd41112532a
Author:     Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Tue Jan 12 15:29:44 2021 +0100
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Tue Jan 19 15:15:43 2021 +0100

    filter: svg: when date/time field is edited directly in mp, is not shown
    
    If a date/time or footer text field in the master page is editede
    directly instead of being filled through the header/footer dialog, is
    not displayed by the js engine.
    
    Change-Id: I4a8aa3a6b5e9931ea0b997d611ce54e8481dbbcb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109175
    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 3d82aac0eaab..4dd876defba9 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -5556,9 +5556,9 @@ PlaceholderShape.prototype.init = function()
                     }
                 }
             }
-            this.element = aTextFieldElement;
             this.textElement = aPlaceholderElement;
         }
+        this.element = aTextFieldElement;
     }
 };
 
@@ -5769,9 +5769,10 @@ MasterPageView.prototype.initTextFieldHandler =
     function( sClassName, aPlaceholderShapeSet, aTextFieldContentProviderSet,
               aDefsElement, aTextFieldHandlerSet, sMasterSlideId )
 {
+    var sRefId = null;
     var aTextFieldHandler = null;
-    if( aPlaceholderShapeSet[sClassName] &&
-        aPlaceholderShapeSet[sClassName].isValid()
+    var aPlaceholderShape = aPlaceholderShapeSet[sClassName];
+    if( aPlaceholderShape  && aPlaceholderShape.isValid()
         && aTextFieldContentProviderSet[sClassName] )
     {
         var sTextFieldContentProviderId = aTextFieldContentProviderSet[sClassName].sId;
@@ -5780,7 +5781,7 @@ MasterPageView.prototype.initTextFieldHandler =
         if ( !aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ] )
         {
             aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ] =
-                new TextFieldHandler( aPlaceholderShapeSet[sClassName],
+                new TextFieldHandler( aPlaceholderShape,
                                       aTextFieldContentProviderSet[sClassName] );
             aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ];
             aTextFieldHandler.update();
@@ -5790,13 +5791,22 @@ MasterPageView.prototype.initTextFieldHandler =
         {
             aTextFieldHandler = aTextFieldHandlerSet[ sMasterSlideId ][ sTextFieldContentProviderId ];
         }
+        sRefId = aTextFieldHandler.sId;
+    }
+    else if( aPlaceholderShape && aPlaceholderShape.element && aPlaceholderShape.element.firstElementChild
+        && !aPlaceholderShape.textElement && !aTextFieldContentProviderSet[sClassName] )
+    {
+        sRefId = aPlaceholderShape.element.firstElementChild.getAttribute('id');
+    }
 
+    if( sRefId )
+    {
         // We create a <use> element referring to the cloned text field and
         // append it to the field group element.
-        var aTextFieldElement = document.createElementNS( NSS['svg'], 'use' );
-        aTextFieldElement.setAttribute( 'class', sClassName );
-        setNSAttribute( 'xlink', aTextFieldElement,
-                        'href', '#' + aTextFieldHandler.sId );
+        var aTextFieldElement = document.createElementNS(NSS['svg'], 'use');
+        aTextFieldElement.setAttribute('class', sClassName);
+        setNSAttribute('xlink', aTextFieldElement,
+            'href', '#' + sRefId);
         // node linking
         this.aBackgroundObjectsElement.appendChild( aTextFieldElement );
     }
commit c54784beade263fc8cb1a0b240dc3974887f22a3
Author:     Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Tue Jan 12 17:55:08 2021 +0100
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Tue Jan 19 15:15:20 2021 +0100

    filter: svg: slide custom background unit test
    
    Change-Id: I29990218bfa6095c368ed36ebc9cca909d2136fb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109189
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>

diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 8c55bf45d48b..57e5f3fcfc0a 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -23,6 +23,7 @@
 #define SVG_G *[name()='g']
 #define SVG_TEXT *[name()='text']
 #define SVG_TSPAN *[name()='tspan']
+#define SVG_DEFS *[name()='defs']
 
 using namespace css;
 
@@ -124,9 +125,20 @@ public:
                             1);
     }
 
+    void testSVGExporSlidetCustomBackground()
+    {
+        executeExport("slide-custom-background.odp");
+
+        xmlDocPtr svgDoc = parseXml(maTempFile);
+        CPPUNIT_ASSERT(svgDoc);
+
+        assertXPath(svgDoc, MAKE_PATH_STRING( /SVG_SVG/SVG_G[2]/SVG_G/SVG_G/SVG_G/SVG_G/SVG_DEFS ), "class", "SlideBackground");
+    }
+
     CPPUNIT_TEST_SUITE(SdSVGFilterTest);
     CPPUNIT_TEST(testSVGExportTextDecorations);
     CPPUNIT_TEST(testSVGExportJavascriptURL);
+    CPPUNIT_TEST(testSVGExporSlidetCustomBackground);
     CPPUNIT_TEST_SUITE_END();
 };
 
diff --git a/sd/qa/unit/data/odp/slide-custom-background.odp b/sd/qa/unit/data/odp/slide-custom-background.odp
new file mode 100644
index 000000000000..df07c6f34579
Binary files /dev/null and b/sd/qa/unit/data/odp/slide-custom-background.odp differ
commit 92e9de9ab53ae717693c7097c9390d26b9564c1b
Author:     Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Mon Jan 11 10:28:57 2021 +0100
Commit:     Andras Timar <andras.timar at collabora.com>
CommitDate: Tue Jan 19 15:14:45 2021 +0100

    filter: svg: slide with a custom background are not exported correctly
    
    When a slide has a custom background, the background overlaps any
    master page object: text fields, shapes, ...
    
    Change-Id: Icc410617760502fa4092cfe248155b3e20906abb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109089
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    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 150510446324..3d82aac0eaab 100644
--- a/filter/source/svg/presentation_engine.js
+++ b/filter/source/svg/presentation_engine.js
@@ -359,7 +359,7 @@ function uniqueArray(src, key, sort) {
  * @returns {String|Undefined} prefixed
  */
 function prefixed(obj, property) {
-    // tml: Have to check for obj being undefined 
+    // tml: Have to check for obj being undefined
     if (obj === undefined) {
         return undefined;
     }
@@ -4436,6 +4436,7 @@ var aOOOAttrSlide = 'slide';
 var aOOOAttrMaster = 'master';
 var aOOOAttrSlideDuration = 'slide-duration';
 var aOOOAttrHasTransition = 'has-transition';
+var aOOOAttrHasCustomBackground = 'has-custom-background';
 var aOOOAttrBackgroundVisibility = 'background-visibility';
 var aOOOAttrMasterObjectsVisibility = 'master-objects-visibility';
 var aOOOAttrPageNumberVisibility = 'page-number-visibility';
@@ -5042,10 +5043,20 @@ function MetaSlide( sMetaSlideId, aMetaDoc )
     assert( this.pageElement,
             'MetaSlide: page element <' + this.slideId + '> not found.' );
 
+    // The slide custom background element and its id attribute.
+    this.backgroundElement = getElementByClassName( this.pageElement, 'Background' );
+    if( this.backgroundElement )
+    {
+        this.backgroundId = this.backgroundElement.getAttribute( 'id' );
+    }
+
     // We initialize the MasterPage object that provides direct access to
     // the target master page element.
     this.masterPage = this.initMasterPage();
 
+    // We check if the slide has a custom background which overrides the one of the targeted master page
+    this.bHasCustomBackground = this.initHasCustomBackground();
+
     // We initialize visibility properties of the target master page elements.
     this.nAreMasterObjectsVisible     = this.initVisibilityProperty( aOOOAttrMasterObjectsVisibility,  VISIBLE );
     this.nIsBackgroundVisible         = this.initVisibilityProperty( aOOOAttrBackgroundVisibility,     VISIBLE );
@@ -5167,6 +5178,12 @@ initHasTransition : function()
     return ( sHasTransition === 'true' );
 },
 
+initHasCustomBackground : function()
+{
+    var sHasCustomBackground = this.element.getAttributeNS( NSS['ooo'], aOOOAttrHasCustomBackground );
+    return ( sHasCustomBackground === 'true' );
+},
+
 initVisibilityProperty : function( aVisibilityAttribute, nDefaultValue )
 {
     var nVisibility = nDefaultValue;
@@ -5646,10 +5663,11 @@ MasterPageView.prototype.createElement = function()
     // init the Background element
     if( this.aMetaSlide.nIsBackgroundVisible )
     {
+        var nBackgroundId = this.aMetaSlide.bHasCustomBackground ? this.aMetaSlide.backgroundId : this.aMasterPage.backgroundId;
         this.aBackgroundElement = theDocument.createElementNS( NSS['svg'], 'use' );
         this.aBackgroundElement.setAttribute( 'class', 'Background' );
         setNSAttribute( 'xlink', this.aBackgroundElement,
-                        'href', '#' + this.aMasterPage.backgroundId );
+                        'href', '#' + nBackgroundId );
 
         // node linking
         aMasterPageViewElement.appendChild( this.aBackgroundElement );
diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx
index ac16f7e754f6..938d833c1275 100644
--- a/filter/source/svg/svgexport.cxx
+++ b/filter/source/svg/svgexport.cxx
@@ -33,6 +33,8 @@
 #include <com/sun/star/xml/sax/Writer.hpp>
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/drawing/ShapeCollection.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/drawing/RectanglePoint.hpp>
 
 #include <rtl/bootstrap.hxx>
 #include <svtools/miscopt.hxx>
@@ -85,6 +87,7 @@ static const char    aOOOElemTextField[] = NSPREFIX "text_field";
 // ooo xml attributes for meta_slide
 static const char    aOOOAttrSlide[] = NSPREFIX "slide";
 static const char    aOOOAttrMaster[] = NSPREFIX "master";
+static const char    aOOOAttrHasCustomBackground[] = NSPREFIX "has-custom-background";
 static const char    aOOOAttrBackgroundVisibility[] = NSPREFIX "background-visibility";
 static const char    aOOOAttrMasterObjectsVisibility[] = NSPREFIX "master-objects-visibility";
 static const char    aOOOAttrSlideDuration[] = NSPREFIX "slide-duration";
@@ -1155,6 +1158,19 @@ void SVGFilter::implGenerateMetaData()
                         VariableDateTimeField         aVariableDateTimeField;
                         FooterField                   aFooterField;
 
+                        // check if the slide has a custom background wich overlaps the matser page background
+                        Reference< XPropertySet > xBackground;
+                        xPropSet->getPropertyValue( "Background" ) >>= xBackground;
+                        if( xBackground.is() )
+                        {
+                            drawing::FillStyle aFillStyle;
+                            bool assigned = ( xBackground->getPropertyValue( "FillStyle" ) >>= aFillStyle );
+                            // has a custom background ?
+                            if( assigned && aFillStyle != drawing::FillStyle_NONE )
+                                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, aOOOAttrHasCustomBackground, "true" );
+
+                        }
+
                         xPropSet->getPropertyValue( "IsBackgroundVisible" )  >>= bBackgroundVisibility;
                         // in case the attribute is set to its default value it is not appended to the meta-slide element
                         if( !bBackgroundVisibility ) // visibility default value: 'visible'
@@ -1760,35 +1776,48 @@ bool SVGFilter::implExportPage( const OUString & sPageId,
             const GDIMetaFile& rMtf = (*mpObjects)[ rxPage ].GetRepresentation();
             if( rMtf.GetActionSize() )
             {
-                // background id = "bg-" + page id
+                // If this is not a master page wrap the slide custom background
+                // by a <defs> element.
+                // Slide custom background, if any, is referenced at a different position
+                // in order to not overlap background objects.
+                std::unique_ptr<SvXMLElementExport> xDefsExp;
+                if (!bMaster) // insert the <defs> open tag related to the slide background
+                {
+                    mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class",  "SlideBackground" );
+                    xDefsExp.reset( new SvXMLElementExport( *mpSVGExport, XML_NAMESPACE_NONE, "defs", true, true ) );
+                }
+                {
+                    // background id = "bg-" + page id
                 OUString sBackgroundId = "bg-";
                 sBackgroundId += sPageId;
-                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sBackgroundId );
-
-                // At present (LibreOffice 3.4.0) the 'IsBackgroundVisible' property is not handled
-                // by Impress; anyway we handle this property as referring only to the visibility
-                // of the master page background. So if a slide has its own background object,
-                // the visibility of such a background object is always inherited from the visibility
-                // of the parent slide regardless of the value of the 'IsBackgroundVisible' property.
-                // This means that we need to set up the visibility attribute only for the background
-                // element of a master page.
-                if( !mbPresentation && bMaster )
-                {
-                    if( !mVisiblePagePropSet.bIsBackgroundVisible )
+                    mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "id", sBackgroundId );
+
+                    // At present (LibreOffice 3.4.0) the 'IsBackgroundVisible' property is not handled
+                    // by Impress; anyway we handle this property as referring only to the visibility
+                    // of the master page background. So if a slide has its own background object,
+                    // the visibility of such a background object is always inherited from the visibility
+                    // of the parent slide regardless of the value of the 'IsBackgroundVisible' property.
+                    // This means that we need to set up the visibility attribute only for the background
+                    // element of a master page.
+                    if( !mbPresentation && bMaster )
                     {
-                        mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "visibility", "hidden" );
+                        if( !mVisiblePagePropSet.bIsBackgroundVisible )
+                        {
+                            mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "visibility", "hidden" );
+                        }
                     }
-                }
 
-                mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class",  "Background" );
+                    mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "class",  "Background" );
+
+                    // insert the <g> open tag related to the Background
+                    SvXMLElementExport aExp2( *mpSVGExport, XML_NAMESPACE_NONE, "g", true, true );
 
-                // insert the <g> open tag related to the Background
-                SvXMLElementExport aExp2( *mpSVGExport, XML_NAMESPACE_NONE, "g", true, true );
+                    // append all elements that make up the Background
+                    const Point aNullPt;
+                    mpSVGWriter->WriteMetaFile( aNullPt, rMtf.GetPrefSize(), rMtf, SVGWRITER_WRITE_FILL );
+                } // insert the </g> closing tag related to the Background
 
-                // append all elements that make up the Background
-                const Point aNullPt;
-                mpSVGWriter->WriteMetaFile( aNullPt, rMtf.GetPrefSize(), rMtf, SVGWRITER_WRITE_FILL );
-            }   // insert the </g> closing tag related to the Background
+            } // insert the </defs> closing tag related to the slide background
         }
 
         // In case we are dealing with a master page we need to group all its shapes
diff --git a/sd/qa/unit/SVGExportTests.cxx b/sd/qa/unit/SVGExportTests.cxx
index 400d604e786c..8c55bf45d48b 100644
--- a/sd/qa/unit/SVGExportTests.cxx
+++ b/sd/qa/unit/SVGExportTests.cxx
@@ -120,7 +120,7 @@ public:
         // There should be only one child (no link to javascript url)
         assertXPathChildren(svgDoc,
                             MAKE_PATH_STRING(/ SVG_SVG / SVG_G[2] / SVG_G / SVG_G / SVG_G / SVG_G
-                                             / SVG_G[4] / SVG_G),
+                                             / SVG_G[3] / SVG_G),
                             1);
     }
 


More information about the Libreoffice-commits mailing list