desktop/source/deployment/dp_persmap.cxx                                      
|    5 
 editeng/source/items/frmitems.cxx                                             
|   68 ++++++---
 include/editeng/boxitem.hxx                                                   
|   15 +-
 sw/ooxmlexport_setup.mk                                                       
|    1 
 sw/qa/core/layout/layout.cxx                                                  
|   71 ++++++++++
 sw/qa/extras/ooxmlexport/ooxmlexport17.cxx                                    
|   42 +++++
 sw/qa/extras/ww8import/ww8import.cxx                                          
|    4 
 sw/source/core/layout/paintfrm.cxx                                            
|    4 
 sw/source/filter/ww8/wrtww8.cxx                                               
|    2 
 sw/source/uibase/shells/tabsh.cxx                                             
|    2 
 writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx                          
|   60 ++++++++
 writerfilter/qa/cppunittests/dmapper/data/negative-page-border-no-margin.docx 
|binary
 writerfilter/qa/cppunittests/dmapper/data/negative-page-border.docx           
|binary
 writerfilter/source/dmapper/PropertyMap.cxx                                   
|    2 
 14 files changed, 239 insertions(+), 37 deletions(-)

New commits:
commit 1f127a2b9e1c1daab0972f98fc8708ecb7afa299
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Jun 7 08:03:34 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Jun 7 08:45:46 2022 +0200

    sw layout: allow negative page border distances
    
    Writer follows the CSS box model when it comes to page borders: there
    can be a positive distance between the edge of the page and the border,
    and again a positive distance between the border and the body frame.
    
    This ensures that the page border never intersect with the body frame,
    which is usually what users expect. Word, however, can work with 2
    distances for border and text, both measured from the edge of the page,
    leading to a page border, which is inside the body text. This is
    described at great detail at
    
<https://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder#Importing_case_3:>.
    
    Fix the problem by allowing negative border distances: this doesn't
    influence the position or the size of the body frame, but it gives us a
    way to position the border more towards the center of the page, leading
    the matching layout between Writer and Word.
    
    The doc model (to allow negative border distances), UNO API and DOCX
    filter is updated in this commit. The ODT filter works without explicit
    effort. Other filters are not yet updated in this commit.
    
    Change-Id: I723e1bdb8dc6391129f1686f88826cc089f6fd67
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135462
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/editeng/source/items/frmitems.cxx 
b/editeng/source/items/frmitems.cxx
index a31df88ca0f0..9780af4904c0 100644
--- a/editeng/source/items/frmitems.cxx
+++ b/editeng/source/items/frmitems.cxx
@@ -1313,6 +1313,20 @@ SvxBoxItem::~SvxBoxItem()
 {
 }
 
