sw/qa/extras/ooxmlexport/data/tdf152206.docx      |binary
 sw/qa/extras/ooxmlexport/ooxmlexport14.cxx        |   17 +++
 writerfilter/source/dmapper/DomainMapper.cxx      |   13 ++
 writerfilter/source/dmapper/DomainMapper_Impl.cxx |   98 ++++++++++++++--------
 writerfilter/source/dmapper/DomainMapper_Impl.hxx |   10 +-
 5 files changed, 100 insertions(+), 38 deletions(-)

New commits:
commit 8e7995a748bb0b9b77265bdcd74ce4800bc8afa2
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Fri Dec 16 13:20:25 2022 +0100
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Tue Dec 20 08:46:54 2022 +0000

    tdf#152506 DOCX import: fix mixed first footnote
    
    Also the first footnote can be at arbitrary
    place in footnote.xml.
    
    Follow-up to commit 09ae3c01940bbc25ffde51963683b04e3cb4bb6a
    "tdf#152203 DOCX import: fix mixed footnotes/endnotes".
    
    Change-Id: Iab356f7373483d812f0e802a994357fdad831d9d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144380
    Tested-by: Jenkins
    Reviewed-by: László Németh <nem...@numbertext.org>
    (cherry picked from commit 96a856f87f16cca2e039c973c18d57c8b9dca362)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144422
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/sw/qa/extras/ooxmlexport/data/tdf152206.docx 
b/sw/qa/extras/ooxmlexport/data/tdf152206.docx
new file mode 100644
index 000000000000..34f0130fdd7b
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf152206.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
index 9ce06be528e9..3916866b9ff4 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
@@ -1290,6 +1290,23 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf152203)
     CPPUNIT_ASSERT_EQUAL( OUString("Footnote for pg5"), 
xLastButOne->getString().trim() );
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf152206)
+{
+    loadAndSave("tdf152206.docx");
+    xmlDocUniquePtr pXml = parseExport("word/footnotes.xml");
+    CPPUNIT_ASSERT(pXml);
+
+    uno::Reference<text::XFootnotesSupplier> xFootnotesSupplier(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<container::XIndexAccess> xFootnotes = 
xFootnotesSupplier->getFootnotes();
+    uno::Reference<text::XTextRange> xLastFootnote(xFootnotes->getByIndex(1), 
uno::UNO_QUERY);
+    // This was "Footnote for pg5" (replaced footnotes)
+    CPPUNIT_ASSERT_EQUAL( OUString("Footnote for pg 6"), 
xLastFootnote->getString().trim() );
+
+    uno::Reference<text::XTextRange> xLastButOne(xFootnotes->getByIndex(0), 
uno::UNO_QUERY);
+    // This was "Footnote for pg 6" (replaced footnotes)
+    CPPUNIT_ASSERT_EQUAL( OUString("Footnote for pg5"), 
xLastButOne->getString().trim() );
+}
+
 // skip test for macOS (missing fonts?)
 #if !defined(MACOSX)
 DECLARE_OOXMLEXPORT_TEST(testTdf146346, "tdf146346.docx")
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx 
b/writerfilter/source/dmapper/DomainMapper.cxx
index 1e0f9597b3b4..e5582d097d55 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -3746,11 +3746,18 @@ void DomainMapper::lcl_checkId(const sal_Int32 nId)
 {
     if (m_pImpl->IsInFootnote())
     {
-        if (m_pImpl->GetFootnoteCount() > -1)
-            m_pImpl->m_aFootnoteIds.push_back(nId);
+        m_pImpl->m_aFootnoteIds.push_back(nId);
+        // keep only the first real footnote
+        if (m_pImpl->GetFootnoteCount() == -1 && 
m_pImpl->m_aFootnoteIds.size() == 2)
+            m_pImpl->m_aFootnoteIds.pop_front();
     }
-    else if (m_pImpl->GetEndnoteCount() > -1)
+    else
+    {
         m_pImpl->m_aEndnoteIds.push_back(nId);
+        // keep only the first real endnote
+        if (m_pImpl->GetEndnoteCount() == -1 && m_pImpl->m_aEndnoteIds.size() 
== 2)
+            m_pImpl->m_aEndnoteIds.pop_front();
+    }
 }
 
 void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len)
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 5365b302c6d2..1227d448a057 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -356,6 +356,8 @@ DomainMapper_Impl::DomainMapper_Impl(
         m_eSkipFootnoteState(SkipFootnoteSeparator::OFF),
         m_nFootnotes(-1),
         m_nEndnotes(-1),
+        m_nFirstFootnoteIndex(-1),
+        m_nFirstEndnoteIndex(-1),
         m_bLineNumberingSet( false ),
         m_bIsInFootnoteProperties( false ),
         m_bIsParaMarkerChange( false ),
@@ -3617,6 +3619,36 @@ static void lcl_PasteRedlines(
     }
 }
 
