[Libreoffice-commits] core.git: editeng/source include/editeng sw/qa sw/source writerfilter/source

Mike Kaganski mike.kaganski at collabora.com
Fri Mar 16 03:55:17 UTC 2018


 editeng/source/items/frmitems.cxx            |  125 +++++++++++++++++++++++++
 include/editeng/boxitem.hxx                  |   30 ++++++
 sw/qa/extras/ooxmlexport/data/tdf112118.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport11.cxx   |    2 
 sw/qa/extras/ww8export/data/tdf112118.doc    |binary
 sw/qa/extras/ww8export/ww8export2.cxx        |   31 ++++++
 sw/source/filter/ww8/docxattributeoutput.cxx |  131 +++------------------------
 sw/source/filter/ww8/docxattributeoutput.hxx |   21 ----
 sw/source/filter/ww8/wrtw8sty.cxx            |    4 
 sw/source/filter/ww8/ww8atr.cxx              |   40 +++++---
 sw/source/filter/ww8/ww8attributeoutput.hxx  |    6 +
 sw/source/filter/ww8/ww8par6.cxx             |   81 ++++------------
 writerfilter/source/dmapper/PropertyMap.cxx  |   36 +------
 13 files changed, 273 insertions(+), 234 deletions(-)

New commits:
commit 8805e10f5887df66edfd0a2fa4b70e87f0c74700
Author: Mike Kaganski <mike.kaganski at collabora.com>
Date:   Thu Mar 15 20:48:53 2018 +0300

    tdf#112118: DOC: properly import/export border distance
    
    DOCX part was done in fb959e581c900b392efd0bb329b7cf30c8ed56a5.
    
    This commit fixes DOC part. Line width wasn't taken into account on
    import; and export was done only with "from text" distance, which
    gave poor interoperability with Word, where the borders were close
    to page edge.
    
    The common code is moved to editeng/source/items/frmitems.cxx and
    include/editeng/boxitem.hxx.
    
    Change-Id: I3d1d1312cb9dc9a9e00d9847ec11234cd787df60
    Reviewed-on: https://gerrit.libreoffice.org/51366
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>

diff --git a/editeng/source/items/frmitems.cxx b/editeng/source/items/frmitems.cxx
index 85790110bc78..ea1d15126ff3 100644
--- a/editeng/source/items/frmitems.cxx
+++ b/editeng/source/items/frmitems.cxx
@@ -2592,6 +2592,131 @@ bool SvxBoxInfoItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
     return true;
 }
 
