[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-5.3-desktop' - sw/qa sw/source writerfilter/source

Mike Kaganski mike.kaganski at collabora.com
Thu Mar 22 23:24:20 UTC 2018


 sw/qa/extras/ooxmlexport/data/tdf112118.docx       |binary
 sw/qa/extras/ooxmlexport/ooxmlexport9.cxx          |   37 ++++
 sw/source/filter/ww8/docxattributeoutput.cxx       |  165 +++++++++++++--------
 sw/source/filter/ww8/docxattributeoutput.hxx       |   18 +-
 writerfilter/source/dmapper/DomainMapper.cxx       |    1 
 writerfilter/source/dmapper/PageBordersHandler.cxx |   16 +-
 writerfilter/source/dmapper/PageBordersHandler.hxx |    8 -
 writerfilter/source/dmapper/PropertyMap.cxx        |   79 ++++++----
 writerfilter/source/dmapper/PropertyMap.hxx        |   24 ++-
 9 files changed, 235 insertions(+), 113 deletions(-)

New commits:
commit 83205787d5dd457128ef606de047454e220f4dcf
Author: Mike Kaganski <mike.kaganski at collabora.com>
Date:   Wed Mar 14 10:18:15 2018 +0300

    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 at libreoffice.org>
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/51399
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Miklos Vajna <vmiklos at collabora.co.uk>
    (cherry picked from commit c91f81f59fac308d8ab86637b241502e68d7ab6a)
    Reviewed-on: https://gerrit.libreoffice.org/51400
    Reviewed-by: Aron Budea <aron.budea at collabora.com>
    Tested-by: Aron Budea <aron.budea at collabora.com>

diff --git a/sw/qa/extras/ooxmlexport/data/tdf112118.docx b/sw/qa/extras/ooxmlexport/data/tdf112118.docx
new file mode 100644
index 000000000000..87081d8c6dd9
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf112118.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx
index be729d8ecd33..11d073ec8b9e 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport9.cxx
@@ -11,6 +11,7 @@
 
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/table/BorderLine.hpp>
 #include <com/sun/star/text/XDependentTextField.hpp>
 #include <com/sun/star/text/XPageCursor.hpp>
 #include <com/sun/star/text/XTextColumns.hpp>
@@ -647,6 +648,42 @@ DECLARE_OOXMLEXPORT_TEST(testGraphicObjectFliph, "graphic-object-fliph.docx")
     CPPUNIT_ASSERT(getProperty<bool>(getShape(1), "HoriMirroredOnOddPages"));
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf112118, "tdf112118.docx")
+{
+    auto xStyles = getStyles("PageStyles");
+    const std::initializer_list<OUStringLiteral> sides = {
+        OUStringLiteral("Top"),
+        OUStringLiteral("Left"),
+        OUStringLiteral("Bottom"),
+        OUStringLiteral("Right")
+    };
+    auto testProc = [&](const OUString& sStyleName, sal_Int32 nMargin, sal_Int32 nBorderDistance,
+                        sal_Int16 nBorderWidth)
+    {
+        uno::Reference<beans::XPropertySet> xStyle(xStyles->getByName(sStyleName), uno::UNO_QUERY_THROW);
+        for (const auto& side : sides)
+        {
+            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("Converted1", 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 2deae355d258..0f9c23d4a788 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -2733,8 +2733,6 @@ static OutputBorderOptions lcl_getTableDefaultBorderOptions(bool bEcma)
     rOptions.bWriteTag = true;
     rOptions.bWriteInsideHV = true;
     rOptions.bWriteDistance = false;
-    rOptions.aShadowLocation = SVX_SHADOW_NONE;
-    rOptions.bCheckDistanceSize = false;
 
     return rOptions;
 }
@@ -2748,8 +2746,6 @@ static OutputBorderOptions lcl_getTableCellBorderOptions(bool bEcma)
     rOptions.bWriteTag = true;
     rOptions.bWriteInsideHV = true;
     rOptions.bWriteDistance = false;
-    rOptions.aShadowLocation = SVX_SHADOW_NONE;
-    rOptions.bCheckDistanceSize = false;
 
     return rOptions;
 }
@@ -2763,23 +2759,103 @@ static OutputBorderOptions lcl_getBoxBorderOptions()
     rOptions.bWriteTag = false;
     rOptions.bWriteInsideHV = false;
     rOptions.bWriteDistance = true;
-    rOptions.aShadowLocation = SVX_SHADOW_NONE;
-    rOptions.bCheckDistanceSize = false;
 
     return rOptions;
 }
 
-static bool boxHasLineLargerThan31(const SvxBoxItem& rBox)
+struct BorderDistances
 {
-    return  (
-                ( rBox.GetDistance( SvxBoxItemLine::TOP ) / 20 ) > 31 ||
-                ( rBox.GetDistance( SvxBoxItemLine::LEFT ) / 20 ) > 31 ||
-                ( rBox.GetDistance( SvxBoxItemLine::BOTTOM ) / 20 ) > 31 ||
-                ( rBox.GetDistance( SvxBoxItemLine::RIGHT ) / 20 ) > 31
-            );
+    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 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).
+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, PageMargins* pageMargins,
+static void impl_borders( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, const OutputBorderOptions& rOptions,
                           std::map<SvxBoxItemLine, css::table::BorderLine2> &rTableStyleConf )
 {
     static const SvxBoxItemLine aBorders[] =
@@ -2797,16 +2873,6 @@ static void impl_borders( FSHelperPtr const & pSerializer, const SvxBoxItem& rBo
     bool tagWritten = false;
     const SvxBoxItemLine* pBrd = aBorders;
 
-    bool bExportDistanceFromPageEdge = false;
-    if ( rOptions.bCheckDistanceSize && boxHasLineLargerThan31(rBox) )
-    {
-        // The distance is larger than '31'. This cannot be exported as 'distance from text'.
-        // Instead - it should be exported as 'distance from page edge'.
-        // This is based on http://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
-        // Specifically 'export case #2'
-        bExportDistanceFromPageEdge = true;
-    }
-
     bool bWriteInsideH = false;
     bool bWriteInsideV = false;
     for( int i = 0; i < 4; ++i, ++pBrd )
@@ -2855,22 +2921,20 @@ static void impl_borders( FSHelperPtr const & pSerializer, const SvxBoxItem& rBo
         sal_uInt16 nDist = 0;
         if (rOptions.bWriteDistance)
         {
-            if (bExportDistanceFromPageEdge)
+            if (rOptions.pDistances)
             {
-                // Export 'Distance from Page Edge'
                 if ( *pBrd == SvxBoxItemLine::TOP)
-                    nDist = pageMargins->nPageMarginTop - rBox.GetDistance( *pBrd );
+                    nDist = rOptions.pDistances->nTop;
                 else if ( *pBrd == SvxBoxItemLine::LEFT)
-                    nDist = pageMargins->nPageMarginLeft - rBox.GetDistance( *pBrd );
+                    nDist = rOptions.pDistances->nLeft;
                 else if ( *pBrd == SvxBoxItemLine::BOTTOM)
-                    nDist = pageMargins->nPageMarginBottom - rBox.GetDistance( *pBrd );
+                    nDist = rOptions.pDistances->nBottom;
                 else if ( *pBrd == SvxBoxItemLine::RIGHT)
-                    nDist = pageMargins->nPageMarginRight - rBox.GetDistance( *pBrd );
+                    nDist = rOptions.pDistances->nRight;
             }
             else
             {
-                // Export 'Distance from text'
-                nDist = rBox.GetDistance( *pBrd );
+                nDist = rBox.GetDistance(*pBrd);
             }
         }
 
@@ -3019,7 +3083,7 @@ void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Point
     const SvxBoxItem& rDefaultBox = (*tableFirstCells.rbegin())->getTableBox( )->GetFrameFormat( )->GetBox( );
     {
         // The cell borders
-        impl_borders( m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma), nullptr, m_aTableStyleConf );
+        impl_borders( m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma), m_aTableStyleConf );
     }
 
     TableBackgrounds( pTableTextNodeInfoInner );
@@ -3477,7 +3541,7 @@ void DocxAttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Point
     if (m_aTableStyleConf.empty())
     {
         // the defaults of the table are taken from the top-left cell
-        impl_borders(m_pSerializer, pFrameFormat->GetBox(), lcl_getTableDefaultBorderOptions(bEcma), nullptr, m_aTableStyleConf);
+        impl_borders(m_pSerializer, pFrameFormat->GetBox(), lcl_getTableDefaultBorderOptions(bEcma), m_aTableStyleConf);
     }
 }
 
@@ -5708,27 +5772,8 @@ void DocxAttributeOutput::SectionPageBorders( const SwFrameFormat* pFormat, cons
 
     if ( pBottom || pTop || pLeft || pRight )
     {
-        bool bExportDistanceFromPageEdge = false;
-        if ( boxHasLineLargerThan31(rBox) )
-        {
-            // The distance is larger than '31'. This cannot be exported as 'distance from text'.
-            // Instead - it should be exported as 'distance from page edge'.
-            // This is based on http://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
-            // Specifically 'export case #2'
-            bExportDistanceFromPageEdge = true;
-        }
-
-        // All distances are relative to the text margins
-        m_pSerializer->startElementNS( XML_w, XML_pgBorders,
-               FSNS( XML_w, XML_display ), "allPages",
-               FSNS( XML_w, XML_offsetFrom ), bExportDistanceFromPageEdge ? "page" : "text",
-               FSEND );
-
         OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions();
 
-        // Check if the distance is larger than 31 points
-        aOutputBorderOptions.bCheckDistanceSize = true;
-
         // Check if there is a shadow item
         const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW );
         if ( pItem )
@@ -5746,9 +5791,16 @@ void DocxAttributeOutput::SectionPageBorders( const SwFrameFormat* pFormat, cons
         if (aGlue.HasFooter())
             aMargins.nPageMarginBottom = aGlue.dyaHdrBottom;
 
+        CalculateExportDistances(rBox, aMargins, aOutputBorderOptions);
+
+        // All distances are relative to the text margins
+        m_pSerializer->startElementNS(XML_w, XML_pgBorders,
+            FSNS(XML_w, XML_display), "allPages",
+            FSNS(XML_w, XML_offsetFrom), aOutputBorderOptions.pDistances->bFromEdge ? "page" : "text",
+            FSEND);
+
         std::map<SvxBoxItemLine, css::table::BorderLine2> aEmptyMap; // empty styles map
-        impl_borders( m_pSerializer, rBox, aOutputBorderOptions, &aMargins,
-                      aEmptyMap );
+        impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aEmptyMap );
 
         m_pSerializer->endElementNS( XML_w, XML_pgBorders );
     }
@@ -8096,8 +8148,7 @@ void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox )
         m_pSerializer->startElementNS( XML_w, XML_pBdr, FSEND );
 
         std::map<SvxBoxItemLine, css::table::BorderLine2> aEmptyMap; // empty styles map
