diff options
author | Mike Kaganski <mike.kaganski@collabora.com> | 2018-03-14 10:18:15 +0300 |
---|---|---|
committer | Mike Kaganski <mike.kaganski@collabora.com> | 2018-03-14 15:32:54 +0100 |
commit | fb959e581c900b392efd0bb329b7cf30c8ed56a5 (patch) | |
tree | 40adc1b075375416b3d1b1a9b59b8f95c3663cad /writerfilter | |
parent | aab440c768b64b9d2ffa72223b6d843a84c2d061 (diff) |
tdf#112118: DOCX: properly import/export border distance
https://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
discusses implementation differences between ODF model and MS formats
wrt dealing with page margins and distances to borders.
This patch corrects import from DOCX, so that the border distance and
width doesn't add to the margin size imported from file anymore. It
takes care to preserve size from page edge to text (the most important
size that affects document layout). When borders go outside of range
valid for ODF, the margin is set to keep text area intact, and the
border is placed as close to intended position as possible.
Export code now also properly handles border width. Also, an improved
heuristic implemented to better export cases unsupported by Word, so
that the result would look closer to ODF original. We still write
correct sizes to OOXML, so that when reopened by LO, the borders will
be in correct places; but as Word cannot handle sizes more than 31 pt,
it will show borders shifted.
This prevents from adding border widths and distances to page margins
at each opening of DOCX, saving back the changed value, increasing
the margins each time.
Change-Id: Ia978ab119dd661949d6c321aea91397f28d205b0
Reviewed-on: https://gerrit.libreoffice.org/51267
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'writerfilter')
-rw-r--r-- | writerfilter/source/dmapper/PageBordersHandler.cxx | 17 | ||||
-rw-r--r-- | writerfilter/source/dmapper/PageBordersHandler.hxx | 8 | ||||
-rw-r--r-- | writerfilter/source/dmapper/PropertyMap.cxx | 75 | ||||
-rw-r--r-- | writerfilter/source/dmapper/PropertyMap.hxx | 23 |
4 files changed, 78 insertions, 45 deletions
diff --git a/writerfilter/source/dmapper/PageBordersHandler.cxx b/writerfilter/source/dmapper/PageBordersHandler.cxx index fc1867be3c80..2bebf0d5af6b 100644 --- a/writerfilter/source/dmapper/PageBordersHandler.cxx +++ b/writerfilter/source/dmapper/PageBordersHandler.cxx @@ -37,8 +37,8 @@ PgBorder::~PgBorder( ) PageBordersHandler::PageBordersHandler( ) : LoggedProperties("PageBordersHandler"), -m_nDisplay( 0 ), -m_nOffset( 0 ) +m_eBorderApply(SectionPropertyMap::BorderApply::ToAllInSection), +m_eOffsetFrom(SectionPropertyMap::BorderOffsetFrom::Text) { } @@ -57,13 +57,13 @@ void PageBordersHandler::lcl_attribute( Id eName, Value& rVal ) { default: case NS_ooxml::LN_Value_doc_ST_PageBorderDisplay_allPages: - m_nDisplay = 0; + m_eBorderApply = SectionPropertyMap::BorderApply::ToAllInSection; break; case NS_ooxml::LN_Value_doc_ST_PageBorderDisplay_firstPage: - m_nDisplay = 1; + m_eBorderApply = SectionPropertyMap::BorderApply::ToFirstPageInSection; break; case NS_ooxml::LN_Value_doc_ST_PageBorderDisplay_notFirstPage: - m_nDisplay = 2; + m_eBorderApply = SectionPropertyMap::BorderApply::ToAllButFirstInSection; break; } } @@ -74,10 +74,10 @@ void PageBordersHandler::lcl_attribute( Id eName, Value& rVal ) { default: case NS_ooxml::LN_Value_doc_ST_PageBorderOffset_page: - m_nOffset = 1; + m_eOffsetFrom = SectionPropertyMap::BorderOffsetFrom::Edge; break; case NS_ooxml::LN_Value_doc_ST_PageBorderOffset_text: - m_nOffset = 0; + m_eOffsetFrom = SectionPropertyMap::BorderOffsetFrom::Text; break; } } @@ -137,7 +137,8 @@ void PageBordersHandler::SetBorders( SectionPropertyMap* pSectContext ) { pSectContext->SetBorder( rBorder.m_ePos, rBorder.m_nDistance, rBorder.m_rLine, rBorder.m_bShadow ); } - pSectContext->SetBorderParams(GetDisplayOffset()); + pSectContext->SetBorderApply(m_eBorderApply); + pSectContext->SetBorderOffsetFrom(m_eOffsetFrom); } } } diff --git a/writerfilter/source/dmapper/PageBordersHandler.hxx b/writerfilter/source/dmapper/PageBordersHandler.hxx index 6ced273953af..7ce2e15eba79 100644 --- a/writerfilter/source/dmapper/PageBordersHandler.hxx +++ b/writerfilter/source/dmapper/PageBordersHandler.hxx @@ -50,8 +50,8 @@ class PageBordersHandler : public LoggedProperties private: // See implementation of SectionPropertyMap::ApplyBorderToPageStyles - sal_Int32 m_nDisplay; - sal_Int32 m_nOffset; + SectionPropertyMap::BorderApply m_eBorderApply; + SectionPropertyMap::BorderOffsetFrom m_eOffsetFrom; std::vector<PgBorder> m_aBorders; // Properties @@ -62,10 +62,6 @@ public: PageBordersHandler( ); virtual ~PageBordersHandler( ) override; - sal_Int32 GetDisplayOffset( ) - { - return ( m_nOffset << 5 ) + m_nDisplay; - }; void SetBorders( SectionPropertyMap* pSectContext ); }; diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx index d20adca936fa..b58b6e20fb5f 100644 --- a/writerfilter/source/dmapper/PropertyMap.cxx +++ b/writerfilter/source/dmapper/PropertyMap.cxx @@ -365,7 +365,8 @@ void PropertyMap::printProperties() SectionPropertyMap::SectionPropertyMap( bool bIsFirstSection ) : m_bIsFirstSection( bIsFirstSection ) - , m_nBorderParams( 0 ) + , m_eBorderApply( BorderApply::ToAllInSection ) + , m_eBorderOffsetFrom( BorderOffsetFrom::Text ) , m_bTitlePage( false ) , m_nColumnCount( 0 ) , m_nColumnDistance( 1249 ) @@ -527,7 +528,7 @@ void SectionPropertyMap::SetBorder( BorderPosition ePos, sal_Int32 nLineDistance void SectionPropertyMap::ApplyBorderToPageStyles( const uno::Reference< container::XNameContainer >& xPageStyles, const uno::Reference < lang::XMultiServiceFactory >& xTextFactory, - sal_Int32 nValue ) + BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom ) { /* page border applies to: @@ -544,25 +545,24 @@ void SectionPropertyMap::ApplyBorderToPageStyles( const uno::Reference< containe */ uno::Reference< beans::XPropertySet > xFirst; uno::Reference< beans::XPropertySet > xSecond; - sal_Int32 nOffsetFrom = (nValue & 0x00E0) >> 5; // todo: negative spacing (from ww8par6.cxx) - switch ( nValue & 0x07 ) + switch ( eBorderApply ) { - case 0: // all styles + case BorderApply::ToAllInSection: // all styles if ( !m_sFollowPageStyleName.isEmpty() ) xFirst = GetPageStyle( xPageStyles, xTextFactory, false ); if ( !m_sFirstPageStyleName.isEmpty() ) xSecond = GetPageStyle( xPageStyles, xTextFactory, true ); break; - case 1: // first page + case BorderApply::ToFirstPageInSection: // first page if ( !m_sFirstPageStyleName.isEmpty() ) xFirst = GetPageStyle( xPageStyles, xTextFactory, true ); break; - case 2: // left and right + case BorderApply::ToAllButFirstInSection: // left and right if ( !m_sFollowPageStyleName.isEmpty() ) xFirst = GetPageStyle( xPageStyles, xTextFactory, false ); break; - case 3: // whole document? + case BorderApply::ToWholeDocument: // whole document? // todo: how to apply a border to the whole document - find all sections or access all page styles? default: return; @@ -610,10 +610,10 @@ void SectionPropertyMap::ApplyBorderToPageStyles( const uno::Reference< containe nLineWidth = m_oBorderLines[nBorder]->LineWidth; if ( xFirst.is() ) SetBorderDistance( xFirst, aMarginIds[nBorder], aBorderDistanceIds[nBorder], - m_nBorderDistances[nBorder], nOffsetFrom, nLineWidth ); + m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth ); if ( xSecond.is() ) SetBorderDistance( xSecond, aMarginIds[nBorder], aBorderDistanceIds[nBorder], - m_nBorderDistances[nBorder], nOffsetFrom, nLineWidth ); + m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth ); } } @@ -644,26 +644,47 @@ void SectionPropertyMap::SetBorderDistance( const uno::Reference< beans::XProper PropertyIds eMarginId, PropertyIds eDistId, sal_Int32 nDistance, - sal_Int32 nOffsetFrom, + BorderOffsetFrom eOffsetFrom, sal_uInt32 nLineWidth ) { - sal_Int32 nDist = nDistance; - if ( nOffsetFrom == 1 ) // From page - { - const OUString sMarginName = getPropertyName( eMarginId ); - uno::Any aMargin = xStyle->getPropertyValue( sMarginName ); - sal_Int32 nMargin = 0; - aMargin >>= nMargin; - - // Change the margins with the border distance - xStyle->setPropertyValue( sMarginName, uno::makeAny( nDistance ) ); + // See https://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder - // Set the distance to ( Margin - distance - nLineWidth ) - nDist = nMargin - nDistance - nLineWidth; - } + if (!xStyle.is()) + return; + const OUString sMarginName = getPropertyName( eMarginId ); const OUString sBorderDistanceName = getPropertyName( eDistId ); - if ( xStyle.is() ) - xStyle->setPropertyValue( sBorderDistanceName, uno::makeAny( nDist ) ); + uno::Any aMargin = xStyle->getPropertyValue( sMarginName ); + sal_Int32 nMargin = 0; + aMargin >>= nMargin; + sal_Int32 nNewMargin = nMargin; + sal_Int32 nNewDist = nDistance; + + switch (eOffsetFrom) + { + case BorderOffsetFrom::Text: + nNewMargin -= nDistance + nLineWidth; + break; + case BorderOffsetFrom::Edge: + nNewMargin = nDistance; + nNewDist = nMargin - nDistance - nLineWidth; + break; + } + // Ensure corrent distance from page edge to text in cases not supported by us: + // when border is outside entire page area (eOffsetFrom == Text && nDistance > nMargin), + // and when border is inside page body area (eOffsetFrom == Edge && nDistance > nMargin) + if (nNewMargin < 0) + { + nNewMargin = 0; + nNewDist = std::max<sal_Int32>(nMargin - nLineWidth, 0); + } + else if (nNewDist < 0) + { + nNewMargin = std::max<sal_Int32>(nMargin - nLineWidth, 0); + nNewDist = 0; + } + // Change the margins with the border distance + xStyle->setPropertyValue( sMarginName, uno::makeAny( nNewMargin ) ); + xStyle->setPropertyValue( sBorderDistanceName, uno::makeAny( nNewDist ) ); } void SectionPropertyMap::DontBalanceTextColumns() @@ -1438,7 +1459,7 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl ) getPropertyName( PROP_TEXT_COLUMNS ), uno::makeAny( xColumns ) ); } - ApplyBorderToPageStyles( rDM_Impl.GetPageStyles(), rDM_Impl.GetTextFactory(), m_nBorderParams ); + ApplyBorderToPageStyles( rDM_Impl.GetPageStyles(), rDM_Impl.GetTextFactory(), m_eBorderApply, m_eBorderOffsetFrom ); try { diff --git a/writerfilter/source/dmapper/PropertyMap.hxx b/writerfilter/source/dmapper/PropertyMap.hxx index c373522d2792..07e1707a8a5a 100644 --- a/writerfilter/source/dmapper/PropertyMap.hxx +++ b/writerfilter/source/dmapper/PropertyMap.hxx @@ -181,6 +181,19 @@ typedef std::shared_ptr< PropertyMap > PropertyMapPtr; class SectionPropertyMap : public PropertyMap { +public: + enum class BorderApply + { + ToAllInSection = 0, + ToFirstPageInSection = 1, + ToAllButFirstInSection = 2, + ToWholeDocument = 3, + }; + enum class BorderOffsetFrom + { + Text = 0, + Edge = 1, + }; private: #ifdef DEBUG_WRITERFILTER sal_Int32 m_nDebugSectionNumber; @@ -199,7 +212,8 @@ private: boost::optional< css::table::BorderLine2 > m_oBorderLines[4]; sal_Int32 m_nBorderDistances[4]; - sal_Int32 m_nBorderParams; + BorderApply m_eBorderApply; + BorderOffsetFrom m_eBorderOffsetFrom; bool m_bBorderShadows[4]; bool m_bTitlePage; @@ -275,7 +289,7 @@ private: PropertyIds eMarginId, PropertyIds eDistId, sal_Int32 nDistance, - sal_Int32 nOffsetFrom, + BorderOffsetFrom eOffsetFrom, sal_uInt32 nLineWidth ); // Determines if conversion of a given floating table is wanted or not. @@ -315,7 +329,8 @@ public: void InheritOrFinalizePageStyles( DomainMapper_Impl& rDM_Impl ); void SetBorder( BorderPosition ePos, sal_Int32 nLineDistance, const css::table::BorderLine2& rBorderLine, bool bShadow ); - void SetBorderParams( sal_Int32 nSet ) { m_nBorderParams = nSet; } + void SetBorderApply( BorderApply nSet ) { m_eBorderApply = nSet; } + void SetBorderOffsetFrom( BorderOffsetFrom nSet ) { m_eBorderOffsetFrom = nSet; } void SetColumnCount( sal_Int16 nCount ) { m_nColumnCount = nCount; } sal_Int16 ColumnCount() const { return m_nColumnCount; } @@ -358,7 +373,7 @@ public: // determine which style gets the borders void ApplyBorderToPageStyles( const css::uno::Reference< css::container::XNameContainer >& xStyles, const css::uno::Reference< css::lang::XMultiServiceFactory >& xTextFactory, - sal_Int32 nValue ); + BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom ); void CloseSectionGroup( DomainMapper_Impl& rDM_Impl ); // Handling of margins, header and footer for any kind of sections breaks. |