+
+namespace editeng
+{
+
+void BorderDistanceFromWord(bool bFromEdge, sal_Int32& nMargin, sal_Int32& nBorderDistance,
+    sal_Int32 nBorderWidth)
+{
+    // See https://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
+
+    sal_Int32 nNewMargin = nMargin;
+    sal_Int32 nNewBorderDistance = nBorderDistance;
+
+    if (bFromEdge)
+    {
+        nNewMargin = nBorderDistance;
+        nNewBorderDistance = nMargin - nBorderDistance - nBorderWidth;
+    }
+    else
+    {
+        nNewMargin -= nBorderDistance + nBorderWidth;
+    }
+
+    // Ensure correct distance from page edge to text in cases not supported by us:
+    // when border is outside entire page area (!bFromEdge && BorderDistance > Margin),
+    // and when border is inside page body area (bFromEdge && BorderDistance > Margin)
+    if (nNewMargin < 0)
+    {
+        nNewMargin = 0;
+        nNewBorderDistance = std::max<sal_Int32>(nMargin - nBorderWidth, 0);
+    }
+    else if (nNewBorderDistance < 0)
+    {
+        nNewMargin = std::max<sal_Int32>(nMargin - nBorderWidth, 0);
+        nNewBorderDistance = 0;
+    }
+
+    nMargin = nNewMargin;
+    nBorderDistance = nNewBorderDistance;
+}
+
+// Heuristics to decide if we need to use "from edge" offset of borders
+//
+// There are two cases when we can safely use "from text" or "from edge" offset without distorting
+// border position (modulo rounding errors):
+// 1. When distance of all borders from text is no greater than 31 pt, we use "from text"
+// 2. Otherwise, if distance of all borders from edge is no greater than 31 pt, we use "from edge"
+// In all other cases, the position of borders would be distirted on export, because Word doesn't
+// support the offset of >31 pts (https://msdn.microsoft.com/en-us/library/ff533820), and we need
+// to decide which type of offset would provide less wrong result (i.e., the result would look
+// closer to original). Here, we just check sum of distances from text to borders, and if it is
+// less than sum of distances from borders to edges. The alternative would be to compare total areas
+// between text-and-borders and between borders-and-edges (taking into account different lengths of
+// borders, and visual impact of that).
+void BorderDistancesToWord(const SvxBoxItem& rBox, const WordPageMargins& rMargins,
+    WordBorderDistances& rDistances)
+{
+    // Use signed sal_Int32 that can hold sal_uInt16, to prevent overflow at substraction below
+    const sal_Int32 nT = rBox.GetDistance(SvxBoxItemLine::TOP);
+    const sal_Int32 nL = rBox.GetDistance(SvxBoxItemLine::LEFT);
+    const sal_Int32 nB = rBox.GetDistance(SvxBoxItemLine::BOTTOM);
+    const sal_Int32 nR = rBox.GetDistance(SvxBoxItemLine::RIGHT);
+
+    // Only take into account existing borders
+    const SvxBorderLine* pLnT = rBox.GetLine(SvxBoxItemLine::TOP);
+    const SvxBorderLine* pLnL = rBox.GetLine(SvxBoxItemLine::LEFT);
+    const SvxBorderLine* pLnB = rBox.GetLine(SvxBoxItemLine::BOTTOM);
+    const SvxBorderLine* pLnR = rBox.GetLine(SvxBoxItemLine::RIGHT);
+
+    // We need to take border widths into account
+    const long nWidthT = pLnT ? pLnT->GetWidth() : 0;
+    const long nWidthL = pLnL ? pLnL->GetWidth() : 0;
+    const long nWidthB = pLnB ? pLnB->GetWidth() : 0;
+    const long nWidthR = pLnR ? pLnR->GetWidth() : 0;
+
+    // Resulting distances from text to borders
+    const sal_Int32 nT2BT = pLnT ? nT : 0;
+    const sal_Int32 nT2BL = pLnL ? nL : 0;
+    const sal_Int32 nT2BB = pLnB ? nB : 0;
+    const sal_Int32 nT2BR = pLnR ? nR : 0;
+
+    // Resulting distances from edge to borders
+    const sal_Int32 nE2BT = pLnT ? std::max<sal_Int32>(rMargins.nTop - nT - nWidthT, 0) : 0;
+    const sal_Int32 nE2BL = pLnL ? std::max<sal_Int32>(rMargins.nLeft - nL - nWidthL, 0) : 0;
+    const sal_Int32 nE2BB = pLnB ? std::max<sal_Int32>(rMargins.nBottom - nB - nWidthB, 0) : 0;
+    const sal_Int32 nE2BR = pLnR ? std::max<sal_Int32>(rMargins.nRight - nR - nWidthR, 0) : 0;
+
+    const sal_Int32 n32pt = 32 * 20;
+    // 1. If all borders are in range of 31 pts from text
+    if (nT2BT < n32pt && nT2BL < n32pt && nT2BB < n32pt && nT2BR < n32pt)
+    {
+        rDistances.bFromEdge = false;
+    }
+    else
+    {
+        // 2. If all borders are in range of 31 pts from edge
+        if (nE2BT < n32pt && nE2BL < n32pt && nE2BB < n32pt && nE2BR < n32pt)
+        {
+            rDistances.bFromEdge = true;
+        }
+        else
+        {
+            // Let's try to guess which would be the best approximation
+            rDistances.bFromEdge =
+                (nT2BT + nT2BL + nT2BB + nT2BR) > (nE2BT + nE2BL + nE2BB + nE2BR);
+        }
+    }
+
+    if (rDistances.bFromEdge)
+    {
+        rDistances.nTop = sal::static_int_cast<sal_uInt16>(nE2BT);
+        rDistances.nLeft = sal::static_int_cast<sal_uInt16>(nE2BL);
+        rDistances.nBottom = sal::static_int_cast<sal_uInt16>(nE2BB);
+        rDistances.nRight = sal::static_int_cast<sal_uInt16>(nE2BR);
+    }
+    else
+    {
+        rDistances.nTop = sal::static_int_cast<sal_uInt16>(nT2BT);
+        rDistances.nLeft = sal::static_int_cast<sal_uInt16>(nT2BL);
+        rDistances.nBottom = sal::static_int_cast<sal_uInt16>(nT2BB);
+        rDistances.nRight = sal::static_int_cast<sal_uInt16>(nT2BR);
+    }
+}
+
+}
+
 // class SvxFormatBreakItem -------------------------------------------------
 
 bool SvxFormatBreakItem::operator==( const SfxPoolItem& rAttr ) const
diff --git a/include/editeng/boxitem.hxx b/include/editeng/boxitem.hxx
index e6ebd6d42379..14a30dfc4c70 100644
--- a/include/editeng/boxitem.hxx
+++ b/include/editeng/boxitem.hxx
@@ -231,6 +231,36 @@ public:
                                          : ( nValidFlags &= ~nValid ); }
     void                    ResetFlags();
 };