-        impl_borders( m_pSerializer, rBox, aOutputBorderOptions, &m_pageMargins,
-                      aEmptyMap );
+        impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aEmptyMap );
 
         // Close the paragraph's borders tag
         m_pSerializer->endElementNS( XML_w, XML_pBdr );
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 11046dcfa514..3eb8801e50af 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -67,6 +67,8 @@ enum DocxColBreakStatus
     COLBRK_WRITE
 };
 
+struct BorderDistances;
+
 /**
  * A structure that holds information about the options selected
  * when outputting a border to DOCX.
@@ -79,15 +81,13 @@ enum DocxColBreakStatus
  */
 struct OutputBorderOptions
 {
-    sal_Int32           tag;
-    bool                bUseStartEnd;
-    bool                bWriteTag;
-    bool                bWriteInsideHV;
-    bool                bWriteDistance;
-    SvxShadowLocation   aShadowLocation;
-    bool                bCheckDistanceSize;
-
-    OutputBorderOptions() : tag(0), bUseStartEnd(false), bWriteTag(true), bWriteInsideHV(false), bWriteDistance(false), aShadowLocation(SVX_SHADOW_NONE), bCheckDistanceSize(false) {}
+    sal_Int32           tag = 0;
+    bool                bUseStartEnd = false;
+    bool                bWriteTag = true;
+    bool                bWriteInsideHV = false;
+    bool                bWriteDistance = false;
+    SvxShadowLocation   aShadowLocation = SVX_SHADOW_NONE;
+    std::shared_ptr<BorderDistances> pDistances;
 };
 
 /**
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
index ee9f30dbf66b..55f6a5f4e7a5 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -2102,7 +2102,6 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
 
             // Set the borders to the context and apply them to the styles
             pHandler->SetBorders( pSectionContext );
-            pSectionContext->SetBorderParams( pHandler->GetDisplayOffset( ) );
         }
     }
     break;
diff --git a/writerfilter/source/dmapper/PageBordersHandler.cxx b/writerfilter/source/dmapper/PageBordersHandler.cxx
index f9fe9685692b..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,6 +137,8 @@ void PageBordersHandler::SetBorders( SectionPropertyMap* pSectContext )
     {
         pSectContext->SetBorder( rBorder.m_ePos, rBorder.m_nDistance, rBorder.m_rLine, rBorder.m_bShadow );
     }
+    pSectContext->SetBorderApply(m_eBorderApply);
+    pSectContext->SetBorderOffsetFrom(m_eOffsetFrom);
 }
 
 } }
diff --git a/writerfilter/source/dmapper/PageBordersHandler.hxx b/writerfilter/source/dmapper/PageBordersHandler.hxx
index 8fa0f40a6976..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;
 
-    inline 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 70d9c3fcbf32..852eb848620d 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -395,7 +395,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 )
@@ -568,7 +569,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:
@@ -585,26 +586,25 @@ void SectionPropertyMap::ApplyBorderToPageStyles(
             */
     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 );
