sw/qa/extras/ooxmlexport/data/tdf162916_nastyTOC.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport10.cxx            |   53 ++++++++++++++
 sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx  |   65 ++++++++++++++----
 3 files changed, 107 insertions(+), 11 deletions(-)

New commits:
commit 9112a4a4cc700569f8321b8191abb4238697a404
Author:     Justin Luth <justin.l...@collabora.com>
AuthorDate: Tue Sep 24 17:29:59 2024 -0400
Commit:     Justin Luth <jl...@mail.com>
CommitDate: Wed Sep 25 02:06:15 2024 +0200

    tdf#162916 writerfilter TOC: import "no page number" flag
    
    The field command "TOC 
 2-2" means that
    level 2 entries should not display the page number,
    so in LO just exclude those portions from the definitions.
    
    The impact only becomes noticable when you update the TOC,
    because the initial content is just read from document.xml.
    
    The export part was already handled at LO initial import,
    since DOC format needed the proper sFieldCmd string.
    DOC format already imports this just fine.
    
    Note that the unit test also specifies 'TOC \o "2-3"'
    which means that the first level should not show,
    but I don't think LO supports skipping earlier levels.
    
    make CppunitTest_sw_ooxmlexport10 \
        CPPUNIT_TEST_NAME=testTdf162916_nastyTOC
    
    Change-Id: I9661c56c84bcd28bf1664d808a0e9c38051cf67b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173885
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <jl...@mail.com>

diff --git a/sw/qa/extras/ooxmlexport/data/tdf162916_nastyTOC.docx 
b/sw/qa/extras/ooxmlexport/data/tdf162916_nastyTOC.docx
new file mode 100644
index 000000000000..34ecd5c9b252
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf162916_nastyTOC.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
index a50c853d7713..a93c38a47306 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
@@ -35,6 +35,8 @@
 #include <officecfg/Office/Common.hxx>
 #include <oox/drawingml/drawingmltypes.hxx>
 