+
+namespace editeng
+{
+
+void EDITENG_DLLPUBLIC BorderDistanceFromWord(bool bFromEdge, sal_Int32& nMargin,
+    sal_Int32& nBorderDistance, sal_Int32 nBorderWidth);
+
+struct EDITENG_DLLPUBLIC WordPageMargins final
+{
+    sal_uInt16 nLeft = 0;
+    sal_uInt16 nRight = 0;
+    sal_uInt16 nTop = 0;
+    sal_uInt16 nBottom = 0;
+};
+
+struct EDITENG_DLLPUBLIC WordBorderDistances final
+{
+    bool bFromEdge = false;
+    sal_uInt16 nLeft = 0;
+    sal_uInt16 nRight = 0;
+    sal_uInt16 nTop = 0;
+    sal_uInt16 nBottom = 0;
+};
+
+// Heuristics to decide if we need to use "from edge" offset of borders. All sizes in twips
+void EDITENG_DLLPUBLIC BorderDistancesToWord(const SvxBoxItem& rBox, const WordPageMargins& rMargins,
+    WordBorderDistances& rDistances);
+
+}
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ooxmlexport/data/tdf112118.docx b/sw/qa/extras/ooxmlexport/data/tdf112118.docx
index 87081d8c6dd9..dc3e14ae82c7 100644
Binary files a/sw/qa/extras/ooxmlexport/data/tdf112118.docx and b/sw/qa/extras/ooxmlexport/data/tdf112118.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 239786d5f8eb..ba89b63b2f6e 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -281,7 +281,7 @@ DECLARE_OOXMLEXPORT_TEST(testTdf107035, "tdf107035.docx")
     CPPUNIT_ASSERT_EQUAL(sal_Int32(COL_AUTO), nPgNumColour);
 }
 
