sw/qa/extras/rtfexport/rtfexport3.cxx                             |   58 
++++++++++
 sw/qa/filter/ww8/ww8.cxx                                          |    2 
 sw/source/filter/ww8/rtfattributeoutput.cxx                       |   46 
++++++-
 sw/source/filter/ww8/rtfattributeoutput.hxx                       |    3 
 writerfilter/qa/cppunittests/rtftok/data/negative-page-border.rtf |    7 +
 writerfilter/qa/cppunittests/rtftok/rtfdispatchvalue.cxx          |   30 +++++
 writerfilter/source/rtftok/rtfdispatchvalue.cxx                   |   13 ++
 7 files changed, 148 insertions(+), 11 deletions(-)

New commits:
commit d4123356c61db269651e950a0a2cc93e6d801c90
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Jun 8 17:05:42 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Jun 8 17:46:58 2022 +0200

    RTF filter: allow measuring page borders from the edge of the page
    
    This is similar to commit 51942eafdb4439559b6d59f3becd4afab45277f0 (DOC
    import: allow negative page border distances, 2022-06-08), except here
    we map \pgbrdropt's 5th bit to the "from page edge" bool, and then the
    rest of the import works already after the DOCX fixes.
    
    Similarly, the export has to map the "from page edge" bool to \pgbrdropt
    and has to call into editeng::BorderDistancesToWord(), but the rest of
    the process works after the DOCX fixes.
    
    Change-Id: Ic88f1ab17ac169025c38790ffa895748df0a76c0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135502
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/extras/rtfexport/rtfexport3.cxx 
b/sw/qa/extras/rtfexport/rtfexport3.cxx
index 665682c7532c..bd8a1c8dc795 100644
--- a/sw/qa/extras/rtfexport/rtfexport3.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport3.cxx
@@ -22,6 +22,12 @@
 #include <comphelper/sequenceashashmap.hxx>
 #include <tools/UnitConversion.hxx>
 
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <fmtpdsc.hxx>
+#include <IDocumentContentOperations.hxx>
+
 using namespace css;
 
 class Test : public SwModelTestBase
@@ -434,6 +440,58 @@ CPPUNIT_TEST_FIXTURE(Test, testRtlGutter)
     verify();
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorder)
+{
+    {
+        // Given a document with a top margin and a border which has more 
spacing than the margin on
+        // its 2nd page:
+        createSwDoc();
+        SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+        SwDocShell* pDocShell = pTextDoc->GetDocShell();
+        SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+        pWrtShell->Insert("first");
+        pWrtShell->SplitNode();
+        pWrtShell->Insert("second");
+        SwPageDesc* pDesc = pWrtShell->FindPageDescByName("Left Page", true);
+        SwPaM aPaM(*pWrtShell->GetCursor()->GetPoint());
+        SwFormatPageDesc aFormatPageDesc(pDesc);
+        
pDocShell->GetDoc()->getIDocumentContentOperations().InsertPoolItem(aPaM, 
aFormatPageDesc);
+        uno::Reference<beans::XPropertySet> xPageStyle(
+            getStyles("PageStyles")->getByName("Left Page"), 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));
+        pDocShell->GetDoc()->dumpAsXml();
+    }
+
+    // When saving that document to RTF:
+    reload(mpFilter, "negative-page-border.rtf");
+
+    // Then make sure that the border distance is negative, so the first line 
of body text appears
+    // on top of the page border:
+    SwXTextDocument* pTextDoc = 
dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    SwDocShell* pDocShell = pTextDoc->GetDocShell();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    pWrtShell->Down(/*bSelect=*/false);
+    OUString aPageStyle = pWrtShell->GetCurPageStyle();
+    uno::Reference<beans::XPropertySet> 
xPageStyle(getStyles("PageStyles")->getByName(aPageStyle),
+                                                   uno::UNO_QUERY);
+    auto nTopMargin = 
xPageStyle->getPropertyValue("TopMargin").get<sal_Int32>();
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(501), nTopMargin);
+    auto aTopBorder = 
xPageStyle->getPropertyValue("TopBorder").get<table::BorderLine2>();
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(159), aTopBorder.LineWidth);
+    auto nTopBorderDistance = 
xPageStyle->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_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/filter/ww8/ww8.cxx b/sw/qa/filter/ww8/ww8.cxx
index eb735b16a746..7ccb1299769e 100644
--- a/sw/qa/filter/ww8/ww8.cxx
+++ b/sw/qa/filter/ww8/ww8.cxx
@@ -26,7 +26,7 @@ class Test : public SwModelTestBase
 {
 };
 