+#include <unotxdoc.hxx>
+
 class Test : public SwModelTestBase
 {
 public:
@@ -218,6 +220,57 @@ 
DECLARE_OOXMLEXPORT_TEST(testTdf120412_400PercentSubscript, "tdf120412_400Percen
     CPPUNIT_ASSERT_DOUBLES_EQUAL( -400.f, getProperty<float>(getRun(xPara, 2, 
u"Subscript"_ustr), u"CharEscapement"_ustr), 0);
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf162916_nastyTOC, "tdf162916_nastyTOC.docx")
+{
+    // given a TOC that wants to skip the 1st level, and not show a page 
number for level 2
+    // (which looked fine on import, but not after manually updating the 
index...)
+
+    // todo: enhancement to start at something other than level 1.
+
+    auto 
xSupplier(mxComponent.queryThrow<css::text::XDocumentIndexesSupplier>());
+    auto xIndexes = xSupplier->getDocumentIndexes();
+    auto 
xTOCIndex(xIndexes->getByIndex(0).queryThrow<css::beans::XPropertySet>());
+    css::uno::Reference<css::container::XIndexReplace> xLevelFormats;
+    CPPUNIT_ASSERT(xTOCIndex->getPropertyValue(u"LevelFormat"_ustr) >>= 
xLevelFormats);
+
+    const auto checkPropVal = [](const auto& expected, const 
css::beans::PropertyValues& entry,
+                                 const OUString& name, sal_Int32 level) {
+        auto it
+            = std::find_if(entry.begin(), entry.end(),
+                           [&name](const css::beans::PropertyValue& p) { 
return p.Name == name; });
+        OString msg = "Property: " + name.toUtf8() + ", level: " + 
OString::number(level);
+        CPPUNIT_ASSERT_MESSAGE(msg.getStr(), it != entry.end());
+        CPPUNIT_ASSERT_EQUAL_MESSAGE(msg.getStr(), css::uno::Any(expected), 
it->Value);
+    };
+
+    //start with level 1, 0 is the header level
+    for (sal_Int32 nLevel = 1; nLevel < xLevelFormats->getCount(); ++nLevel)
+    {
+        css::uno::Sequence<css::beans::PropertyValues> aLevel;
+        xLevelFormats->getByIndex(nLevel) >>= aLevel;
+
+        // level 2 does not display the page number with its separating 
tabstop.
+        sal_Int32 nExpectedTokens = nLevel == 2 ? 4 : 6;
+        CPPUNIT_ASSERT_EQUAL(nExpectedTokens ,aLevel.getLength());
+
+        sal_Int32 nIndex = 0;
+        checkPropVal(u"TokenHyperlinkStart"_ustr, aLevel[nIndex++], 
u"TokenType"_ustr, nLevel);
+
+        checkPropVal(u"TokenEntryNumber"_ustr, aLevel[nIndex++], 
u"TokenType"_ustr, nLevel);
+
+        checkPropVal(u"TokenEntryText"_ustr, aLevel[nIndex++], 
u"TokenType"_ustr, nLevel);
+
+        if (nLevel != 2)
+        {
+            checkPropVal(u"TokenTabStop"_ustr, aLevel[nIndex++], 
u"TokenType"_ustr, nLevel);
+
+            checkPropVal(u"TokenPageNumber"_ustr, aLevel[nIndex++], 
u"TokenType"_ustr, nLevel);
+        }
+
+        checkPropVal(u"TokenHyperlinkEnd"_ustr, aLevel[nIndex++], 
u"TokenType"_ustr, nLevel);
+    }
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testFontEsc)
 {
     loadAndSave("test_tdf120412.docx");
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index 31137ba9757b..4147155a4e55 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -7018,7 +7018,8 @@ void DomainMapper_Impl::handleDocProperty
 }
 
 static uno::Sequence< beans::PropertyValues > lcl_createTOXLevelHyperlinks( 
bool bHyperlinks, const OUString& sChapterNoSeparator,
-                                   const uno::Sequence< beans::PropertyValues 
>& aLevel, const std::optional<style::TabStop> numtab)
+                                   const uno::Sequence< beans::PropertyValues 
>& aLevel, const std::optional<style::TabStop> numtab,
+                                   bool bSkipPageNumberAndTab)
 {
     //create a copy of the level and add new entries
 
@@ -7028,8 +7029,8 @@ static uno::Sequence< beans::PropertyValues > 
lcl_createTOXLevelHyperlinks( bool
     static constexpr OUString tokType(u"TokenType"_ustr);
     static constexpr OUString tokHStart(u"TokenHyperlinkStart"_ustr);
     static constexpr OUString tokHEnd(u"TokenHyperlinkEnd"_ustr);
-    static constexpr OUStringLiteral tokPNum(u"TokenPageNumber");
-    static constexpr OUStringLiteral tokENum(u"TokenEntryNumber");
+    static constexpr OUString tokPNum(u"TokenPageNumber"_ustr);
+    static constexpr OUString tokENum(u"TokenEntryNumber"_ustr);
 
     if (bHyperlinks)
         aNewLevel.push_back({ comphelper::makePropertyValue(tokType, 
tokHStart) });
@@ -7055,7 +7056,23 @@ static uno::Sequence< beans::PropertyValues > 
lcl_createTOXLevelHyperlinks( bool
                                   comphelper::makePropertyValue(u"Text"_ustr, 
sChapterNoSeparator) });
         }
 