-DECLARE_OOXMLEXPORT_TEST(testTdf112118, "tdf112118.docx")
+DECLARE_OOXMLEXPORT_TEST(testTdf112118_DOCX, "tdf112118.docx")
 {
     auto xStyles = getStyles("PageStyles");
     auto testProc = [&](const OUString& sStyleName, sal_Int32 nMargin, sal_Int32 nBorderDistance,
diff --git a/sw/qa/extras/ww8export/data/tdf112118.doc b/sw/qa/extras/ww8export/data/tdf112118.doc
new file mode 100644
index 000000000000..3c8e256407ad
Binary files /dev/null and b/sw/qa/extras/ww8export/data/tdf112118.doc differ
diff --git a/sw/qa/extras/ww8export/ww8export2.cxx b/sw/qa/extras/ww8export/ww8export2.cxx
index 3d977881c677..4bb2947aef78 100644
--- a/sw/qa/extras/ww8export/ww8export2.cxx
+++ b/sw/qa/extras/ww8export/ww8export2.cxx
@@ -741,6 +741,37 @@ DECLARE_OOXMLEXPORT_TEST( testObjectCrossReference, "object_cross_reference.odt"
     }
 }
 
+DECLARE_WW8EXPORT_TEST(testTdf112118_DOC, "tdf112118.doc")
+{
+    auto xStyles = getStyles("PageStyles");
+    auto testProc = [&](const OUString& sStyleName, sal_Int32 nMargin, sal_Int32 nBorderDistance,
+        sal_Int16 nBorderWidth)
+    {
+        typedef std::initializer_list<OUStringLiteral> StringList;
+        uno::Reference<beans::XPropertySet> xStyle(xStyles->getByName(sStyleName), uno::UNO_QUERY_THROW);
+        for (const auto& side : StringList{ "Top", "Left", "Bottom", "Right" })
+        {
+            table::BorderLine aBorder = getProperty<table::BorderLine>(xStyle, side + "Border");
+            CPPUNIT_ASSERT_EQUAL(sal_Int16(nBorderWidth), aBorder.OuterLineWidth);
+            CPPUNIT_ASSERT_EQUAL(sal_Int16(0), aBorder.InnerLineWidth);
+            CPPUNIT_ASSERT_EQUAL(sal_Int16(0), aBorder.LineDistance);
+
+            sal_Int32 nMarginActual = getProperty<sal_Int32>(xStyle, side + "Margin");
+            CPPUNIT_ASSERT_EQUAL(nMargin, nMarginActual);
+
+            sal_Int32 nBorderDistanceActual = getProperty<sal_Int32>(xStyle, side + "BorderDistance");
+            CPPUNIT_ASSERT_EQUAL(nBorderDistance, nBorderDistanceActual);
+        }
+    };
+
+    // For both styles used in document, the total distance from page edge to text must be 2.54 cm.
+    // The first style uses "from edge" border distance; the second uses "from text" border distance
+    // Border distances in both cases are 24 pt = 847 mm100; line widths are 6 pt = 212 mm100.
+    // 1482 + 847 + 212 = 2541
+    testProc("Standard", 847, 1482, 212);
+    testProc("Convert 1", 1482, 847, 212);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index d4cf4c8f29c4..fd8750dfb1ac 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -74,7 +74,6 @@
 #include <editeng/colritem.hxx>
 #include <editeng/hyphenzoneitem.hxx>
 #include <editeng/ulspitem.hxx>
-#include <editeng/boxitem.hxx>
 #include <editeng/contouritem.hxx>
 #include <editeng/shdditem.hxx>
 #include <editeng/emphasismarkitem.hxx>
@@ -3066,98 +3065,6 @@ static OutputBorderOptions lcl_getBoxBorderOptions()
     return rOptions;
 }
 
-struct BorderDistances
-{
-    bool bFromEdge = false;
-    sal_uInt16 nTop = 0;
-    sal_uInt16 nLeft = 0;
-    sal_uInt16 nBottom = 0;
-    sal_uInt16 nRight = 0;
-};
-
-// Heuristics to decide if we need to use "from edge" offset of borders
-//
-// There are two cases when we can safely use "from text" or "from edge" offset without distorting
-// border position (modulo rounding errors):
-// 1. When distance of all borders from text is no greater than 31 pt, we use "from text"
-// 2. Otherwise, if distance of all borders from edge is no greater than 31 pt, we use "from edge"
-// In all other cases, the position of borders would be distorted on export, because Word doesn't
-// support the offset of >31 pts (https://msdn.microsoft.com/en-us/library/ff533820), and we need
-// to decide which type of offset would provide less wrong result (i.e., the result would look
-// closer to original). Here, we just check sum of distances from text to borders, and if it is
-// less than sum of distances from borders to edges. The alternative would be to compare total areas
-// between text-and-borders and between borders-and-edges (taking into account different lengths of
-// borders, and visual impact of that).
-static void CalculateExportDistances(const SvxBoxItem& rBox, const PageMargins& rMargins,
-                                     OutputBorderOptions& rOptions)
-{
-    rOptions.pDistances = std::make_shared<BorderDistances>();
-
-    const sal_uInt16 nT = rBox.GetDistance(SvxBoxItemLine::TOP);
-    const sal_uInt16 nL = rBox.GetDistance(SvxBoxItemLine::LEFT);
-    const sal_uInt16 nB = rBox.GetDistance(SvxBoxItemLine::BOTTOM);
-    const sal_uInt16 nR = rBox.GetDistance(SvxBoxItemLine::RIGHT);
-
-    // Only take into account existing borders
-    const SvxBorderLine* pLnT = rBox.GetLine(SvxBoxItemLine::TOP);
-    const SvxBorderLine* pLnL = rBox.GetLine(SvxBoxItemLine::LEFT);
-    const SvxBorderLine* pLnB = rBox.GetLine(SvxBoxItemLine::BOTTOM);
-    const SvxBorderLine* pLnR = rBox.GetLine(SvxBoxItemLine::RIGHT);
-
-    // We need to take border widths into account
-    const sal_uInt16 nWidthT = pLnT ? pLnT->GetWidth() : 0;
-    const sal_uInt16 nWidthL = pLnL ? pLnL->GetWidth() : 0;
-    const sal_uInt16 nWidthB = pLnB ? pLnB->GetWidth() : 0;
-    const sal_uInt16 nWidthR = pLnR ? pLnR->GetWidth() : 0;
-
-    // Resulting distances from text to borders
-    const sal_uInt16 nT2BT = pLnT ? nT : 0;
-    const sal_uInt16 nT2BL = pLnL ? nL : 0;
-    const sal_uInt16 nT2BB = pLnB ? nB : 0;
-    const sal_uInt16 nT2BR = pLnR ? nR : 0;
-
-    // Resulting distances from edge to borders
-    const sal_uInt16 nE2BT = pLnT ? rMargins.nPageMarginTop - nT - nWidthT : 0;
-    const sal_uInt16 nE2BL = pLnL ? rMargins.nPageMarginLeft - nL - nWidthL : 0;
-    const sal_uInt16 nE2BB = pLnB ? rMargins.nPageMarginBottom - nB - nWidthB : 0;
-    const sal_uInt16 nE2BR = pLnR ? rMargins.nPageMarginRight - nR - nWidthR : 0;
-
-    // 1. If all borders are in range of 31 pts from text
-    if ((nT2BT / 20) <= 31 && (nT2BL / 20) <= 31 && (nT2BB / 20) <= 31 && (nT2BR / 20) <= 31)
-    {
-        rOptions.pDistances->bFromEdge = false;
-    }
-    else
-    {
-        // 2. If all borders are in range of 31 pts from edge
-        if ((nE2BT / 20) <= 31 && (nE2BL / 20) <= 31 && (nE2BB / 20) <= 31 && (nE2BR / 20) <= 31)
-        {
-            rOptions.pDistances->bFromEdge = true;
-        }
-        else
-        {
-            // Let's try to guess which would be the best approximation
-            rOptions.pDistances->bFromEdge =
-                (nT2BT + nT2BL + nT2BB + nT2BR) > (nE2BT + nE2BL + nE2BB + nE2BR);
-        }
-    }
-
-    if (rOptions.pDistances->bFromEdge)
-    {
-        rOptions.pDistances->nTop = nE2BT;
-        rOptions.pDistances->nLeft = nE2BL;
-        rOptions.pDistances->nBottom = nE2BB;
-        rOptions.pDistances->nRight = nE2BR;
-    }
-    else
-    {
-        rOptions.pDistances->nTop = nT2BT;
-        rOptions.pDistances->nLeft = nT2BL;
-        rOptions.pDistances->nBottom = nT2BB;
-        rOptions.pDistances->nRight = nT2BR;
-    }
-}
-
 static void impl_borders( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, const OutputBorderOptions& rOptions,
                           std::map<SvxBoxItemLine, css::table::BorderLine2> &rTableStyleConf )
 {
@@ -6143,15 +6050,16 @@ void DocxAttributeOutput::SectionPageBorders( const SwFrameFormat* pFormat, cons
     }
 
     // By top margin, impl_borders() means the distance between the top of the page and the header frame.
-    PageMargins aMargins = m_pageMargins;
+    editeng::WordPageMargins aMargins = m_pageMargins;
     HdFtDistanceGlue aGlue(pFormat->GetAttrSet());
     if (aGlue.HasHeader())
-        aMargins.nPageMarginTop = aGlue.dyaHdrTop;
+        aMargins.nTop = aGlue.dyaHdrTop;
     // Ditto for bottom margin.
     if (aGlue.HasFooter())
-        aMargins.nPageMarginBottom = aGlue.dyaHdrBottom;
+        aMargins.nBottom = aGlue.dyaHdrBottom;
 
-    CalculateExportDistances(rBox, aMargins, aOutputBorderOptions);
+    aOutputBorderOptions.pDistances = std::make_shared<editeng::WordBorderDistances>();
+    editeng::BorderDistancesToWord(rBox, aMargins, *aOutputBorderOptions.pDistances);
 
     // All distances are relative to the text margins
     m_pSerializer->startElementNS(XML_w, XML_pgBorders,
@@ -7993,24 +7901,21 @@ void DocxAttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLRSpace )
     }
     else if ( m_rExport.m_bOutPageDescs )
     {
-        m_pageMargins.nPageMarginLeft = 0;
-        m_pageMargins.nPageMarginRight = 0;
+        m_pageMargins.nLeft = 0;
+        m_pageMargins.nRight = 0;
 
-        const SfxPoolItem* pItem = m_rExport.HasItem( RES_BOX );
-        if ( pItem )
+        if ( auto pBoxItem = static_cast<const SvxBoxItem*>(m_rExport.HasItem( RES_BOX )) )
         {
-            m_pageMargins.nPageMarginRight = static_cast<const SvxBoxItem*>(pItem)->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
-            m_pageMargins.nPageMarginLeft = static_cast<const SvxBoxItem*>(pItem)->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
+            m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
+            m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
         }
-        else
-            m_pageMargins.nPageMarginLeft = m_pageMargins.nPageMarginRight = 0;
 
-        m_pageMargins.nPageMarginLeft = m_pageMargins.nPageMarginLeft + static_cast<sal_uInt16>(rLRSpace.GetLeft());
-        m_pageMargins.nPageMarginRight = m_pageMargins.nPageMarginRight + static_cast<sal_uInt16>(rLRSpace.GetRight());
+        m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
+        m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
 
         AddToAttrList( m_pSectionSpacingAttrList, 2,
-                FSNS( XML_w, XML_left ), OString::number( m_pageMargins.nPageMarginLeft ).getStr(),
-                FSNS( XML_w, XML_right ), OString::number( m_pageMargins.nPageMarginRight ).getStr() );
+                FSNS( XML_w, XML_left ), OString::number( m_pageMargins.nLeft ).getStr(),
+                FSNS( XML_w, XML_right ), OString::number( m_pageMargins.nRight ).getStr() );
     }
     else
     {
@@ -8062,20 +7967,20 @@ void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace )
             nHeader = sal_Int32( aDistances.dyaHdrTop );
 
         // Page top