+bool DomainMapper_Impl::CopyTemporaryNotes(
+        uno::Reference< text::XFootnote > xNoteSrc,
+        uno::Reference< text::XFootnote > xNoteDest )
+{
+    if (!m_bSaxError && xNoteSrc != xNoteDest)
+    {
+        uno::Reference< text::XText > xSrc( xNoteSrc, uno::UNO_QUERY_THROW );
+        uno::Reference< text::XText > xDest( xNoteDest, uno::UNO_QUERY_THROW );
+        uno::Reference< text::XTextCopy > xTxt, xTxt2;
+        xTxt.set(  xSrc, uno::UNO_QUERY_THROW );
+        xTxt2.set( xDest, uno::UNO_QUERY_THROW );
+        xTxt2->copyText( xTxt );
+
+        // copy its redlines
+        std::vector<sal_Int32> redPos, redLen;
+        sal_Int32 redIdx;
+        enum StoredRedlines eType = IsInFootnote() ? StoredRedlines::FOOTNOTE 
: StoredRedlines::ENDNOTE;
+        lcl_CopyRedlines(xSrc, m_aStoredRedlines[eType], redPos, redLen, 
redIdx);
+        lcl_PasteRedlines(xDest, m_aStoredRedlines[eType], redPos, redLen, 
redIdx);
+
+        // remove processed redlines
+        for( size_t i = 0; redIdx > -1 && i <= 
sal::static_int_cast<size_t>(redIdx) + 2; i++)
+            m_aStoredRedlines[eType].pop_front();
+
+        return true;
+    }
+
+    return false;
+}
+
 void DomainMapper_Impl::RemoveTemporaryFootOrEndnotes()
 {
     uno::Reference< text::XFootnotesSupplier> xFootnotesSupplier( 
GetTextDocument(), uno::UNO_QUERY );
@@ -3625,6 +3657,15 @@ void DomainMapper_Impl::RemoveTemporaryFootOrEndnotes()
     if  (GetFootnoteCount() > 0)
     {
         auto xFootnotes = xFootnotesSupplier->getFootnotes();
+        if ( m_nFirstFootnoteIndex > 0 )
+        {
+            uno::Reference< text::XFootnote > xFirstNote;
+            xFootnotes->getByIndex(0) >>= xFirstNote;
+            uno::Reference< text::XText > xText( xFirstNote, 
uno::UNO_QUERY_THROW );
+            xText->setString("");
+            xFootnotes->getByIndex(m_nFirstFootnoteIndex) >>= xNote;
+            CopyTemporaryNotes(xNote, xFirstNote);
+        }
         for (sal_Int32 i = GetFootnoteCount(); i > 0; --i)
         {
             xFootnotes->getByIndex(i) >>= xNote;
@@ -3634,6 +3675,15 @@ void DomainMapper_Impl::RemoveTemporaryFootOrEndnotes()
     if  (GetEndnoteCount() > 0)
     {
         auto xEndnotes = xEndnotesSupplier->getEndnotes();
+        if ( m_nFirstEndnoteIndex > 0 )
+        {
+            uno::Reference< text::XFootnote > xFirstNote;
+            xEndnotes->getByIndex(0) >>= xFirstNote;
+            uno::Reference< text::XText > xText( xFirstNote, 
uno::UNO_QUERY_THROW );
+            xText->setString("");
+            xEndnotes->getByIndex(m_nFirstEndnoteIndex) >>= xNote;
+            CopyTemporaryNotes(xNote, xFirstNote);
+        }
         for (sal_Int32 i = GetEndnoteCount(); i > 0; --i)
         {
             xEndnotes->getByIndex(i) >>= xNote;
@@ -3642,7 +3692,7 @@ void DomainMapper_Impl::RemoveTemporaryFootOrEndnotes()
     }
 }
 
-static void lcl_convertToNoteIndices(std::deque<sal_Int32>& rNoteIds)
+static void lcl_convertToNoteIndices(std::deque<sal_Int32>& rNoteIds, 
sal_Int32& rFirstNoteIndex)
 {
     // convert arbitrary footnote identifiers to 0, 1, 2...
     // indices, keeping their possible random order
@@ -3653,6 +3703,8 @@ static void 
lcl_convertToNoteIndices(std::deque<sal_Int32>& rNoteIds)
         aMapIds[aSortedIds[i]] = i;
     for (size_t i = 0; i < rNoteIds.size(); ++i)
         rNoteIds[i] = aMapIds[rNoteIds[i]];
+    rFirstNoteIndex = rNoteIds.front();
+    rNoteIds.pop_front();
 }
 
 void DomainMapper_Impl::PopFootOrEndnote()
@@ -3666,30 +3718,30 @@ void DomainMapper_Impl::PopFootOrEndnote()
     if ( IsInFootOrEndnote() && ( ( IsInFootnote() && GetFootnoteCount() > -1 
&& xFootnotesSupplier.is() ) ||
          ( !IsInFootnote() && GetEndnoteCount() > -1 && xEndnotesSupplier.is() 
) ) )
     {
-        uno::Reference< text::XFootnote > xFootnoteFirst, xFootnoteLast;
+        uno::Reference< text::XFootnote > xNoteFirst, xNoteLast;
         auto xFootnotes = xFootnotesSupplier->getFootnotes();
         auto xEndnotes = xEndnotesSupplier->getEndnotes();
         if ( ( ( IsInFootnote() && xFootnotes->getCount() > 1 &&
-                       ( xFootnotes->getByIndex(xFootnotes->getCount()-1) >>= 
xFootnoteLast ) ) ||
+                       ( xFootnotes->getByIndex(xFootnotes->getCount()-1) >>= 
xNoteLast ) ) ||
                ( !IsInFootnote() && xEndnotes->getCount() > 1 &&
-                       ( xEndnotes->getByIndex(xEndnotes->getCount()-1) >>= 
xFootnoteLast ) )
-             ) && xFootnoteLast->getLabel().isEmpty() )
+                       ( xEndnotes->getByIndex(xEndnotes->getCount()-1) >>= 
xNoteLast ) )
+             ) && xNoteLast->getLabel().isEmpty() )
         {
             // copy content of the next temporary footnote
             try
             {
                 if ( IsInFootnote() && !m_aFootnoteIds.empty() )
                 {
-                    if ( m_aFootnoteIds.size() == 
sal::static_int_cast<size_t>(GetFootnoteCount()) )
-                        lcl_convertToNoteIndices(m_aFootnoteIds);
-                    xFootnotes->getByIndex(m_aFootnoteIds.front() + 1) >>= 
xFootnoteFirst;
+                    if ( m_nFirstFootnoteIndex == -1 )
+                        lcl_convertToNoteIndices(m_aFootnoteIds, 
m_nFirstFootnoteIndex);
+                    xFootnotes->getByIndex(m_aFootnoteIds.front()) >>= 
xNoteFirst;
                     m_aFootnoteIds.pop_front();
                 }
                 else if ( !IsInFootnote() && !m_aEndnoteIds.empty() )
                 {
-                    if ( m_aEndnoteIds.size() == 
sal::static_int_cast<size_t>(GetEndnoteCount()) )
-                        lcl_convertToNoteIndices(m_aEndnoteIds);
-                    xEndnotes->getByIndex(m_aEndnoteIds.front() + 1) >>= 
xFootnoteFirst;
+                    if ( m_nFirstEndnoteIndex == -1 )
+                        lcl_convertToNoteIndices(m_aEndnoteIds, 
m_nFirstEndnoteIndex);
+                    xEndnotes->getByIndex(m_aEndnoteIds.front()) >>= 
xNoteFirst;
                     m_aEndnoteIds.pop_front();
                 }
                 else
@@ -3700,28 +3752,8 @@ void DomainMapper_Impl::PopFootOrEndnote()
                 TOOLS_WARN_EXCEPTION("writerfilter.dmapper", "Cannot insert 
footnote/endnote");
                 m_bSaxError = true;
             }
-            if (!m_bSaxError && xFootnoteFirst != xFootnoteLast)
-            {
-                uno::Reference< text::XText > xSrc( xFootnoteFirst, 
uno::UNO_QUERY_THROW );
-                uno::Reference< text::XText > xDest( xFootnoteLast, 
uno::UNO_QUERY_THROW );
-                uno::Reference< text::XTextCopy > xTxt, xTxt2;
-                xTxt.set(  xSrc, uno::UNO_QUERY_THROW );
-                xTxt2.set( xDest, uno::UNO_QUERY_THROW );
-                xTxt2->copyText( xTxt );
-
-                // copy its redlines
-                std::vector<sal_Int32> redPos, redLen;
-                sal_Int32 redIdx;
-                enum StoredRedlines eType = IsInFootnote() ? 
StoredRedlines::FOOTNOTE : StoredRedlines::ENDNOTE;
-                lcl_CopyRedlines(xSrc, m_aStoredRedlines[eType], redPos, 
redLen, redIdx);
-                lcl_PasteRedlines(xDest, m_aStoredRedlines[eType], redPos, 
redLen, redIdx);
-
-                // remove processed redlines
-                for( size_t i = 0; redIdx > -1 && i <= 
sal::static_int_cast<size_t>(redIdx) + 2; i++)
-                    m_aStoredRedlines[eType].pop_front();
-
-                bCopied = true;
-            }
+
+            bCopied = CopyTemporaryNotes(xNoteFirst, xNoteLast);
         }
     }
 
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 1c0c9654c1b0..7c5792b7e4e4 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -558,8 +558,11 @@ private:
     /// Skip paragraphs from the <w:separator/> footnote
     SkipFootnoteSeparator           m_eSkipFootnoteState;
     /// preload footnotes and endnotes
-    sal_Int32                       m_nFootnotes;
-    sal_Int32                       m_nEndnotes;
+    sal_Int32                       m_nFootnotes; // footnote count
+    sal_Int32                       m_nEndnotes;  // endnote count
+    // these are the real first notes, use their content in the first notes
+    sal_Int32                       m_nFirstFootnoteIndex;
+    sal_Int32                       m_nFirstEndnoteIndex;
 
     bool                            m_bLineNumberingSet;
     bool                            m_bIsInFootnoteProperties;
@@ -886,6 +889,9 @@ public:
     void IncrementFootnoteCount() { ++m_nFootnotes; }
     sal_Int32 GetEndnoteCount() const { return m_nEndnotes; }
     void IncrementEndnoteCount() { ++m_nEndnotes; }
+    bool CopyTemporaryNotes(
+        css::uno::Reference< css::text::XFootnote > xNoteSrc,
+        css::uno::Reference< css::text::XFootnote > xNoteDest );
     void RemoveTemporaryFootOrEndnotes();
 
     void PushAnnotation();

Reply via email to