-CPPUNIT_TEST_FIXTURE(Test, testSwAttrSet)
+CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorderDocImport)
 {
     // Given a document with a border distance that is larger than the margin, 
when loading that
     // document:
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx 
b/sw/source/filter/ww8/rtfattributeoutput.cxx
index 20e8e6fc907c..9d634e73af7a 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -1281,22 +1281,32 @@ void RtfAttributeOutput::SectionPageBorders(const 
SwFrameFormat* pFormat,
                                             const SwFrameFormat* 
/*pFirstPageFormat*/)
 {
     const SvxBoxItem& rBox = pFormat->GetBox();
+    editeng::WordBorderDistances aDistances;
+    editeng::BorderDistancesToWord(rBox, m_aPageMargins, aDistances);
+
+    if (aDistances.bFromEdge)
+    {
+        sal_uInt16 nOpt = (1 << 5);
+        m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_PGBRDROPT);
+        m_aSectionBreaks.append(static_cast<sal_Int32>(nOpt));
+    }
+
     const editeng::SvxBorderLine* pLine = rBox.GetTop();
     if (pLine)
-        m_aSectionBreaks.append(OutBorderLine(m_rExport, pLine, 
OOO_STRING_SVTOOLS_RTF_PGBRDRT,
-                                              
rBox.GetDistance(SvxBoxItemLine::TOP)));
+        m_aSectionBreaks.append(
+            OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRT, 
aDistances.nTop));
     pLine = rBox.GetBottom();
     if (pLine)
-        m_aSectionBreaks.append(OutBorderLine(m_rExport, pLine, 
OOO_STRING_SVTOOLS_RTF_PGBRDRB,
-                                              
rBox.GetDistance(SvxBoxItemLine::BOTTOM)));
+        m_aSectionBreaks.append(
+            OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRB, 
aDistances.nBottom));
     pLine = rBox.GetLeft();
     if (pLine)
-        m_aSectionBreaks.append(OutBorderLine(m_rExport, pLine, 
OOO_STRING_SVTOOLS_RTF_PGBRDRL,
-                                              
rBox.GetDistance(SvxBoxItemLine::LEFT)));
+        m_aSectionBreaks.append(
+            OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRL, 
aDistances.nLeft));
     pLine = rBox.GetRight();
     if (pLine)
-        m_aSectionBreaks.append(OutBorderLine(m_rExport, pLine, 
OOO_STRING_SVTOOLS_RTF_PGBRDRR,
-                                              
rBox.GetDistance(SvxBoxItemLine::RIGHT)));
+        m_aSectionBreaks.append(
+            OutBorderLine(m_rExport, pLine, OOO_STRING_SVTOOLS_RTF_PGBRDRR, 
aDistances.nRight));
 }
 
 void RtfAttributeOutput::SectionBiDi(bool bBiDi)
@@ -3166,15 +3176,29 @@ void RtfAttributeOutput::FormatLRSpace(const 
SvxLRSpaceItem& rLRSpace)
     {
         if (m_rExport.m_bOutPageDescs)
         {
+            m_aPageMargins.nLeft = 0;
+            m_aPageMargins.nRight = 0;
+
+            if (const SvxBoxItem* pBoxItem = m_rExport.HasItem(RES_BOX))
+            {
+                m_aPageMargins.nLeft
+                    = pBoxItem->CalcLineSpace(SvxBoxItemLine::LEFT, 
/*bEvenIfNoLine*/ true);
+                m_aPageMargins.nRight
+                    = pBoxItem->CalcLineSpace(SvxBoxItemLine::RIGHT, 
/*bEvenIfNoLine*/ true);
+            }
+
+            m_aPageMargins.nLeft += 
sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
+            m_aPageMargins.nRight += 
sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
+
             if (rLRSpace.GetLeft())
             {
                 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGLSXN);
-                
m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetLeft()));
+                
m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nLeft));
             }
             if (rLRSpace.GetRight())
             {
                 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGRSXN);