+                xFirst = GetPageStyle( xPageStyles, xTextFactory, false );
         break;
-        case 3: //whole document?
-            //todo: how to apply a border to the whole document - find all sections or access all page styles?
+        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;
     }
@@ -648,10 +648,10 @@ void SectionPropertyMap::ApplyBorderToPageStyles(
                 nLineWidth = m_oBorderLines[nBorder]->LineWidth;
             if(xFirst.is())
                 SetBorderDistance( xFirst, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
-                  m_nBorderDistances[nBorder], nOffsetFrom, nLineWidth );
-            if(xSecond.is())
+                    m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth );
+            if ( xSecond.is() )
                 SetBorderDistance( xSecond, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
-                      m_nBorderDistances[nBorder], nOffsetFrom, nLineWidth );
+                    m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth );
         }
     }
 
@@ -679,25 +679,46 @@ table::ShadowFormat PropertyMap::getShadowFromBorder(const table::BorderLine2& r
 }
 
 void SectionPropertyMap::SetBorderDistance( uno::Reference< beans::XPropertySet > const& xStyle,
-        PropertyIds eMarginId, PropertyIds eDistId, sal_Int32 nDistance, sal_Int32 nOffsetFrom, sal_uInt32 nLineWidth )
+        PropertyIds eMarginId, PropertyIds eDistId, sal_Int32 nDistance, 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;
+    // See https://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
 