-        m_pageMargins.nPageMarginTop = aDistances.dyaTop;
+        m_pageMargins.nTop = aDistances.dyaTop;
 
         sal_Int32 nFooter = 0;
         if ( aDistances.HasFooter() )
             nFooter = sal_Int32( aDistances.dyaHdrBottom );
 
         // Page Bottom
-        m_pageMargins.nPageMarginBottom = aDistances.dyaBottom;
+        m_pageMargins.nBottom = aDistances.dyaBottom;
 
         AddToAttrList( m_pSectionSpacingAttrList, 5,
                 FSNS( XML_w, XML_header ), OString::number( nHeader ).getStr(),
-                FSNS( XML_w, XML_top ), OString::number( m_pageMargins.nPageMarginTop ).getStr(),
+                FSNS( XML_w, XML_top ), OString::number( m_pageMargins.nTop ).getStr(),
                 FSNS( XML_w, XML_footer ), OString::number( nFooter ).getStr(),
-                FSNS( XML_w, XML_bottom ), OString::number( m_pageMargins.nPageMarginBottom ).getStr(),
+                FSNS( XML_w, XML_bottom ), OString::number( m_pageMargins.nBottom ).getStr(),
                 // FIXME Page Gutter is not handled ATM, setting to 0 as it's mandatory for OOXML
                 FSNS( XML_w, XML_gutter ), "0" );
     }
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 887976a53931..4d380bf6e656 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -27,6 +27,7 @@
 #include <IMark.hxx>
 #include "docxexport.hxx"
 