-                
m_aSectionBreaks.append(static_cast<sal_Int32>(rLRSpace.GetRight()));
+                
m_aSectionBreaks.append(static_cast<sal_Int32>(m_aPageMargins.nRight));
             }
             if (rLRSpace.GetGutterMargin())
             {
@@ -3233,6 +3257,7 @@ void RtfAttributeOutput::FormatULSpace(const 
SvxULSpaceItem& rULSpace)
             {
                 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGTSXN);
                 
m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaTop));
+                m_aPageMargins.nTop = aDistances.m_DyaTop;
             }
             if (aDistances.HasHeader())
             {
@@ -3244,6 +3269,7 @@ void RtfAttributeOutput::FormatULSpace(const 
SvxULSpaceItem& rULSpace)
             {
                 m_aSectionBreaks.append(OOO_STRING_SVTOOLS_RTF_MARGBSXN);
                 
m_aSectionBreaks.append(static_cast<sal_Int32>(aDistances.m_DyaBottom));
+                m_aPageMargins.nBottom = aDistances.m_DyaBottom;
             }
             if (aDistances.HasFooter())
             {
diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx 
b/sw/source/filter/ww8/rtfattributeoutput.hxx
index aedef264b7d2..17de105ed4f6 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.hxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.hxx
@@ -29,6 +29,7 @@
 #include <wrtswtbl.hxx>
 
 #include <rtl/strbuf.hxx>
+#include <editeng/boxitem.hxx>
 
 #include <optional>
 
@@ -640,6 +641,8 @@ private:
     /// If m_bParaBeforeAutoSpacing is set, value of \sa.
     sal_Int32 m_nParaAfterSpacing;
 
+    editeng::WordPageMargins m_aPageMargins;
+
 public:
     explicit RtfAttributeOutput(RtfExport& rExport);
 
diff --git a/writerfilter/qa/cppunittests/rtftok/data/negative-page-border.rtf 
b/writerfilter/qa/cppunittests/rtftok/data/negative-page-border.rtf
new file mode 100644
index 000000000000..e5bec712a60e
--- /dev/null
+++ b/writerfilter/qa/cppunittests/rtftok/data/negative-page-border.rtf
@@ -0,0 +1,7 @@
+{\rtf1
+\paperw11906\paperh16838\margl1134\margr1134\margt284\margb1134
+\sectd\pgbrdropt32\pgbrdrt\brdrs\brdrw90\brsp560 
\pgbrdrl\brdrs\brdrw90\brsp560 \pgbrdrb\brdrs\brdrw90\brsp560 
\pgbrdrr\brdrs\brdrw90\brsp560
+\pard\plain
+In this example, the page top margin (0.5cm) is the same as the border top 
margin (0.5 cm).
+\par
+}
diff --git a/writerfilter/qa/cppunittests/rtftok/rtfdispatchvalue.cxx 
b/writerfilter/qa/cppunittests/rtftok/rtfdispatchvalue.cxx
index 662e65d75166..4479a0c3cbd3 100644
--- a/writerfilter/qa/cppunittests/rtftok/rtfdispatchvalue.cxx
+++ b/writerfilter/qa/cppunittests/rtftok/rtfdispatchvalue.cxx
@@ -13,6 +13,7 @@
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/frame/Desktop.hpp>
 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
 
 using namespace ::com::sun::star;
 
@@ -72,6 +73,35 @@ CPPUNIT_TEST_FIXTURE(Test, testFollowStyle)
     // i.e. \snext was ignored.
     CPPUNIT_ASSERT_EQUAL(OUString("Standard"), aFollowStyle);
 }
+
+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.rtf";
+
+    // 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  : 342
+    // i.e. the border negative distance was lost.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(-646), nTopBorderDistance);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/rtftok/rtfdispatchvalue.cxx 
b/writerfilter/source/rtftok/rtfdispatchvalue.cxx
index eb5370223aaa..a77e22a09ea5 100644
--- a/writerfilter/source/rtftok/rtfdispatchvalue.cxx
+++ b/writerfilter/source/rtftok/rtfdispatchvalue.cxx
@@ -1803,6 +1803,19 @@ RTFError RTFDocumentImpl::dispatchValue(RTFKeyword 
nKeyword, int nParam)
             }
         }
         break;
+        case RTFKeyword::PGBRDROPT:
+        {
+            sal_Int16 nOffsetFrom = (nParam & 0xe0) >> 5;
+            bool bFromEdge = nOffsetFrom == 1;
+            if (bFromEdge)
+            {
+                Id nId = NS_ooxml::LN_Value_doc_ST_PageBorderOffset_page;
+                putNestedAttribute(m_aStates.top().getSectionSprms(),
+                                   NS_ooxml::LN_EG_SectPrContents_pgBorders,
+                                   NS_ooxml::LN_CT_PageBorders_offsetFrom, new 
RTFValue(nId));
+            }
+        }
+        break;
         default:
         {
             SAL_INFO("writerfilter", "TODO handle value '" << 
keywordToString(nKeyword) << "'");

Reply via email to