sw/qa/extras/rtfexport/data/tdf160976_headerFooter.odt |binary sw/qa/extras/rtfexport/data/tdf160976_headerFooter2.odt |binary sw/qa/extras/rtfexport/data/tdf160976_headerFooter3.odt |binary 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(-)
New commits: commit 0abe0f62b9913ceb77c7ac067f1074ce0395ab93 Author: Justin Luth <jl...@mail.com> AuthorDate: Wed Jul 10 14:57:44 2024 -0400 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Thu Jul 11 09:02:20 2024 +0200 tdf#160976 rtfexport: export first header as well as default header 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 itlepg 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 <qui...@gmail.com> Reviewed-by: Justin Luth <jl...@mail.com> Tested-by: Jenkins 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 3f013605d625..28b755cd8f4a 100644 --- a/sw/qa/extras/rtfexport/rtfexport8.cxx +++ b/sw/qa/extras/rtfexport/rtfexport8.cxx @@ -416,6 +416,61 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf159824_gradientAngle4) verify(); } +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(); +} + CPPUNIT_TEST_FIXTURE(Test, testTdf158830) { auto verify = [this]() { diff --git a/sw/source/filter/ww8/rtfexport.cxx b/sw/source/filter/ww8/rtfexport.cxx index 41a9cc4b9908..eeb560e540ea 100644 --- a/sw/source/filter/ww8/rtfexport.cxx +++ b/sw/source/filter/ww8/rtfexport.cxx @@ -1494,26 +1494,29 @@ void RtfExport::OutPageDescription(const SwPageDesc& rPgDsc) 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 @@ -1524,8 +1527,18 @@ void RtfExport::OutPageDescription(const SwPageDesc& rPgDsc) 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<const SwFormatHeader&>(rItem); @@ -1541,21 +1554,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 9f56549b7ea0..3769808a3489 100644 --- a/sw/source/filter/ww8/rtfexport.hxx +++ b/sw/source/filter/ww8/rtfexport.hxx @@ -226,7 +226,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);