+#include <editeng/boxitem.hxx>
 #include <sax/fshelper.hxx>
 #include <sax/fastattribs.hxx>
 #include <vcl/vclenum.hxx>
@@ -68,8 +69,6 @@ enum DocxColBreakStatus
     COLBRK_WRITE
 };
 
-struct BorderDistances;
-
 /**
  * A structure that holds information about the options selected
  * when outputting a border to DOCX.
@@ -88,21 +87,7 @@ struct OutputBorderOptions
     bool                bWriteInsideHV = false;
     bool                bWriteDistance = false;
     SvxShadowLocation   aShadowLocation = SvxShadowLocation::NONE;
-    std::shared_ptr<BorderDistances> pDistances;
-};
-
-/**
- * A structure that holds information about the page margins.
- *
- */
-struct PageMargins
-{
-    sal_uInt16 nPageMarginLeft;
-    sal_uInt16 nPageMarginRight;
-    sal_uInt16 nPageMarginTop;
-    sal_uInt16 nPageMarginBottom;
-
-    PageMargins() : nPageMarginLeft(0), nPageMarginRight(0), nPageMarginTop(0), nPageMarginBottom(0) {}
+    std::shared_ptr<editeng::WordBorderDistances> pDistances;
 };
 
 struct DocxTableExportContext;
@@ -929,7 +914,7 @@ private:
     /// Is fake rotation detected, so rotation with 90 degrees should be ignored in this cell?
     bool m_bBtLr;
 
-    PageMargins m_pageMargins;
+    editeng::WordPageMargins m_pageMargins;
 
     std::shared_ptr<DocxTableStyleExport> m_pTableStyleExport;
     // flag to check if auto spacing was set in original file
diff --git a/sw/source/filter/ww8/wrtw8sty.cxx b/sw/source/filter/ww8/wrtw8sty.cxx
index 5cc7bc335008..3505d8f641e3 100644
--- a/sw/source/filter/ww8/wrtw8sty.cxx
+++ b/sw/source/filter/ww8/wrtw8sty.cxx
@@ -1392,6 +1392,10 @@ void WW8AttributeOutput::SectionPageBorders( const SwFrameFormat* pPdFormat, con
             nPgBorder = 2;
     }
 
+    // [MS-DOC] 2.9.255 SPgbPropOperand; 2.9.185 PgbOffsetFrom
+    if (m_bFromEdge)
+        nPgBorder |= (1<<5);
+
     if ( USHRT_MAX != nPgBorder )
     {
         // write the Flag and Border Attribute
diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx
index f6c40194bf5e..9f1419f75209 100644
--- a/sw/source/filter/ww8/ww8atr.cxx
+++ b/sw/source/filter/ww8/ww8atr.cxx
@@ -3959,25 +3959,25 @@ void WW8AttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLR )
     }
     else if ( m_rWW8Export.m_bOutPageDescs )                // PageDescs
     {
-        sal_uInt16 nLDist, nRDist;
-        const SfxPoolItem* pItem = m_rWW8Export.HasItem( RES_BOX );
-        if ( pItem )
+        m_pageMargins.nLeft = 0;
+        m_pageMargins.nRight = 0;
+
+        if ( auto pBoxItem = static_cast<const SvxBoxItem*>(m_rWW8Export.HasItem( RES_BOX )) )
         {
-            nRDist = static_cast<const SvxBoxItem*>(pItem)->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
-            nLDist = static_cast<const SvxBoxItem*>(pItem)->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
+            m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
+            m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
         }
-        else
-            nLDist = nRDist = 0;
-        nLDist = nLDist + static_cast<sal_uInt16>(rLR.GetLeft());
-        nRDist = nRDist + static_cast<sal_uInt16>(rLR.GetRight());
+
+        m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLR.GetLeft());
+        m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLR.GetRight());
 
         // sprmSDxaLeft
         m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaLeft );
-        m_rWW8Export.InsUInt16( nLDist );
+        m_rWW8Export.InsUInt16( m_pageMargins.nLeft );
 
         // sprmSDxaRight
         m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaRight );
-        m_rWW8Export.InsUInt16( nRDist );
+        m_rWW8Export.InsUInt16( m_pageMargins.nRight );
     }
     else
     {                                          // normal paragraphs
@@ -4024,6 +4024,7 @@ void WW8AttributeOutput::FormatULSpace( const SvxULSpaceItem& rUL )
         // sprmSDyaTop
         m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaTop );
         m_rWW8Export.InsUInt16( aDistances.dyaTop );