+void SvxBoxItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxBoxItem"));
+    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("top-dist"),
+                                      
BAD_CAST(OString::number(nTopDist).getStr()));
+    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bottom-dist"),
+                                      
BAD_CAST(OString::number(nBottomDist).getStr()));
+    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("left-dist"),
+                                      
BAD_CAST(OString::number(nLeftDist).getStr()));
+    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("right-dist"),
+                                      
BAD_CAST(OString::number(nRightDist).getStr()));
+    SfxPoolItem::dumpAsXml(pWriter);
+    (void)xmlTextWriterEndElement(pWriter);
+}
 
 boost::property_tree::ptree SvxBoxItem::dumpAsJSON() const
 {
@@ -1380,7 +1394,7 @@ bool SvxBoxItem::QueryValue( uno::Any& rVal, sal_uInt8 
nMemberId ) const
 {
     bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
     table::BorderLine2 aRetLine;
-    sal_uInt16 nDist = 0;
+    sal_Int16 nDist = 0;
     bool bDistMember = false;
     nMemberId &= ~CONVERT_TWIPS;
     switch(nMemberId)
@@ -1670,7 +1684,6 @@ bool SvxBoxItem::PutValue( const uno::Any& rVal, 
sal_uInt8 nMemberId )
         if(!(rVal >>= nDist))
             return false;
 
-        if(nDist >= 0)
         {
             if( bConvert )
                 nDist = o3tl::toTwips(nDist, o3tl::Length::mm100);
@@ -1883,10 +1896,10 @@ void SvxBoxItem::ScaleMetrics( tools::Long nMult, 
tools::Long nDiv )
     if ( pBottom )  pBottom->ScaleMetrics( nMult, nDiv );
     if ( pLeft )    pLeft->ScaleMetrics( nMult, nDiv );
     if ( pRight )   pRight->ScaleMetrics( nMult, nDiv );
-    nTopDist = static_cast<sal_uInt16>(BigInt::Scale( nTopDist, nMult, nDiv ));
-    nBottomDist = static_cast<sal_uInt16>(BigInt::Scale( nBottomDist, nMult, 
nDiv ));
-    nLeftDist = static_cast<sal_uInt16>(BigInt::Scale( nLeftDist, nMult, nDiv 
));
-    nRightDist = static_cast<sal_uInt16>(BigInt::Scale( nRightDist, nMult, 
nDiv ));
+    nTopDist = static_cast<sal_Int16>(BigInt::Scale( nTopDist, nMult, nDiv ));
+    nBottomDist = static_cast<sal_Int16>(BigInt::Scale( nBottomDist, nMult, 
nDiv ));
+    nLeftDist = static_cast<sal_Int16>(BigInt::Scale( nLeftDist, nMult, nDiv 
));
+    nRightDist = static_cast<sal_Int16>(BigInt::Scale( nRightDist, nMult, nDiv 
));
 }
 
 
@@ -1962,9 +1975,9 @@ sal_uInt16 SvxBoxItem::GetSmallestDistance() const
 }
 
 
-sal_uInt16 SvxBoxItem::GetDistance( SvxBoxItemLine nLine ) const
+sal_Int16 SvxBoxItem::GetDistance( SvxBoxItemLine nLine, bool bAllowNegative ) 
const
 {
-    sal_uInt16 nDist = 0;
+    sal_Int16 nDist = 0;
     switch ( nLine )
     {
         case SvxBoxItemLine::TOP:
@@ -1983,11 +1996,15 @@ sal_uInt16 SvxBoxItem::GetDistance( SvxBoxItemLine 
nLine ) const
             OSL_FAIL( "wrong line" );
     }
 
+    if (!bAllowNegative && nDist < 0)
+    {
+        nDist = 0;
+    }
     return nDist;
 }
 
 
-void SvxBoxItem::SetDistance( sal_uInt16 nNew, SvxBoxItemLine nLine )
+void SvxBoxItem::SetDistance( sal_Int16 nNew, SvxBoxItemLine nLine )
 {
     switch ( nLine )
     {
@@ -2036,10 +2053,10 @@ sal_uInt16 SvxBoxItem::CalcLineWidth( SvxBoxItemLine 
nLine ) const
     return nWidth;
 }
 
-sal_uInt16 SvxBoxItem::CalcLineSpace( SvxBoxItemLine nLine, bool bEvenIfNoLine 
) const
+sal_Int16 SvxBoxItem::CalcLineSpace( SvxBoxItemLine nLine, bool bEvenIfNoLine, 
bool bAllowNegative ) const
 {
     SvxBorderLine* pTmp = nullptr;
-    sal_uInt16 nDist = 0;
+    sal_Int16 nDist = 0;
     switch ( nLine )
     {
     case SvxBoxItemLine::TOP:
@@ -2068,6 +2085,12 @@ sal_uInt16 SvxBoxItem::CalcLineSpace( SvxBoxItemLine 
nLine, bool bEvenIfNoLine )
     }
     else if( !bEvenIfNoLine )
         nDist = 0;
+
+    if (!bAllowNegative && nDist < 0)
+    {
+        nDist = 0;
+    }
+
     return nDist;
 }
 
@@ -2429,7 +2452,7 @@ namespace editeng
 {
 
 void BorderDistanceFromWord(bool bFromEdge, sal_Int32& nMargin, sal_Int32& 
nBorderDistance,
-    sal_Int32 nBorderWidth)
+    sal_Int32 nBorderWidth, bool bAllowNegativeBorderDistance)
 {
     // See 
https://wiki.openoffice.org/wiki/Writer/MSInteroperability/PageBorder
 
@@ -2456,8 +2479,15 @@ void BorderDistanceFromWord(bool bFromEdge, sal_Int32& 
nMargin, sal_Int32& nBord
     }
     else if (nNewBorderDistance < 0)
     {
-        nNewMargin = std::max<sal_Int32>(nMargin - nBorderWidth, 0);
-        nNewBorderDistance = 0;
+        if (bAllowNegativeBorderDistance)
+        {
+            nNewMargin = nMargin;
+        }
+        else
+        {
+            nNewMargin = std::max<sal_Int32>(nMargin - nBorderWidth, 0);
+            nNewBorderDistance = 0;
+        }
     }
 
     nMargin = nNewMargin;
@@ -2481,10 +2511,10 @@ void BorderDistancesToWord(const SvxBoxItem& rBox, 
const WordPageMargins& rMargi
     WordBorderDistances& rDistances)
 {
     // Use signed sal_Int32 that can hold sal_uInt16, to prevent overflow at 
subtraction 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);
+    const sal_Int32 nT = rBox.GetDistance(SvxBoxItemLine::TOP, 
/*bAllowNegative=*/true);
+    const sal_Int32 nL = rBox.GetDistance(SvxBoxItemLine::LEFT, 
/*bAllowNegative=*/true);
+    const sal_Int32 nB = rBox.GetDistance(SvxBoxItemLine::BOTTOM, 
/*bAllowNegative=*/true);
+    const sal_Int32 nR = rBox.GetDistance(SvxBoxItemLine::RIGHT, 
/*bAllowNegative=*/true);
 
     // Only take into account existing borders
     const SvxBorderLine* pLnT = rBox.GetLine(SvxBoxItemLine::TOP);
@@ -2512,7 +2542,7 @@ void BorderDistancesToWord(const SvxBoxItem& rBox, const 
WordPageMargins& rMargi
 
     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)
+    if (nT2BT >= 0 && nT2BT < n32pt && nT2BL >= 0 && nT2BL < n32pt && nT2BB >= 
0 && nT2BB < n32pt && nT2BR >= 0 && nT2BR < n32pt)
     {
         rDistances.bFromEdge = false;
     }
diff --git a/include/editeng/boxitem.hxx b/include/editeng/boxitem.hxx
index 6e0fc53d080c..750993c91f7d 100644
--- a/include/editeng/boxitem.hxx
+++ b/include/editeng/boxitem.hxx
@@ -59,7 +59,7 @@ class EDITENG_DLLPUBLIC SvxBoxItem final : public SfxPoolItem
                     pBottom,
                     pLeft,
                     pRight;
-    sal_uInt16      nTopDist,
+    sal_Int16       nTopDist,
                     nBottomDist,
                     nLeftDist,
                     nRightDist;
@@ -97,29 +97,30 @@ public:
     //The Pointers are being copied!
     void    SetLine( const editeng::SvxBorderLine* pNew, SvxBoxItemLine nLine 
);
 
-    sal_uInt16  GetDistance( SvxBoxItemLine nLine ) const;
+    sal_Int16  GetDistance( SvxBoxItemLine nLine, bool bAllowNegative = false 
) const;
     sal_uInt16  GetSmallestDistance() const;
 
     bool IsRemoveAdjacentCellBorder() const { return bRemoveAdjCellBorder; }
 
-    void    SetDistance( sal_uInt16 nNew, SvxBoxItemLine nLine );
-    inline void SetAllDistances( sal_uInt16 nNew );
+    void    SetDistance( sal_Int16 nNew, SvxBoxItemLine nLine );
+    inline void SetAllDistances( sal_Int16 nNew );
 
     void SetRemoveAdjacentCellBorder( bool bSet ) { bRemoveAdjCellBorder = 
bSet; }
 
     // Line width plus Space plus inward distance
     // bEvenIfNoLine = TRUE -> Also return distance, when no Line is set
     sal_uInt16  CalcLineWidth( SvxBoxItemLine nLine ) const;
-    sal_uInt16  CalcLineSpace( SvxBoxItemLine nLine, bool bEvenIfNoLine = 
false ) const;
+    sal_Int16  CalcLineSpace( SvxBoxItemLine nLine, bool bEvenIfNoLine = 
false, bool bAllowNegative = false ) const;
     bool HasBorder( bool bTreatPaddingAsBorder ) const;
     static css::table::BorderLine2 SvxLineToLine( const 
editeng::SvxBorderLine* pLine, bool bConvert );
     static bool LineToSvxLine(const css::table::BorderLine& rLine, 
editeng::SvxBorderLine& rSvxLine, bool bConvert);
     static bool LineToSvxLine(const css::table::BorderLine2& rLine, 
editeng::SvxBorderLine& rSvxLine, bool bConvert);
 
     virtual boost::property_tree::ptree dumpAsJSON() const override;
+    void dumpAsXml(xmlTextWriterPtr pWriter) const override;
 };
 
-inline void SvxBoxItem::SetAllDistances(sal_uInt16 const nNew)
+inline void SvxBoxItem::SetAllDistances(sal_Int16 const nNew)
 {
     nTopDist = nBottomDist = nLeftDist = nRightDist = nNew;
 }
@@ -240,7 +241,7 @@ namespace editeng
 {
 
 void EDITENG_DLLPUBLIC BorderDistanceFromWord(bool bFromEdge, sal_Int32& 
nMargin,
-    sal_Int32& nBorderDistance, sal_Int32 nBorderWidth);
+    sal_Int32& nBorderDistance, sal_Int32 nBorderWidth, bool 
bAllowNegativeBorderDistance = false);
 
 struct EDITENG_DLLPUBLIC WordPageMargins final
 {
diff --git a/sw/ooxmlexport_setup.mk b/sw/ooxmlexport_setup.mk
index 404ee5153438..f19e41f28469 100644
--- a/sw/ooxmlexport_setup.mk
+++ b/sw/ooxmlexport_setup.mk
@@ -50,6 +50,7 @@ $(eval $(call 
gb_CppunitTest_use_externals,sw_ooxmlexport$(1),\
 $(eval $(call gb_CppunitTest_set_include,sw_ooxmlexport$(1),\
     -I$(SRCDIR)/sw/inc \
     -I$(SRCDIR)/sw/source/core/inc \
+       -I$(SRCDIR)/sw/source/uibase/inc \
        -I$(SRCDIR)/sw/qa/inc \
     $$(INCLUDE) \
 ))
diff --git a/sw/qa/core/layout/layout.cxx b/sw/qa/core/layout/layout.cxx
index 58e5562f3f5c..a6a949b5337b 100644
--- a/sw/qa/core/layout/layout.cxx
+++ b/sw/qa/core/layout/layout.cxx
@@ -743,6 +743,77 @@ CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, 
testDoublePageBorder)
     CPPUNIT_ASSERT_GREATER(aBorderWidthVec[2], aBorderWidthVec[3]);
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testNegativePageBorder)
+{
+    // Given a document with a top margin and a negative border distance:
+    createSwDoc();
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    SwDocShell* pDocShell = pTextDoc->GetDocShell();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    pWrtShell->Insert("test");
+    uno::Reference<beans::XPropertySet> 
xPageStyle(getStyles("PageStyles")->getByName("Standard"),
+                                                   uno::UNO_QUERY);
+    xPageStyle->setPropertyValue("TopMargin", 
uno::Any(static_cast<sal_Int32>(501))); // 284 twips
+    table::BorderLine2 aBorder;
+    aBorder.LineWidth = 159; // 90 twips
+    aBorder.OuterLineWidth = 159;
+    xPageStyle->setPropertyValue("TopBorder", uno::Any(aBorder));
+    sal_Int32 nTopBorderDistance = -646; // -366 twips
+    xPageStyle->setPropertyValue("TopBorderDistance", 
uno::Any(nTopBorderDistance));
+    nTopBorderDistance = 0;
+    xPageStyle->getPropertyValue("TopBorderDistance") >>= nTopBorderDistance;
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-646), nTopBorderDistance);
+
+    // When rendering that border:
+    std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
+
+    // Then make sure that the negative distance pushes the horizontal 
borderline down:
+    MetafileXmlDump aDumper;
+    xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 899
+    // - Actual  : 524
+    // i.e. the negative border distance was rounded up to 0 in 
lcl_CalcBorderRect().
+    // Ideally this would be 284 (top of first page) + 284 (top margin) + 366 
(border distance) =
+    // 934.
+    assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "y", "899");
+    assertXPath(pXmlDoc, "//polyline[@style='solid']/point[2]", "y", "899");
+}
+
+CPPUNIT_TEST_FIXTURE(SwCoreLayoutTest, testNegativePageBorderNoMargin)
+{
+    // Given a document with no top margin and a negative border distance:
+    createSwDoc();
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    SwDocShell* pDocShell = pTextDoc->GetDocShell();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    pWrtShell->Insert("test");
+    uno::Reference<beans::XPropertySet> 
xPageStyle(getStyles("PageStyles")->getByName("Standard"),
+                                                   uno::UNO_QUERY);
+    xPageStyle->setPropertyValue("TopMargin", 
uno::Any(static_cast<sal_Int32>(0))); // 0 twips
+    table::BorderLine2 aBorder;
+    aBorder.LineWidth = 159; // 90 twips
+    aBorder.OuterLineWidth = 159;
+    xPageStyle->setPropertyValue("TopBorder", uno::Any(aBorder));
+    sal_Int32 nTopBorderDistance = -1147; // -650 twips
+    xPageStyle->setPropertyValue("TopBorderDistance", 
uno::Any(nTopBorderDistance));
+
+    // When rendering that border:
+    std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
+
+    // Then make sure that the negative distance pushes the horizontal 
borderline down:
+    MetafileXmlDump aDumper;
+    xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 899
+    // - Actual  : 329
+    // i.e. this failed differently: the lack of top margin caused a second 
problem.
+    // Ideally this would be 284 (top of first page) + 650 (border distance) =
+    // 934.
+    assertXPath(pXmlDoc, "//polyline[@style='solid']/point[1]", "y", "899");
+    assertXPath(pXmlDoc, "//polyline[@style='solid']/point[2]", "y", "899");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index d85a8da12f18..bcd34ff459a2 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -7,6 +7,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include <swmodeltestbase.hxx>
+
+#include <queue>
+
 #include <com/sun/star/beans/NamedValue.hpp>
 #include <com/sun/star/text/XBookmarksSupplier.hpp>
 #include <com/sun/star/text/XFootnotesSupplier.hpp>
@@ -25,8 +29,9 @@
 #include <o3tl/string_view.hxx>
 #include <comphelper/propertyvalue.hxx>
 
-#include <queue>
-#include <swmodeltestbase.hxx>
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
 
 constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/extras/ooxmlexport/data/";
 
@@ -417,6 +422,39 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport)
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w15:color", "val", "008000");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorder)
+{
+    // Given a document with a negative border distance:
+    createSwDoc();
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    SwDocShell* pDocShell = pTextDoc->GetDocShell();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    pWrtShell->Insert("test");
+    uno::Reference<beans::XPropertySet> 
xPageStyle(getStyles("PageStyles")->getByName("Standard"),
+                                                   uno::UNO_QUERY);
+    xPageStyle->setPropertyValue("TopMargin", 
uno::Any(static_cast<sal_Int32>(501)));
+    table::BorderLine2 aBorder;
+    aBorder.LineWidth = 159;
+    aBorder.OuterLineWidth = 159;
+    xPageStyle->setPropertyValue("TopBorder", uno::Any(aBorder));
+    sal_Int32 nTopBorderDistance = -646;
+    xPageStyle->setPropertyValue("TopBorderDistance", 
uno::Any(nTopBorderDistance));
+
+    // When exporting to DOCX:
+    save("Office Open XML Text", maTempFile);
+    mbExported = true;
+
+    // Then make sure that the page edge -> border space is correct:
+    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
+    assertXPath(pXmlDoc, "//w:pgMar", "top", "284");
+    assertXPath(pXmlDoc, "//w:pgBorders/w:top", "sz", "36");
+    // Without the fix in place, this test would have failed with:
+    // - Expected: 28
+    // - Actual  : 0
+    // i.e. editeng::BorderDistancesToWord() mis-handled negative border 
distances.
+    assertXPath(pXmlDoc, "//w:pgBorders/w:top", "space", "28");
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testTdf148494)
 {
     loadAndSave("tdf148494.docx");
diff --git a/sw/qa/extras/ww8import/ww8import.cxx 
b/sw/qa/extras/ww8import/ww8import.cxx
index 7eb0c9f6b221..ab35fa9d7346 100644
--- a/sw/qa/extras/ww8import/ww8import.cxx
+++ b/sw/qa/extras/ww8import/ww8import.cxx
@@ -182,7 +182,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf121734)
         for (auto eLine : { SvxBoxItemLine::TOP, SvxBoxItemLine::BOTTOM,
                             SvxBoxItemLine::LEFT, SvxBoxItemLine::RIGHT })
         {
-            CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), pBox->GetDistance(eLine));
+            CPPUNIT_ASSERT_EQUAL(sal_Int16(0), pBox->GetDistance(eLine));
             CPPUNIT_ASSERT(!pBox->GetLine(eLine));
         }
     }