-        aNewLevel.push_back(item);
+        if (bSkipPageNumberAndTab && tokenType == tokPNum)
+        {
+            // also skip the preceding tabstop
+            if (aNewLevel.size())
+            {
+                OUString aPrevTokenType;
+                const auto& rPrevLevel = aNewLevel.back();
+                auto it = std::find_if(rPrevLevel.begin(), rPrevLevel.end(),
+                              [](const auto& p) { return p.Name == tokType; });
+                if (it != rPrevLevel.end())
+                    it->Value >>= aPrevTokenType;
+                if (aPrevTokenType == u"TokenTabStop"_ustr)
+                    aNewLevel.pop_back();
+            }
+        }
+        else
+            aNewLevel.push_back(item);
 
         if (numtab && tokenType == tokENum)
         {
@@ -7218,6 +7235,10 @@ void DomainMapper_Impl::handleToc
     bool bIsTabEntry = false ;
     bool bNewLine = false ;
     bool bParagraphOutlineLevel = false;
+    // some levels (optionally specified via a single range) might not display 
the page number
+    sal_uInt8 nStartNoPageNumber = 0;
+    sal_uInt8 nEndNoPageNumber = 0;
+
 
     sal_Int16 nMaxLevel = 10;
     OUString sTemplate;
@@ -7267,10 +7288,32 @@ void DomainMapper_Impl::handleToc
                             //todo: entries can only be included completely
 //                    }
 //                  
 Builds a table of contents or a range of entries, such as 1-9 in a table of 
contents without page numbers
-//                    if( lcl_FindInCommand( pContext->GetCommand(), 'n', 
sValue ))
-//                    {
-                        //todo: what does the description mean?
-//                    }
+    if (lcl_FindInCommand(pContext->GetCommand(), 'n', sValue))
+    {
+        // skip the tabstop and page-number on the specified levels
+        sValue = sValue.replaceAll("\"", "").trim();
+        if (sValue.isEmpty())
+        {
+            nStartNoPageNumber = 1;
+            nEndNoPageNumber = WW_OUTLINE_MAX;
+        }
+        else
+        {
+            // valid command format is fairly strict, requiring # <dash> #: 
TOC 
 "2-3"
+            sal_Int32 nIndex = 0;
+            o3tl::getToken(sValue, 0, '-', nIndex);
+            if (nIndex > 1)
+            {
+                const sal_Int32 nStartLevel = o3tl::toInt32(sValue.subView(0, 
nIndex - 1));
+                const sal_Int32 nEndLevel = 
o3tl::toInt32(sValue.subView(nIndex));
+                if (nStartLevel > 0 && nStartLevel <= nEndLevel && nEndLevel 
<= WW_OUTLINE_MAX)
+                {
+                    nStartNoPageNumber = static_cast<sal_uInt8>(nStartLevel);
+                    nEndNoPageNumber = static_cast<sal_uInt8>(nEndLevel);
+                }
+            }
+        }
+    }
 //                  \o  Builds a table of contents by using outline levels 
instead of TC entries
     if( lcl_FindInCommand( pContext->GetCommand(), 'o', sValue ))
     {
@@ -7497,10 +7540,10 @@ void DomainMapper_Impl::handleToc
                     }
                 }
             }
-
+            bool bSkipPageNumberAndTab = nLevel >= nStartNoPageNumber && 
nLevel <= nEndNoPageNumber;
             uno::Sequence< beans::PropertyValues > aNewLevel = 
lcl_createTOXLevelHyperlinks(
                                                 bHyperlinks, 
sChapterNoSeparator,
-                                                aLevel, numTab);
+                                                aLevel, numTab, 
bSkipPageNumberAndTab);
             xLevelFormats->replaceByIndex( nLevel, uno::Any( aNewLevel ) );
         }
     }
@@ -7525,7 +7568,7 @@ void DomainMapper_Impl::handleToc
 
             uno::Sequence< beans::PropertyValues > aNewLevel = 
lcl_createTOXLevelHyperlinks(
                                                 bHyperlinks, 
sChapterNoSeparator,
-                                                aLevel, {});
+                                                aLevel, {}, 
/*SkipPageNumberAndTab=*/false);
             xLevelFormats->replaceByIndex( 1, uno::Any( aNewLevel ) );
         }
     }

Reply via email to