+        m_pageMargins.nTop = aDistances.dyaTop;
 
         if ( aDistances.HasFooter() )
         {
@@ -4035,6 +4036,7 @@ void WW8AttributeOutput::FormatULSpace( const SvxULSpaceItem& rUL )
         //sprmSDyaBottom
         m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaBottom );
         m_rWW8Export.InsUInt16( aDistances.dyaBottom );
+        m_pageMargins.nBottom = aDistances.dyaBottom;
     }
     else
     {
@@ -4461,7 +4463,21 @@ void WW8AttributeOutput::FormatBox( const SvxBoxItem& rBox )
                       && ( p->GetWidth() != 0 );
         }
 
-        m_rWW8Export.Out_SwFormatBox( rBox, bShadow );
+        SvxBoxItem aBox(rBox);
+        if (m_rWW8Export.m_bOutPageDescs)
+        {
+            editeng::WordBorderDistances aDistances;
+            editeng::BorderDistancesToWord(aBox, m_pageMargins, aDistances);
+
+            aBox.SetDistance(aDistances.nTop, SvxBoxItemLine::TOP);
+            aBox.SetDistance(aDistances.nLeft, SvxBoxItemLine::LEFT);
+            aBox.SetDistance(aDistances.nBottom, SvxBoxItemLine::BOTTOM);
+            aBox.SetDistance(aDistances.nRight, SvxBoxItemLine::RIGHT);
+
+            m_bFromEdge = aDistances.bFromEdge;
+        }
+
+        m_rWW8Export.Out_SwFormatBox( aBox, bShadow );
     }
 }
 
diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx
index cf47f534d9e6..d42c97511303 100644
--- a/sw/source/filter/ww8/ww8attributeoutput.hxx
+++ b/sw/source/filter/ww8/ww8attributeoutput.hxx
@@ -22,6 +22,7 @@
 
 #include "attributeoutputbase.hxx"
 #include "wrtww8.hxx"
+#include <editeng/boxitem.hxx>
 
 class WW8AttributeOutput : public AttributeOutputBase
 {
@@ -491,6 +492,11 @@ protected:
     void TableCellBorders(
         ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner );
 
+private:
+
+    editeng::WordPageMargins m_pageMargins;
+    bool m_bFromEdge = false;
+
 };
 
 #endif // INCLUDED_SW_SOURCE_FILTER_WW8_WW8ATTRIBUTEOUTPUT_HXX
diff --git a/sw/source/filter/ww8/ww8par6.cxx b/sw/source/filter/ww8/ww8par6.cxx
index b088f86e5c4d..5e8341e88060 100644
--- a/sw/source/filter/ww8/ww8par6.cxx
+++ b/sw/source/filter/ww8/ww8par6.cxx
@@ -430,11 +430,22 @@ void wwSectionManager::SetPage(SwPageDesc &rInPageDesc, SwFrameFormat &rFormat,
         SetCols(rFormat, rSection, rSection.GetTextAreaWidth());
 }
 
-static sal_uInt16 lcl_MakeSafeNegativeSpacing(sal_uInt16 nIn)
+namespace {
+// Returns corrected (ODF) margin size
+long SetBorderDistance(bool bFromEdge, SvxBoxItem& aBox, SvxBoxItemLine eLine, long nMSMargin)
 {
-    if (nIn > SHRT_MAX)
-        nIn = 0;
-    return nIn;
+    const editeng::SvxBorderLine* pLine = aBox.GetLine(eLine);
+    if (!pLine)
+        return nMSMargin;
+    sal_Int32 nNewMargin = nMSMargin;
+    sal_Int32 nNewDist = aBox.GetDistance(eLine);
+    sal_Int32 nLineWidth = pLine->GetWidth();
+
+    editeng::BorderDistanceFromWord(bFromEdge, nNewMargin, nNewDist, nLineWidth);
+    aBox.SetDistance(nNewDist, eLine);
+
+    return nNewMargin;
+}
 }
 
 void SwWW8ImplReader::SetPageBorder(SwFrameFormat &rFormat, const wwSection &rSection)
@@ -447,65 +458,15 @@ void SwWW8ImplReader::SetPageBorder(SwFrameFormat &rFormat, const wwSection &rSe
     SetFlyBordersShadow(aSet, rSection.brc, &aSizeArray[0]);
     SvxLRSpaceItem aLR(ItemGet<SvxLRSpaceItem>(aSet, RES_LR_SPACE));
     SvxULSpaceItem aUL(ItemGet<SvxULSpaceItem>(aSet, RES_UL_SPACE));
-
     SvxBoxItem aBox(ItemGet<SvxBoxItem>(aSet, RES_BOX));
-    short aOriginalBottomMargin = aBox.GetDistance(SvxBoxItemLine::BOTTOM);
-
-    if (rSection.maSep.pgbOffsetFrom == 1)
-    {
-        sal_uInt16 nDist;
-        if (aBox.GetLeft())
-        {
-            nDist = aBox.GetDistance(SvxBoxItemLine::LEFT);
-            aBox.SetDistance(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aLR.GetLeft() - nDist)), SvxBoxItemLine::LEFT);
-            aSizeArray[WW8_LEFT] =
-                aSizeArray[WW8_LEFT] - nDist + aBox.GetDistance(SvxBoxItemLine::LEFT);
-        }
-
-        if (aBox.GetRight())
-        {
-            nDist = aBox.GetDistance(SvxBoxItemLine::RIGHT);
-            aBox.SetDistance(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aLR.GetRight() - nDist)), SvxBoxItemLine::RIGHT);
-            aSizeArray[WW8_RIGHT] =
-                aSizeArray[WW8_RIGHT] - nDist + aBox.GetDistance(SvxBoxItemLine::RIGHT);
-        }
+    bool bFromEdge = rSection.maSep.pgbOffsetFrom == 1;
 
