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) << "'");