From 5ad541462aec381bb6a9d86db5ed20ecb6ddb496 Mon Sep 17 00:00:00 2001 From: Marco Cecchetti Date: Fri, 19 Feb 2021 16:04:07 +0100 Subject: 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 Reviewed-by: Andras Timar --- filter/source/svg/presentation_engine.js | 125 +++++++++++++++++++------------ filter/source/svg/svgexport.cxx | 6 +- 2 files changed, 83 insertions(+), 48 deletions(-) diff --git a/filter/source/svg/presentation_engine.js b/filter/source/svg/presentation_engine.js index 0d4fc767c4ad..24fd4f53d2a7 100644 --- a/filter/source/svg/presentation_engine.js +++ b/filter/source/svg/presentation_engine.js @@ -5575,68 +5575,99 @@ PlaceholderShape.prototype.isValid = function() */ PlaceholderShape.prototype.init = function() { - var aTextFieldElement = getElementByClassName( this.masterPage.backgroundObjects, this.className ); if( aTextFieldElement ) { - var aPlaceholderElement = getElementByClassName( aTextFieldElement, 'PlaceholderText' ); - if( aPlaceholderElement ) + var aTextElem = getElementByClassName( aTextFieldElement, 'SVGTextShape' ); + if( aTextElem ) { - // Each text field element has an invisible rectangle that can be - // regarded as the text field bounding box. - // We exploit such a feature and the exported text adjust attribute - // value in order to set up correctly the position and text - // adjustment for the placeholder element. - var aSVGRectElem = getElementByClassName( aTextFieldElement, 'BoundingBox' ); - if( aSVGRectElem ) + var aPlaceholderElement = getElementByClassName(aTextElem, 'PlaceholderText'); + if( aPlaceholderElement ) { - var aRect = new Rectangle( aSVGRectElem ); - var sTextAdjust = getOOOAttribute( aTextFieldElement, aOOOAttrTextAdjust ) || 'left'; - var sTextAnchor, sX; - if( sTextAdjust == 'left' ) - { - sTextAnchor = 'start'; - sX = String( aRect.left ); - } - else if( sTextAdjust == 'right' ) - { - sTextAnchor = 'end'; - sX = String( aRect.right ); - } - else if( sTextAdjust == 'center' ) + // SVG 1.1 does not support text wrapping wrt a rectangle. + // When a text shape contains a placeholder, setting up the position + // of each text line doesn't work since the position is computed + // before replacing the placeholder text. + // Anyway each text shape has an invisible rectangle that can be + // regarded as the text shape bounding box. + // We exploit such a feature and the exported text adjust attribute + // value in order to set up correctly the position and text + // adjustment for the text shape content. + // We assume that once the real value has been substituted to + // the placeholder the resulting content is no more than a single line. + // So we remove from elements used for setting up the + // position of text lines (class TextPosition) the 'x' and 'y' attribute. + // In the general case we would need to implement a function + // which is able to compute at which words the text shape content has + // to be wrapped. + var aSVGRectElem = getElementByClassName( aTextFieldElement, 'BoundingBox' ); + if( aSVGRectElem ) { - sTextAnchor = 'middle'; - var nMiddle = ( aRect.left + aRect.right ) / 2; - sX = String( parseInt( String( nMiddle ) ) ); + var aRect = new Rectangle( aSVGRectElem ); + var sTextAdjust = getOOOAttribute( aTextFieldElement, aOOOAttrTextAdjust ); + // the bbox of the text shape is indeed a bit larger, there is a bit of internal padding + var nMargin = 250; // 1000th mm + var sTextAnchor, sX; + if( sTextAdjust == 'left' ) + { + sTextAnchor = 'start'; + sX = String( Math.trunc( aRect.left + nMargin ) ); + } + else if( sTextAdjust == 'right' ) + { + sTextAnchor = 'end'; + sX = String( Math.trunc( aRect.right - nMargin ) ); + } + else if( sTextAdjust == 'center' ) + { + sTextAnchor = 'middle'; + var nMiddle = ( aRect.left + aRect.right ) / 2; + sX = String( parseInt( String( nMiddle ) ) ); + } + if( sTextAnchor ) + { + aTextElem.setAttribute( 'text-anchor', sTextAnchor ); + if( sX ) + aTextElem.setAttribute( 'x', sX ); + + var aTSpanElements = getElementsByClassName( aTextElem, 'TextPosition' ); + if( aTSpanElements ) + { + var i = 0; + for( ; i < aTSpanElements.length; ++i ) + { + var aTSpanElem = aTSpanElements[i]; + aTSpanElem.removeAttribute( 'x' ); + if( i !== 0 ) + aTSpanElem.removeAttribute( 'y' ); + } + } + } } - if( sTextAnchor ) - aPlaceholderElement.setAttribute( 'text-anchor', sTextAnchor ); - if( sX ) - aPlaceholderElement.setAttribute( 'x', sX ); - } - // date/time fields were not exported correctly when positioned chars are used - if( this.masterPage.metaSlide.theMetaDoc.bIsUsePositionedChars ) - { - // We remove all text lines but the first one used as placeholder. - var aTextLineGroupElem = aPlaceholderElement.parentNode.parentNode; - if( aTextLineGroupElem ) + // date/time fields were not exported correctly when positioned chars are used + if( this.masterPage.metaSlide.theMetaDoc.bIsUsePositionedChars ) { - // Just to be sure it is the element we are looking for. - var sFontFamilyAttr = aTextLineGroupElem.getAttribute( 'font-family' ); - if( sFontFamilyAttr ) + // We remove all text lines but the first one used as placeholder. + var aTextLineGroupElem = aPlaceholderElement.parentNode.parentNode; + if( aTextLineGroupElem ) { - var aChildSet = getElementChildren( aTextLineGroupElem ); - if( aChildSet.length > 1 ) - var i = 1; - for( ; i < aChildSet.length; ++i ) + // Just to be sure it is the element we are looking for. + var sFontFamilyAttr = aTextLineGroupElem.getAttribute( 'font-family' ); + if( sFontFamilyAttr ) { - aTextLineGroupElem.removeChild( aChildSet[i] ); + var aChildSet = getElementChildren( aTextLineGroupElem ); + if( aChildSet.length > 1 ) + var i = 1; + for( ; i < aChildSet.length; ++i ) + { + aTextLineGroupElem.removeChild( aChildSet[i] ); + } } } } + this.textElement = aPlaceholderElement; } - this.textElement = aPlaceholderElement; } this.element = aTextFieldElement; } diff --git a/filter/source/svg/svgexport.cxx b/filter/source/svg/svgexport.cxx index dec88345b43d..1f71feafe93a 100644 --- a/filter/source/svg/svgexport.cxx +++ b/filter/source/svg/svgexport.cxx @@ -2135,14 +2135,18 @@ bool SVGFilter::implExportShape( const Reference< css::drawing::XShape >& rxShap bool bIsPageNumber = ( aShapeClass == "Slide_Number" ); bool bIsFooter = ( aShapeClass == "Footer" ); bool bIsDateTime = ( aShapeClass == "Date/Time" ); - if( bIsPageNumber || bIsDateTime || bIsFooter ) + bool bTextField = bIsPageNumber || bIsFooter || bIsDateTime; + if( bTextField ) { // to notify to the SVGActionWriter::ImplWriteActions method // that we are dealing with a placeholder shape pElementId = &sPlaceholderTag; mpSVGExport->AddAttribute( XML_NAMESPACE_NONE, "visibility", "hidden" ); + } + if( bTextField || ( aShapeClass == "TextShape" ) ) + { sal_uInt16 nTextAdjust = sal_uInt16(ParagraphAdjust_LEFT); OUString sTextAdjust; xShapePropSet->getPropertyValue( "ParaAdjust" ) >>= nTextAdjust; -- cgit