@@ -251,7 +251,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf122425_1)
         for (auto eLine : { SvxBoxItemLine::TOP, SvxBoxItemLine::BOTTOM,
                             SvxBoxItemLine::LEFT, SvxBoxItemLine::RIGHT })
         {
-            CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), pBox->GetDistance(eLine));
+            CPPUNIT_ASSERT_EQUAL(sal_Int16(0), pBox->GetDistance(eLine));
             CPPUNIT_ASSERT(!pBox->GetLine(eLine));
         }
     }
diff --git a/sw/source/core/layout/paintfrm.cxx 
b/sw/source/core/layout/paintfrm.cxx
index 560dd0f659bc..d470b1ae030f 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -1291,10 +1291,10 @@ static void lcl_CalcBorderRect( SwRect &rRect, const 
SwFrame *pFrame,
 
         const SvxBoxItem &rBox = rAttrs.GetBox();
         const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)();
-        if ( bTop )
+        if ( bTop || rBox.GetTop() )
         {
             SwTwips nDiff = rBox.GetTop() ?
-                rBox.CalcLineSpace( SvxBoxItemLine::TOP ) :
+                rBox.CalcLineSpace( SvxBoxItemLine::TOP, 
/*bEvenIfNoLine=*/false, /*bAllowNegative=*/true ) :
                 rBox.GetDistance( SvxBoxItemLine::TOP );
             if( nDiff )
                 (rRect.*fnRect->fnSubTop)( nDiff );
diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx
index faa25df0ec62..dafba458ec05 100644
--- a/sw/source/filter/ww8/wrtww8.cxx
+++ b/sw/source/filter/ww8/wrtww8.cxx
@@ -2594,7 +2594,7 @@ void WW8AttributeOutput::TableCellBorders(
             sal_uInt8 nSideBits[4] = {0, 0, 0, 0}; // 0001:top, 0010:left, 
0100:bottom, 1000:right
             for ( int i = 0; i < 4; ++i )  // sides: top, left, bottom, right
             {
-                nMargin[i] = std::min(sal_uInt16(31680), 
pLastBox->GetDistance( aBorders[i] ));
+                nMargin[i] = std::min(sal_Int16(31680), pLastBox->GetDistance( 
aBorders[i] ));
                 if ( nMargin[i] == nDefaultMargin[i] )
                     continue;
 
diff --git a/sw/source/uibase/shells/tabsh.cxx 
b/sw/source/uibase/shells/tabsh.cxx
index 9dbb1b689e8e..3cbea6b77389 100644
--- a/sw/source/uibase/shells/tabsh.cxx
+++ b/sw/source/uibase/shells/tabsh.cxx
@@ -512,7 +512,7 @@ void SwTableShell::Execute(SfxRequest &rReq)
             if ( pBoxItem )
             {
                 aBox.reset(pBoxItem->Clone());
-                sal_uInt16 nDefValue = MIN_BORDER_DIST;
+                sal_Int16 nDefValue = MIN_BORDER_DIST;
                 if ( !rReq.IsAPI() )
                     nDefValue = 55;
                 if (!rReq.IsAPI() || aBox->GetSmallestDistance() < 
MIN_BORDER_DIST)
diff --git a/writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx 
b/writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx
index ae32ce1e4dc7..cc651d224736 100644
--- a/writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx
+++ b/writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx
@@ -16,6 +16,7 @@
 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
 
 using namespace ::com::sun::star;
 
@@ -108,6 +109,65 @@ CPPUNIT_TEST_FIXTURE(Test, testTableNegativeVerticalPos)
     // i.e. this was imported as a plain table, resulting in a 0 top margin (y 
pos too large).
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), xDrawPage->getCount());
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorder)
+{
+    // Given a document with a top margin and a border which has more spacing 
than the margin:
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"negative-page-border.docx";
+
+    // When loading that document:
+    getComponent() = loadFromDesktop(aURL);
+
+    // Then make sure that the border distance is negative, so it can appear 
at the correct
+    // position:
+    uno::Reference<style::XStyleFamiliesSupplier> 
xStyleFamiliesSupplier(getComponent(),
+                                                                         
uno::UNO_QUERY);
+    uno::Reference<container::XNameAccess> xStyleFamilies
+        = xStyleFamiliesSupplier->getStyleFamilies();
+    uno::Reference<container::XNameAccess> 
xStyleFamily(xStyleFamilies->getByName("PageStyles"),
+                                                        uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> 
xStyle(xStyleFamily->getByName("Standard"), uno::UNO_QUERY);
+    auto nTopMargin = xStyle->getPropertyValue("TopMargin").get<sal_Int32>();
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(501), nTopMargin);
+    auto aTopBorder = 
xStyle->getPropertyValue("TopBorder").get<table::BorderLine2>();
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(159), aTopBorder.LineWidth);
+    auto nTopBorderDistance = 
xStyle->getPropertyValue("TopBorderDistance").get<sal_Int32>();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: -646
+    // - Actual  : 0
+    // i.e. the border negative distance was lost.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-646), nTopBorderDistance);
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorderNoMargin)
+{
+    // Given a document with no top margin and a border which has spacing:
+    OUString aURL
+        = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"negative-page-border-no-margin.docx";
+
+    // When loading that document:
+    getComponent() = loadFromDesktop(aURL);
+
+    // Then make sure that the border distance is negative, so it can appear 
at the correct
+    // position:
+    uno::Reference<style::XStyleFamiliesSupplier> 
xStyleFamiliesSupplier(getComponent(),
+                                                                         
uno::UNO_QUERY);
+    uno::Reference<container::XNameAccess> xStyleFamilies
+        = xStyleFamiliesSupplier->getStyleFamilies();
+    uno::Reference<container::XNameAccess> 
xStyleFamily(xStyleFamilies->getByName("PageStyles"),
+                                                        uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> 
xStyle(xStyleFamily->getByName("Standard"), uno::UNO_QUERY);
+    auto nTopMargin = xStyle->getPropertyValue("TopMargin").get<sal_Int32>();
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), nTopMargin);
+    auto aTopBorder = 
xStyle->getPropertyValue("TopBorder").get<table::BorderLine2>();
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(159), aTopBorder.LineWidth);
+    auto nTopBorderDistance = 
xStyle->getPropertyValue("TopBorderDistance").get<sal_Int32>();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: -1147
+    // - Actual  : 0
+    // i.e. the border negative distance was lost.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-1147), nTopBorderDistance);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git 
a/writerfilter/qa/cppunittests/dmapper/data/negative-page-border-no-margin.docx 
b/writerfilter/qa/cppunittests/dmapper/data/negative-page-border-no-margin.docx
new file mode 100644
index 000000000000..8bd464a9ea6c
Binary files /dev/null and 
b/writerfilter/qa/cppunittests/dmapper/data/negative-page-border-no-margin.docx 
differ
diff --git 
a/writerfilter/qa/cppunittests/dmapper/data/negative-page-border.docx 
b/writerfilter/qa/cppunittests/dmapper/data/negative-page-border.docx
new file mode 100644
index 000000000000..878ba1e7899b
Binary files /dev/null and 
b/writerfilter/qa/cppunittests/dmapper/data/negative-page-border.docx differ
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx 
b/writerfilter/source/dmapper/PropertyMap.cxx
index e1814a8ce1d5..645cc8c00c86 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -670,7 +670,7 @@ void SectionPropertyMap::SetBorderDistance( const 
uno::Reference< beans::XProper
     sal_Int32 nMargin = 0;
     aMargin >>= nMargin;
     editeng::BorderDistanceFromWord(eOffsetFrom == BorderOffsetFrom::Edge, 
nMargin, nDistance,
-                                    nLineWidth);
+                                    nLineWidth, 
/*bAllowNegativeBorderDistance=*/true);
 
     if (eOffsetFrom == BorderOffsetFrom::Edge)
     {
commit f4b6cee77e1725837f9a6044fec0b561c7049c3b
Author:     Stephan Bergmann <sberg...@redhat.com>
AuthorDate: Fri Jun 3 10:22:43 2022 +0200
Commit:     Stephan Bergmann <sberg...@redhat.com>
CommitDate: Tue Jun 7 08:45:31 2022 +0200

    Use o3tl::make_unsigned, OString::getLength() is guaranteed to be 
non-negative
    
    Change-Id: Ibddd4f2b3d8680c94fd0ab45f6ed6204215c6009
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135339
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <sberg...@redhat.com>

diff --git a/desktop/source/deployment/dp_persmap.cxx 
b/desktop/source/deployment/dp_persmap.cxx
index 652e5d2ea5ff..8b032fffb96e 100644
--- a/desktop/source/deployment/dp_persmap.cxx
+++ b/desktop/source/deployment/dp_persmap.cxx
@@ -23,6 +23,7 @@
 
 #include <dp_misc.h>
 #include <dp_persmap.h>
+#include <o3tl/safeint.hxx>
 #include <rtl/byteseq.hxx>
 #include <rtl/strbuf.hxx>
 #include <sal/log.hxx>
@@ -243,13 +244,13 @@ void PersistentMap::flush()
         const OString aKeyString = encodeString( entry.first);
         const sal_Int32 nKeyLen = aKeyString.getLength();
         m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten);
-        OSL_ASSERT( nKeyLen == static_cast<sal_Int32>(nBytesWritten));
+        OSL_ASSERT( o3tl::make_unsigned(nKeyLen) == nBytesWritten);
         m_MapFile.write( "\n", 1, nBytesWritten);
         // write line for value
         const OString& rValString = encodeString( entry.second);
         const sal_Int32 nValLen = rValString.getLength();
         m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten);
-        OSL_ASSERT( nValLen == static_cast<sal_Int32>(nBytesWritten));
+        OSL_ASSERT( o3tl::make_unsigned(nValLen) == nBytesWritten);
         m_MapFile.write( "\n", 1, nBytesWritten);
     }
 

Reply via email to