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 <[email protected]> AuthorDate: Sun Jan 17 23:36:53 2021 +0100 Commit: Andras Timar <[email protected]> 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 <[email protected]> Reviewed-by: Ashod Nakashian <[email protected]> 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 <[email protected]> AuthorDate: Tue Jan 12 15:29:44 2021 +0100 Commit: Andras Timar <[email protected]> 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 <[email protected]> Reviewed-by: Andras Timar <[email protected]> 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 <[email protected]> AuthorDate: Tue Jan 12 17:55:08 2021 +0100 Commit: Andras Timar <[email protected]> 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 <[email protected]> Reviewed-by: Marco Cecchetti <[email protected]> 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 <[email protected]> AuthorDate: Mon Jan 11 10:28:57 2021 +0100 Commit: Andras Timar <[email protected]> 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 <[email protected]> Reviewed-by: Marco Cecchetti <[email protected]> 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); } _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
