include/xmloff/xmlimp.hxx                       |    2 +
 xmloff/inc/txtlists.hxx                         |    5 ++++
 xmloff/qa/unit/data/continue-numbering-word.odt |binary
 xmloff/qa/unit/text.cxx                         |   25 ++++++++++++++++++++++++
 xmloff/source/core/xmlimp.cxx                   |   22 +++++++++++++++++++++
 xmloff/source/text/XMLTextListBlockContext.cxx  |    8 +++++++
 xmloff/source/text/txtlists.cxx                 |   23 ++++++++++++++++++++++
 7 files changed, 85 insertions(+)

New commits:
commit 6092b48a08d463deb8f02131fbec74fa2424c4fe
Author:     Miklos Vajna <[email protected]>
AuthorDate: Thu Feb 24 16:43:19 2022 +0100
Commit:     Mike Kaganski <[email protected]>
CommitDate: Fri Feb 25 13:17:14 2022 +0100

    ODT import: fix MSO-style <text:list text:continue-numbering="true">
    
    The ODF spec says that text:continue-numbering="true" should only
    continue the numbering in case the styles of the previous and the
    current list match.
    
    In contrast, Word continues the numbering even in case there is e.g.
    numbering, then bullets, then numbering again, in case the list styles of
    the two numberings are the same.
    
    Work this around at import time when the generator confirms that the
    document is coming from Word. At least Office 2019 and the latest
    renderer at office.com is affected.
    
    (I've mailed dochelp@microsoft, no answer yet.)
    
    (cherry picked from commit 1127c63470096f62394f133c61cee2e6fb7fd0c7)
    
    Conflicts:
            xmloff/qa/unit/text.cxx
    
    Change-Id: Ib63e14322e5501a6220f798abd9365d7913dab4c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130520
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/include/xmloff/xmlimp.hxx b/include/xmloff/xmlimp.hxx
index a054fa86538d..db790e8ad405 100644
--- a/include/xmloff/xmlimp.hxx
+++ b/include/xmloff/xmlimp.hxx
@@ -461,6 +461,8 @@ public:
 
     OUString GetODFVersion() const;
     bool IsOOoXML() const; // legacy non-ODF format?
+    /// Determines if the document was generated by Microsoft Office.
+    bool IsMSO() const;
 
     /**
      * Record an error condition that occurred during import. The
diff --git a/xmloff/inc/txtlists.hxx b/xmloff/inc/txtlists.hxx
index 1d335be473f9..22a7d4f48611 100644
--- a/xmloff/inc/txtlists.hxx
+++ b/xmloff/inc/txtlists.hxx
@@ -121,6 +121,9 @@ class XMLTextListsHelper
             bool* o_pRestartNumbering = nullptr,
             bool* io_pSetDefaults = nullptr);
 
+        /// Looks up the last list id of a given list style, by name.
+        OUString GetLastIdOfStyleName(const OUString& sListStyleName) const;
+
     private:
 
         /** list context: list, list-item, numbered-paragraph
@@ -152,6 +155,8 @@ class XMLTextListsHelper
         typedef ::std::map< OUString, OUString > tMapForContinuingLists;
         std::unique_ptr<tMapForContinuingLists> mpContinuingLists;
 
+        std::unique_ptr<std::map<OUString, OUString>> mpStyleNameLastListIds;
+
         // stack type for opened list elements and its list style:
         // vector with pair( <ListId>, <ListStyleName> ) as value
         typedef ::std::vector< ::std::pair< OUString, OUString > >
diff --git a/xmloff/qa/unit/data/continue-numbering-word.odt 
b/xmloff/qa/unit/data/continue-numbering-word.odt
new file mode 100644
index 000000000000..278a1fa65684
Binary files /dev/null and b/xmloff/qa/unit/data/continue-numbering-word.odt 
differ
diff --git a/xmloff/qa/unit/text.cxx b/xmloff/qa/unit/text.cxx
index aff33db6a229..bdbb632bde76 100644
--- a/xmloff/qa/unit/text.cxx
+++ b/xmloff/qa/unit/text.cxx
@@ -100,6 +100,31 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testCommentResolved)
     CPPUNIT_ASSERT(bResolved);
 }
 
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testContinueNumberingWord)
+{
+    // Given a document, which is produced by Word and contains 
text:continue-numbering="true":
+    OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + 
"continue-numbering-word.odt";
+
+    // When loading that document:
+    getComponent() = loadFromDesktop(aURL);
+
+    // Then make sure that the numbering from the 1st para is continued on the 
3rd para:
+    uno::Reference<text::XTextDocument> xTextDocument(getComponent(), 
uno::UNO_QUERY);
+    uno::Reference<text::XText> xText = xTextDocument->getText();
+    uno::Reference<container::XEnumerationAccess> 
xParaEnumAccess(xTextDocument->getText(),
+                                                                  
uno::UNO_QUERY);
+    uno::Reference<container::XEnumeration> xParaEnum = 
xParaEnumAccess->createEnumeration();
+    xParaEnum->nextElement();
+    xParaEnum->nextElement();
+    uno::Reference<beans::XPropertySet> xPara(xParaEnum->nextElement(), 
uno::UNO_QUERY);
+    auto aActual = xPara->getPropertyValue("ListLabelString").get<OUString>();
+    // Without the accompanying fix in place, this failed with:
+    // - Expected: 2.
+    // - Actual  : 1.
+    // i.e. the numbering was not continued, like in Word.
+    CPPUNIT_ASSERT_EQUAL(OUString("2."), aActual);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx
index bc977309b169..d902a2b5ffa8 100644
--- a/xmloff/source/core/xmlimp.cxx
+++ b/xmloff/source/core/xmlimp.cxx
@@ -55,6 +55,7 @@
 #include <com/sun/star/xml/sax/FastParser.hpp>
 #include <com/sun/star/xml/sax/SAXException.hpp>
 #include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
 #include <comphelper/fileformat.h>
 #include <comphelper/namecontainer.hxx>
 #include <comphelper/servicehelper.hxx>
@@ -279,6 +280,8 @@ public:
 
     bool mbIsOOoXML;
 
+    std::optional<bool> mbIsMSO;
+
     // Boolean, indicating that position attributes
     // of shapes are given in horizontal left-to-right layout. This is the case
     // for the OpenOffice.org file format. (#i28749#)
@@ -1942,6 +1945,25 @@ bool SvXMLImport::IsOOoXML() const
     return mpImpl->mbIsOOoXML;
 }
 
+bool SvXMLImport::IsMSO() const
+{
+    if (!mpImpl->mbIsMSO.has_value())
+    {
+        uno::Reference<document::XDocumentPropertiesSupplier> 
xSupplier(GetModel(), uno::UNO_QUERY);
+        if (xSupplier.is())
+        {
+            uno::Reference<document::XDocumentProperties> xProps
+                = xSupplier->getDocumentProperties();
+            if (xProps.is())
+            {
+                mpImpl->mbIsMSO = 
xProps->getGenerator().startsWith("MicrosoftOffice");
+            }
+        }
+    }
+
+    return mpImpl->mbIsMSO.has_value() ? *mpImpl->mbIsMSO : false;
+}
+
 // xml:id for RDF metadata
 void SvXMLImport::SetXmlId(uno::Reference<uno::XInterface> const & i_xIfc,
     OUString const & i_rXmlId)
diff --git a/xmloff/source/text/XMLTextListBlockContext.cxx 
b/xmloff/source/text/XMLTextListBlockContext.cxx
index 3fcdca9d25a9..cba88c030d47 100644
--- a/xmloff/source/text/XMLTextListBlockContext.cxx
+++ b/xmloff/source/text/XMLTextListBlockContext.cxx
@@ -194,6 +194,14 @@ XMLTextListBlockContext::XMLTextListBlockContext(
         }
     }
 
+    bool bContinueNumbering = bIsContinueNumberingAttributePresent && 
!mbRestartNumbering;
+    if (msContinueListId.isEmpty() && bContinueNumbering && 
GetImport().IsMSO())
+    {
+        // No "continue list" id, but continue numbering was requested. 
Connect to the last list of
+        // the same list style in the Word case, even if there was a different 
list in the meantime.
+        msContinueListId = 
rTextListsHelper.GetLastIdOfStyleName(msListStyleName);
+    }
+
     if ( !msContinueListId.isEmpty() )
     {
         if ( !rTextListsHelper.IsListProcessed( msContinueListId ) )
diff --git a/xmloff/source/text/txtlists.cxx b/xmloff/source/text/txtlists.cxx
index af53ad308d2f..bb45447ac9cb 100644
--- a/xmloff/source/text/txtlists.cxx
+++ b/xmloff/source/text/txtlists.cxx
@@ -126,6 +126,13 @@ void XMLTextListsHelper::KeepListAsProcessed( const 
OUString& sListId,
     msLastProcessedListId = sListId;
     msListStyleOfLastProcessedList = sListStyleName;
 
+    // Remember what is the last list id of this list style.
+    if (!mpStyleNameLastListIds)
+    {
+        mpStyleNameLastListIds = std::make_unique<std::map<OUString, 
OUString>>();
+    }
+    (*mpStyleNameLastListIds)[sListStyleName] = sListId;
+
     // Inconsistent behavior regarding lists (#i92811#)
     if ( sListStyleDefaultListId.isEmpty())
         return;
@@ -475,4 +482,20 @@ XMLTextListsHelper::MakeNumRule(
     return xNumRules;
 }
 
+OUString XMLTextListsHelper::GetLastIdOfStyleName(const OUString& 
sListStyleName) const
+{
+    if (!mpStyleNameLastListIds)
+    {
+        return {};
+    }
+
+    auto it = mpStyleNameLastListIds->find(sListStyleName);
+    if (it == mpStyleNameLastListIds->end())
+    {
+        return {};
+    }
+
+    return it->second;
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to