-        // Change the margins with the border distance
-        xStyle->setPropertyValue( sMarginName, uno::makeAny( nDistance ) );
+    if (!xStyle.is())
+        return;
+    const OUString sMarginName = getPropertyName( eMarginId );
+    const OUString sBorderDistanceName = getPropertyName( eDistId );
+    uno::Any aMargin = xStyle->getPropertyValue( sMarginName );
+    sal_Int32 nMargin = 0;
+    aMargin >>= nMargin;
+    sal_Int32 nNewMargin = nMargin;
+    sal_Int32 nNewDist = nDistance;
 
-        // Set the distance to ( Margin - distance - nLineWidth )
-        nDist = nMargin - nDistance - nLineWidth;
+    switch (eOffsetFrom)
+    {
+    case BorderOffsetFrom::Text:
+        nNewMargin -= nDistance + nLineWidth;
+        break;
+    case BorderOffsetFrom::Edge:
+        nNewMargin = nDistance;
+        nNewDist = nMargin - nDistance - nLineWidth;
+        break;
     }
-    const OUString sBorderDistanceName = getPropertyName( eDistId );
-    if (xStyle.is())
-        xStyle->setPropertyValue( sBorderDistanceName, uno::makeAny( nDist ));
+    // 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()
@@ -1408,7 +1429,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 4aad5da6873b..b4188cb0c7df 100644
--- a/writerfilter/source/dmapper/PropertyMap.hxx
+++ b/writerfilter/source/dmapper/PropertyMap.hxx
@@ -172,6 +172,20 @@ 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:
     //--> debug
     sal_Int32 nSectionNumber;
     //<-- debug
@@ -188,7 +202,8 @@ class SectionPropertyMap : public PropertyMap
 
     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;
@@ -261,7 +276,7 @@ class SectionPropertyMap : public PropertyMap
                            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.
     bool FloatingTableConversion(DomainMapper_Impl& rDM_Impl, FloatingTableInfo& rInfo);
@@ -297,7 +312,8 @@ public:
                 css::uno::RuntimeException, std::exception);
 
     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; }
@@ -338,7 +354,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.


More information about the Libreoffice-commits mailing list