sw/qa/extras/ooxmlimport/data/tdf154695-ToC_no_numbers.docx |binary sw/qa/extras/ooxmlimport/ooxmlimport2.cxx | 80 ++++++++++-- writerfilter/source/dmapper/DomainMapper_Impl.cxx | 37 ++++- 3 files changed, 98 insertions(+), 19 deletions(-)
New commits: commit 8914765ec9330d250e220b1ef51f7c62b82b8e2f Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Sat Apr 8 00:03:31 2023 +0300 Commit: Adolfo Jayme Barrientos <fit...@ubuntu.com> CommitDate: Sat Apr 8 23:18:53 2023 +0200 tdf#154695: use Chapter Numbering Rules to confirm that ToC needs tabs Indeed, this still can't handle it per-element, like Word does: the latter would omit both the number and the tab for headings without numbering. But at least the most common case should be handled better now. Change-Id: I0be5d61a5d2cbbf778ee88a11129a6e7655593ec Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150133 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> (cherry picked from commit b6a5f16a9bf3227867bf46b88d012440138b6f24) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150142 Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com> diff --git a/sw/qa/extras/ooxmlimport/data/tdf154695-ToC_no_numbers.docx b/sw/qa/extras/ooxmlimport/data/tdf154695-ToC_no_numbers.docx new file mode 100644 index 000000000000..fb86250fd415 Binary files /dev/null and b/sw/qa/extras/ooxmlimport/data/tdf154695-ToC_no_numbers.docx differ diff --git a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx index 532ad030b28d..37f1a48ca834 100644 --- a/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx +++ b/sw/qa/extras/ooxmlimport/ooxmlimport2.cxx @@ -1045,8 +1045,9 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf154319) }; // tdf#154360: check tab stops between the number and the entry text + // The last (10th) level does not correspont to any MS level (only 9 levels there) constexpr sal_Int32 levelTabStops[] - = { 776, 1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270 }; + = { 776, 1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270, -1 }; //start with level 1, 0 is the header level for (sal_Int32 nLevel = 1; nLevel < xLevelFormats->getCount(); ++nLevel) @@ -1054,27 +1055,80 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf154319) css::uno::Sequence<css::beans::PropertyValues> aLevel; xLevelFormats->getByIndex(nLevel) >>= aLevel; - CPPUNIT_ASSERT_EQUAL(sal_Int32(9), aLevel.getLength()); + sal_Int32 nTabStop = levelTabStops[nLevel - 1]; + sal_Int32 nExpectedTokens = nTabStop < 0 ? 8 : 9; + CPPUNIT_ASSERT_EQUAL(nExpectedTokens, aLevel.getLength()); + sal_Int32 nIndex = 0; - checkPropVal(OUString("TokenHyperlinkStart"), aLevel[0], "TokenType", nLevel); + checkPropVal(OUString("TokenHyperlinkStart"), aLevel[nIndex++], "TokenType", nLevel); - checkPropVal(OUString("TokenEntryNumber"), aLevel[1], "TokenType", nLevel); + checkPropVal(OUString("TokenEntryNumber"), aLevel[nIndex++], "TokenType", nLevel); + + if (nTabStop >= 0) + { + checkPropVal(OUString("TokenTabStop"), aLevel[nIndex], "TokenType", nLevel); + checkPropVal(levelTabStops[nLevel - 1], aLevel[nIndex++], "TabStopPosition", nLevel); + } + + checkPropVal(OUString("TokenEntryText"), aLevel[nIndex++], "TokenType", nLevel); + + checkPropVal(OUString("TokenTabStop"), aLevel[nIndex++], "TokenType", nLevel); + + checkPropVal(OUString("TokenChapterInfo"), aLevel[nIndex++], "TokenType", nLevel); + + checkPropVal(OUString("TokenText"), aLevel[nIndex], "TokenType", nLevel); + checkPropVal(OUString("\""), aLevel[nIndex++], "Text", nLevel); + + checkPropVal(OUString("TokenPageNumber"), aLevel[nIndex++], "TokenType", nLevel); + + checkPropVal(OUString("TokenHyperlinkEnd"), aLevel[nIndex++], "TokenType", nLevel); + } +} - checkPropVal(OUString("TokenTabStop"), aLevel[2], "TokenType", nLevel); - checkPropVal(levelTabStops[nLevel - 1], aLevel[2], "TabStopPosition", nLevel); +CPPUNIT_TEST_FIXTURE(Test, testTdf154695) +{ + createSwDoc("tdf154695-ToC_no_numbers.docx"); - checkPropVal(OUString("TokenEntryText"), aLevel[3], "TokenType", nLevel); + css::uno::Reference<css::text::XDocumentIndexesSupplier> xSupplier(mxComponent, + css::uno::UNO_QUERY_THROW); + auto xIndexes = xSupplier->getDocumentIndexes(); + css::uno::Reference<css::beans::XPropertySet> xTOCIndex(xIndexes->getByIndex(0), + css::uno::UNO_QUERY_THROW); + css::uno::Reference<css::container::XIndexReplace> xLevelFormats; + CPPUNIT_ASSERT(xTOCIndex->getPropertyValue("LevelFormat") >>= xLevelFormats); + CPPUNIT_ASSERT_EQUAL(sal_Int32(11), xLevelFormats->getCount()); + + 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; + + CPPUNIT_ASSERT_EQUAL(sal_Int32(6), aLevel.getLength()); + + checkPropVal(OUString("TokenHyperlinkStart"), aLevel[0], "TokenType", nLevel); + + checkPropVal(OUString("TokenEntryNumber"), aLevel[1], "TokenType", nLevel); - checkPropVal(OUString("TokenTabStop"), aLevel[4], "TokenType", nLevel); + // There's no tab stop between [#E] and [E]! - checkPropVal(OUString("TokenChapterInfo"), aLevel[5], "TokenType", nLevel); + checkPropVal(OUString("TokenEntryText"), aLevel[2], "TokenType", nLevel); - checkPropVal(OUString("TokenText"), aLevel[6], "TokenType", nLevel); - checkPropVal(OUString("\""), aLevel[6], "Text", nLevel); + checkPropVal(OUString("TokenTabStop"), aLevel[3], "TokenType", nLevel); - checkPropVal(OUString("TokenPageNumber"), aLevel[7], "TokenType", nLevel); + checkPropVal(OUString("TokenPageNumber"), aLevel[4], "TokenType", nLevel); - checkPropVal(OUString("TokenHyperlinkEnd"), aLevel[8], "TokenType", nLevel); + checkPropVal(OUString("TokenHyperlinkEnd"), aLevel[5], "TokenType", nLevel); } } diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index ebcd8bebbbfa..3b774608756a 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -51,6 +51,7 @@ #include <com/sun/star/text/XFootnotesSupplier.hpp> #include <com/sun/star/text/XLineNumberingProperties.hpp> #include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/text/LabelFollow.hpp> #include <com/sun/star/text/PageNumberType.hpp> #include <com/sun/star/text/HoriOrientation.hpp> #include <com/sun/star/text/VertOrientation.hpp> @@ -60,6 +61,7 @@ #include <com/sun/star/text/SizeType.hpp> #include <com/sun/star/text/TextContentAnchorType.hpp> #include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/text/XChapterNumberingSupplier.hpp> #include <com/sun/star/text/XDependentTextField.hpp> #include <com/sun/star/text/XParagraphCursor.hpp> #include <com/sun/star/text/XRedline.hpp> @@ -6433,6 +6435,10 @@ void DomainMapper_Impl::handleToc } + uno::Reference<container::XIndexAccess> xChapterNumberingRules; + if (uno::Reference<text::XChapterNumberingSupplier> xSupplier{ GetTextDocument(), + uno::UNO_QUERY }) + xChapterNumberingRules = xSupplier->getChapterNumberingRules(); uno::Reference<container::XNameContainer> xStyles; if (uno::Reference<style::XStyleFamiliesSupplier> xStylesSupplier{ GetTextDocument(), uno::UNO_QUERY }) @@ -6452,13 +6458,32 @@ void DomainMapper_Impl::handleToc // Get the tab stops coming from the styles; store to the level definitions uno::Sequence<style::TabStop> tabStops; - if (xStyles) + if (xChapterNumberingRules && xStyles) { - OUString style; - xTOC->getPropertyValue("ParaStyleLevel" + OUString::number(nLevel)) >>= style; - uno::Reference<beans::XPropertySet> xStyle; - if (xStyles->getByName(style) >>= xStyle) - xStyle->getPropertyValue("ParaTabStops") >>= tabStops; + // This relies on the chapter numbering rules already defined + // (see ListDef::CreateNumberingRules) + uno::Sequence<beans::PropertyValue> props; + xChapterNumberingRules->getByIndex(nLevel - 1) >>= props; + bool bHasNumbering = false; + bool bUseTabStop = false; + for (const auto& propval : props) + { + // We rely on PositionAndSpaceMode being always equal to LABEL_ALIGNMENT, + // because ListDef::CreateNumberingRules doesn't create legacy lists + if (propval.Name == "NumberingType") + bHasNumbering = propval.Value != style::NumberingType::NUMBER_NONE; + else if (propval.Name == "LabelFollowedBy") + bUseTabStop = propval.Value == text::LabelFollow::LISTTAB; + // Do not use FirstLineIndent property from the rules, because it is unreliable + } + if (bHasNumbering && bUseTabStop) + { + OUString style; + xTOC->getPropertyValue("ParaStyleLevel" + OUString::number(nLevel)) >>= style; + uno::Reference<beans::XPropertySet> xStyle; + if (xStyles->getByName(style) >>= xStyle) + xStyle->getPropertyValue("ParaTabStops") >>= tabStops; + } } uno::Sequence< beans::PropertyValues > aNewLevel = lcl_createTOXLevelHyperlinks(