-        if (aBox.GetTop())
-        {
-            nDist = aBox.GetDistance(SvxBoxItemLine::TOP);
-            aBox.SetDistance(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aUL.GetUpper() - nDist)), SvxBoxItemLine::TOP);
-            aSizeArray[WW8_TOP] =
-                aSizeArray[WW8_TOP] - nDist + aBox.GetDistance(SvxBoxItemLine::TOP);
-        }
-
-        if (aBox.GetBottom())
-        {
-            nDist = aBox.GetDistance(SvxBoxItemLine::BOTTOM);
-            aBox.SetDistance(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aUL.GetLower() - nDist)), SvxBoxItemLine::BOTTOM);
-            aSizeArray[WW8_BOT] =
-                aSizeArray[WW8_BOT] - nDist + aBox.GetDistance(SvxBoxItemLine::BOTTOM);
-        }
-
-        aSet.Put(aBox);
-    }
-
-    if (aBox.GetLeft())
-        aLR.SetLeft(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aLR.GetLeft() - aSizeArray[WW8_LEFT])));
-    if (aBox.GetRight())
-        aLR.SetRight(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aLR.GetRight() - aSizeArray[WW8_RIGHT])));
-    if (aBox.GetTop())
-        aUL.SetUpper(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aUL.GetUpper() - aSizeArray[WW8_TOP])));
-    if (aBox.GetBottom())
-    {
-        //#i30088# and #i30074# - do a final sanity check on
-        //bottom value. Do not allow a resulting zero if bottom
-        //Border margin value was not originally zero.
-        if(aUL.GetLower() != 0)
-            aUL.SetLower(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aUL.GetLower() - aSizeArray[WW8_BOT])));
-        else
-            aUL.SetLower(lcl_MakeSafeNegativeSpacing(static_cast<sal_uInt16>(aOriginalBottomMargin - aSizeArray[WW8_BOT])));
-    }
+    aLR.SetLeft(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::LEFT, aLR.GetLeft()));
+    aLR.SetRight(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::RIGHT, aLR.GetRight()));
+    aUL.SetUpper(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::TOP, aUL.GetUpper()));
+    aUL.SetLower(SetBorderDistance(bFromEdge, aBox, SvxBoxItemLine::BOTTOM, aUL.GetLower()));
 
+    aSet.Put(aBox);
     aSet.Put(aLR);
     aSet.Put(aUL);
     rFormat.SetFormatAttr(aSet);
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
index 2447ee40aeb9..76851c428438 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -20,6 +20,7 @@
 #include <ooxml/resourceids.hxx>
 #include "DomainMapper_Impl.hxx"
 #include "ConversionHelper.hxx"
+#include <editeng/boxitem.hxx>
 #include <i18nutil/paper.hxx>
 #include <osl/diagnose.h>
 #include <rtl/ustring.hxx>
@@ -647,8 +648,6 @@ void SectionPropertyMap::SetBorderDistance( const uno::Reference< beans::XProper
                                             BorderOffsetFrom eOffsetFrom,
                                             sal_uInt32 nLineWidth )
 {
-    // See https://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
-
     if (!xStyle.is())
         return;
     const OUString sMarginName = getPropertyName( eMarginId );
@@ -656,35 +655,12 @@ void SectionPropertyMap::SetBorderDistance( const uno::Reference< beans::XProper
     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 correct 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;
-    }
+    editeng::BorderDistanceFromWord(eOffsetFrom == BorderOffsetFrom::Edge, nMargin, nDistance,
+                                    nLineWidth);
+
     // Change the margins with the border distance
-    xStyle->setPropertyValue( sMarginName, uno::makeAny( nNewMargin ) );
-    xStyle->setPropertyValue( sBorderDistanceName, uno::makeAny( nNewDist ) );
+    xStyle->setPropertyValue( sMarginName, uno::makeAny( nMargin ) );
+    xStyle->setPropertyValue( sBorderDistanceName, uno::makeAny( nDistance ) );
 }
 
 void SectionPropertyMap::DontBalanceTextColumns()


More information about the Libreoffice-commits mailing list