From 5cd2dba6c613d0b4799a0c2ffe0cd72bc7cce3c2 Mon Sep 17 00:00:00 2001 From: Justin Luth Date: Wed, 10 Jul 2024 14:57:44 -0400 Subject: tdf#160976 rtfexport: export first header as well as default header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LO has never handled exporting "different first header" ever since it was introduced in LO 4.0-ish. In 24.2, quikee change the RTF import to default to putting headers into "different first header" instead of "First Page Style" with commit 4b0fa253a4540f5461397815d290586f9ddabe61, so this existing problem has become more pronounced. Unhandled still is any attempt to handle different even/odd pages. This patch also reduces the number of \titlepg duplicates. make CppunitTest_sw_rtfexport8 \ CPPUNIT_TEST_NAME=testTdf160976_headerFooter make CppunitTest_sw_rtfexport8 \ CPPUNIT_TEST_NAME=testTdf160976_headerFooter2 make CppunitTest_sw_rtfexport8 \ CPPUNIT_TEST_NAME=testTdf160976_headerFooter3 Change-Id: Idc12c57b69ff83cbdcbac3f6a469fadd8f26fe2f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170326 Reviewed-by: Tomaž Vajngerl Reviewed-by: Justin Luth Tested-by: Jenkins (cherry picked from commit 0abe0f62b9913ceb77c7ac067f1074ce0395ab93) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170352 Reviewed-by: Xisco Fauli Signed-off-by: Xisco Fauli Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170486 --- .../rtfexport/data/tdf160976_headerFooter.odt | Bin 0 -> 11991 bytes .../rtfexport/data/tdf160976_headerFooter2.odt | Bin 0 -> 12352 bytes .../rtfexport/data/tdf160976_headerFooter3.odt | Bin 0 -> 12451 bytes sw/qa/extras/rtfexport/rtfexport8.cxx | 55 +++++++++++++++++++++ sw/source/filter/ww8/rtfexport.cxx | 53 +++++++++++++------- sw/source/filter/ww8/rtfexport.hxx | 3 +- 6 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 sw/qa/extras/rtfexport/data/tdf160976_headerFooter.odt create mode 100644 sw/qa/extras/rtfexport/data/tdf160976_headerFooter2.odt create mode 100644 sw/qa/extras/rtfexport/data/tdf160976_headerFooter3.odt (limited to 'sw') diff --git a/sw/qa/extras/rtfexport/data/tdf160976_headerFooter.odt b/sw/qa/extras/rtfexport/data/tdf160976_headerFooter.odt new file mode 100644 index 000000000000..d2b692613684 Binary files /dev/null and b/sw/qa/extras/rtfexport/data/tdf160976_headerFooter.odt differ diff --git a/sw/qa/extras/rtfexport/data/tdf160976_headerFooter2.odt b/sw/qa/extras/rtfexport/data/tdf160976_headerFooter2.odt new file mode 100644 index 000000000000..eb6fd0f1be8a Binary files /dev/null and b/sw/qa/extras/rtfexport/data/tdf160976_headerFooter2.odt differ diff --git a/sw/qa/extras/rtfexport/data/tdf160976_headerFooter3.odt b/sw/qa/extras/rtfexport/data/tdf160976_headerFooter3.odt new file mode 100644 index 000000000000..43077a273fe0 Binary files /dev/null and b/sw/qa/extras/rtfexport/data/tdf160976_headerFooter3.odt differ diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx b/sw/qa/extras/rtfexport/rtfexport8.cxx index 7b303f3fd3da..743d652ce31c 100644 --- a/sw/qa/extras/rtfexport/rtfexport8.cxx +++ b/sw/qa/extras/rtfexport/rtfexport8.cxx @@ -349,6 +349,61 @@ DECLARE_RTFEXPORT_TEST(testTdf158982, "tdf158982.rtf") getProperty(xPara, "ParaLeftMargin")); } +CPPUNIT_TEST_FIXTURE(Test, testTdf160976_headerFooter) +{ + // given a nasty ODT with first-even-odd page styles, emulate using RTF's abilities + auto verify = [this](bool bIsExported = false) { + // Sanity check - always good to test when dealing with page styles and breaks. + CPPUNIT_ASSERT_EQUAL(3, getPages()); + + CPPUNIT_ASSERT_EQUAL(u"First page first footer"_ustr, + parseDump("/root/page[1]/footer/txt"_ostr)); + CPPUNIT_ASSERT_EQUAL(u"First Left"_ustr, parseDump("/root/page[2]/footer/txt"_ostr)); + if (!bIsExported) + CPPUNIT_ASSERT_EQUAL(u"First Right"_ustr, parseDump("/root/page[3]/footer/txt"_ostr)); + }; + createSwDoc("tdf160976_headerFooter.odt"); + verify(); + saveAndReload(mpFilter); + verify(/*IsExported*/ true); + // note: an unexpected header surfaces on page 3. +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf160976_headerFooter2) +{ + // given a typical ODT with first-follow page styles, emulate using RTF's abilities + auto verify = [this]() { + // Sanity check - always good to test when dealing with page styles and breaks. + CPPUNIT_ASSERT_EQUAL(3, getPages()); + + CPPUNIT_ASSERT_EQUAL(u"First page first footer"_ustr, + parseDump("/root/page[1]/footer/txt"_ostr)); + CPPUNIT_ASSERT_EQUAL(u"First page footer"_ustr, parseDump("/root/page[2]/footer/txt"_ostr)); + CPPUNIT_ASSERT_EQUAL(u"Default footer"_ustr, parseDump("/root/page[3]/footer/txt"_ostr)); + }; + createSwDoc("tdf160976_headerFooter2.odt"); + verify(); + saveAndReload(mpFilter); + verify(); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf160976_headerFooter3) +{ + // given a simple ODT with typical "different first" on the default page style + auto verify = [this]() { + // Sanity check - always good to test when dealing with page styles and breaks. + CPPUNIT_ASSERT_EQUAL(3, getPages()); + + CPPUNIT_ASSERT_EQUAL(u"First page footer"_ustr, parseDump("/root/page[1]/footer/txt"_ostr)); + CPPUNIT_ASSERT_EQUAL(u"Default footer"_ustr, parseDump("/root/page[2]/footer/txt"_ostr)); + CPPUNIT_ASSERT_EQUAL(u"Default footer"_ustr, parseDump("/root/page[3]/footer/txt"_ostr)); + }; + createSwDoc("tdf160976_headerFooter3.odt"); + verify(); + saveAndReload(mpFilter); + verify(); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/ww8/rtfexport.cxx b/sw/source/filter/ww8/rtfexport.cxx index 87a8a2e8e262..15c004793970 100644 --- a/sw/source/filter/ww8/rtfexport.cxx +++ b/sw/source/filter/ww8/rtfexport.cxx @@ -1425,26 +1425,29 @@ void RtfExport::OutPageDescription(const SwPageDesc& rPgDsc, bool bCheckForFirst m_pFirstPageItemSet = nullptr; m_bOutPageDescs = false; + const bool bFakeFirst = m_pCurrentPageDesc != &rPgDsc; + if (bFakeFirst || !m_pCurrentPageDesc->IsFirstShared()) + Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_TITLEPG); + // normal header / footer (without a style) const SfxPoolItem* pItem; if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_HEADER, false, &pItem) == SfxItemState::SET) - WriteHeaderFooter(*pItem, true); + WriteHeaderFooter(*pItem, /*Header*/ true, /*AsTitlePg*/ false, /*WriteFirst*/ !bFakeFirst); if (m_pCurrentPageDesc->GetLeft().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem) == SfxItemState::SET) - WriteHeaderFooter(*pItem, false); + WriteHeaderFooter(*pItem, false, /*AsTitlePg*/ false, /*WriteFirst*/ !bFakeFirst); // title page - if (m_pCurrentPageDesc != &rPgDsc) + if (bFakeFirst) { - Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_TITLEPG); m_pCurrentPageDesc = &rPgDsc; if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_HEADER, false, &pItem) == SfxItemState::SET) - WriteHeaderFooter(*pItem, true); + WriteHeaderFooter(*pItem, /*Header*/ true, /*AsTitlePg*/ true, /*WriteFirst*/ false); if (m_pCurrentPageDesc->GetMaster().GetAttrSet().GetItemState(RES_FOOTER, false, &pItem) == SfxItemState::SET) - WriteHeaderFooter(*pItem, false); + WriteHeaderFooter(*pItem, /*Header*/ false, /*AsTitlePg*/ true, /*WriteFirst*/ false); } // numbering type @@ -1455,8 +1458,18 @@ void RtfExport::OutPageDescription(const SwPageDesc& rPgDsc, bool bCheckForFirst SAL_INFO("sw.rtf", __func__ << " end"); } -void RtfExport::WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader) +/** WriteHeaderFooter: used to write the initial header and footers + * @param bHeader: true for a header, false for a footer. + * @param bAsTitlePg: used to emulate a first-follow page style linking. + * Set to true to only write this header as if it were a "first header". + * @param bWriteFirst: used to determine whether to write a non-shared first header as the header. + * Set to false if bAsTitlePg will be used to define the "first header". + */ +void RtfExport::WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader, bool bAsTitlePg, + bool bWriteFirst) { + assert(!bAsTitlePg || !bWriteFirst); + if (bHeader) { const auto& rHeader = static_cast(rItem); @@ -1472,21 +1485,25 @@ void RtfExport::WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader) SAL_INFO("sw.rtf", __func__ << " start"); - const char* pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADER : OOO_STRING_SVTOOLS_RTF_FOOTER); /* is this a title page? */ - if ((m_pCurrentPageDesc->GetFollow() && m_pCurrentPageDesc->GetFollow() != m_pCurrentPageDesc) - || !m_pCurrentPageDesc->IsFirstShared()) + if (bAsTitlePg || (bWriteFirst && !m_pCurrentPageDesc->IsFirstShared())) { - Strm().WriteOString(OOO_STRING_SVTOOLS_RTF_TITLEPG); - pStr = (bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERF : OOO_STRING_SVTOOLS_RTF_FOOTERF); + auto pStr = bHeader ? OOO_STRING_SVTOOLS_RTF_HEADERF : OOO_STRING_SVTOOLS_RTF_FOOTERF; + Strm().WriteChar('{').WriteOString(pStr); + WriteHeaderFooterText(m_pCurrentPageDesc->IsFirstShared() + ? m_pCurrentPageDesc->GetMaster() + : m_pCurrentPageDesc->GetFirstMaster(), + bHeader); + Strm().WriteChar('}'); } - Strm().WriteChar('{').WriteOString(pStr); - WriteHeaderFooterText(m_pCurrentPageDesc->IsFirstShared() - ? m_pCurrentPageDesc->GetMaster() - : m_pCurrentPageDesc->GetFirstMaster(), - bHeader); - Strm().WriteChar('}'); + if (!bAsTitlePg) + { + auto pStr = bHeader ? OOO_STRING_SVTOOLS_RTF_HEADER : OOO_STRING_SVTOOLS_RTF_FOOTER; + Strm().WriteChar('{').WriteOString(pStr); + WriteHeaderFooterText(m_pCurrentPageDesc->GetMaster(), bHeader); + Strm().WriteChar('}'); + } SAL_INFO("sw.rtf", __func__ << " end"); } diff --git a/sw/source/filter/ww8/rtfexport.hxx b/sw/source/filter/ww8/rtfexport.hxx index 11f3f0471f23..faf6aeab36bf 100644 --- a/sw/source/filter/ww8/rtfexport.hxx +++ b/sw/source/filter/ww8/rtfexport.hxx @@ -218,7 +218,8 @@ private: void WriteDocVars(); /// This is necessary to have the numbering table ready before the main text is being processed. void BuildNumbering(); - void WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader); + void WriteHeaderFooter(const SfxPoolItem& rItem, bool bHeader, bool bAsTitlePg, + bool bWriteFirst); void WriteHeaderFooter(const SwFrameFormat& rFormat, bool bHeader, const char* pStr, bool bTitlepg = false); -- cgit