diff options
author | László Németh <nemeth@numbertext.org> | 2021-02-17 09:47:43 +0100 |
---|---|---|
committer | László Németh <nemeth@numbertext.org> | 2021-02-18 13:30:55 +0100 |
commit | 7dd8f8aace536a8e60e87e61ee1d90d61fba15eb (patch) | |
tree | 72638bfc11fa2d0ee1c55f214df3a8e027168aaa | |
parent | 63d14566ba93b87f23a7ba68f8ea733cf19be101 (diff) |
tdf#120351 DOCX import: fix slow endnote import
by parsing endnotes.xml only once instead
of parsing again and again for every endnotes.
This was a serious performance problem for
documents with hundreds of endnotes, where the
endnote import took minutes instead of seconds.
Note: switch off CHECK_NOTMERGED in a debug build
to measure realistic speed-up, e.g. 98 s -> 18 s
for a document with thousand of endnotes.
Follow-up of commit 9b39ce0e66acfe812e1d50e530dc2ccdef3e1357
"tdf#76260 DOCX import: fix slow footnote import".
Change-Id: I88cdc5101c25041f985fdc23739a0dadf24a13e0
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111030
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
-rw-r--r-- | writerfilter/source/dmapper/DomainMapper.cxx | 24 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapper_Impl.cxx | 224 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapper_Impl.hxx | 26 | ||||
-rw-r--r-- | writerfilter/source/dmapper/PropertyMap.cxx | 2 | ||||
-rw-r--r-- | writerfilter/source/ooxml/OOXMLDocumentImpl.cxx | 6 | ||||
-rw-r--r-- | writerfilter/source/ooxml/OOXMLFastContextHandler.cxx | 2 |
6 files changed, 157 insertions, 127 deletions
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index 4f58670aefb5..a3b04a014ca8 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -3249,7 +3249,9 @@ void DomainMapper::lcl_text(const sal_uInt8 * data_, size_t len) m_pImpl->SetFieldLocked(); return; case 0x0c: //page break - m_pImpl->deferBreak(PAGE_BREAK); + // page breaks aren't supported in footnotes and endnotes + if (!m_pImpl->IsInFootOrEndnote()) + m_pImpl->deferBreak(PAGE_BREAK); return; case 0x0e: //column break m_pImpl->deferBreak(COLUMN_BREAK); @@ -3373,12 +3375,24 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len) // preload all footnotes in separated footnotes if (sText[0] == 0x5) { - if (m_pImpl->GetFootnoteCount() > -1) + if (m_pImpl->IsInFootnote()) { - m_pImpl->PopFootOrEndnote(/*bIsFootnote=*/true); - m_pImpl->PushFootOrEndnote(/*bIsFootnote=*/true); + if (m_pImpl->GetFootnoteCount() > -1) + { + m_pImpl->PopFootOrEndnote(); + m_pImpl->PushFootOrEndnote(/*bIsFootnote=*/true); + } + m_pImpl->IncrementFootnoteCount(); + } + else + { + if (m_pImpl->GetEndnoteCount() > -1) + { + m_pImpl->PopFootOrEndnote(); + m_pImpl->PushFootOrEndnote(/*bIsFootnote=*/false); + } + m_pImpl->IncrementEndnoteCount(); } - m_pImpl->IncrementFootnoteCount(); } // If the footnote contains a Footnote Reference Mark, it can't be a custom footnote diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index ebfe9d625bd2..d0ddb431c4d7 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -304,10 +304,12 @@ DomainMapper_Impl::DomainMapper_Impl( m_eInHeaderFooterImport( HeaderFooterImportState::none ), m_bDiscardHeaderFooter( false ), m_bInFootOrEndnote(false), + m_bInFootnote(false), m_bHasFootnoteStyle(false), m_bCheckFootnoteStyle(false), m_eSkipFootnoteState(SkipFootnoteSeparator::OFF), m_nFootnotes(-1), + m_nEndnotes(-1), m_bLineNumberingSet( false ), m_bIsInFootnoteProperties( false ), m_bIsParaMarkerChange( false ), @@ -2614,6 +2616,7 @@ void DomainMapper_Impl::PushFootOrEndnote( bool bIsFootnote ) { SAL_WARN_IF(m_bInFootOrEndnote, "writerfilter.dmapper", "PushFootOrEndnote() is called from another foot or endnote"); m_bInFootOrEndnote = true; + m_bInFootnote = bIsFootnote; m_bCheckFirstFootnoteTab = true; m_bSaveFirstParagraphInCell = m_bFirstParagraphInCell; try @@ -2703,24 +2706,25 @@ void DomainMapper_Impl::CreateRedline(uno::Reference<text::XTextRange> const& xR pRedlineProperties[1].Value <<= ConversionHelper::ConvertDateStringToDateTime( pRedline->m_sDate ); pRedlineProperties[2].Name = getPropertyName( PROP_REDLINE_REVERT_PROPERTIES ); pRedlineProperties[2].Value <<= pRedline->m_aRevertProperties; - // TODO: add !IsInFootOrEndnote(), if loading of endnotes is optimized if (!m_bIsActualParagraphFramed) { uno::Reference < text::XRedline > xRedline( xRange, uno::UNO_QUERY_THROW ); xRedline->makeRedline( sType, aRedlineProperties ); } // store frame and (possible floating) table redline data for restoring them after frame conversion + enum StoredRedlines eType; if (m_bIsActualParagraphFramed || (hasTableManager() && getTableManager().isInTable())) - { - aFramedRedlines.push_back( uno::makeAny(xRange) ); - aFramedRedlines.push_back( uno::makeAny(sType) ); - aFramedRedlines.push_back( uno::makeAny(aRedlineProperties) ); - } + eType = StoredRedlines::FRAME; else if (IsInFootOrEndnote()) + eType = IsInFootnote() ? StoredRedlines::FOOTNOTE : StoredRedlines::ENDNOTE; + else + eType = StoredRedlines::NONE; + + if (eType != StoredRedlines::NONE) { - aFootnoteRedlines.push_back( uno::makeAny(xRange) ); - aFootnoteRedlines.push_back( uno::makeAny(sType) ); - aFootnoteRedlines.push_back( uno::makeAny(aRedlineProperties) ); + m_aStoredRedlines[eType].push_back( uno::makeAny(xRange) ); + m_aStoredRedlines[eType].push_back( uno::makeAny(sType) ); + m_aStoredRedlines[eType].push_back( uno::makeAny(aRedlineProperties) ); } } catch( const uno::Exception & ) @@ -2832,23 +2836,101 @@ void DomainMapper_Impl::PushAnnotation() } } +static void lcl_CopyRedlines( + uno::Reference< text::XText > const& xSrc, + std::deque<css::uno::Any>& rRedlines, + std::vector<sal_Int32>& redPos, + std::vector<sal_Int32>& redLen, + sal_Int32& redIdx) +{ + redIdx = -1; + for( size_t i = 0; i < rRedlines.size(); i+=3) + { + uno::Reference< text::XTextRange > xRange; + rRedlines[i] >>= xRange; + + // is this a redline of the temporary footnote? + uno::Reference<text::XTextCursor> xRangeCursor; + try + { + xRangeCursor = xSrc->createTextCursorByRange( xRange ); + } + catch( const uno::Exception& ) + { + } + if (xRangeCursor.is()) + { + redIdx = i; + sal_Int32 nLen = xRange->getString().getLength(); + redLen.push_back(nLen); + xRangeCursor->gotoRange(xSrc->getStart(), true); + redPos.push_back(xRangeCursor->getString().getLength() - nLen); + } + else + { + // we have already found all redlines of the footnote, + // skip checking the redlines of the other footnotes + if (redIdx > -1) + break; + // failed createTextCursorByRange(), for example, table inside the frame + redLen.push_back(-1); + redPos.push_back(-1); + } + } +} + +static void lcl_PasteRedlines( + uno::Reference< text::XText > const& xDest, + std::deque<css::uno::Any>& rRedlines, + std::vector<sal_Int32>& redPos, + std::vector<sal_Int32>& redLen, + sal_Int32& redIdx) +{ + // create redlines in the copied footnote + for( size_t i = 0; redIdx > -1 && i <= sal::static_int_cast<size_t>(redIdx); i+=3) + { + OUString sType; + beans::PropertyValues aRedlineProperties( 3 ); + // skip failed createTextCursorByRange() + if (redPos[i/3] == -1) + continue; + rRedlines[i+1] >>= sType; + rRedlines[i+2] >>= aRedlineProperties; + uno::Reference< text::XTextCursor > xCrsr = xDest->getText()->createTextCursor(); + xCrsr->goRight(redPos[i/3], false); + xCrsr->goRight(redLen[i/3], true); + uno::Reference < text::XRedline > xRedline( xCrsr, uno::UNO_QUERY_THROW ); + xRedline->makeRedline( sType, aRedlineProperties ); + } +} -void DomainMapper_Impl::PopFootOrEndnote( bool bIsFootnote ) +void DomainMapper_Impl::PopFootOrEndnote() { // content of the footnotes were inserted after the first footnote in temporary footnotes, // restore the content of the actual footnote by copying its content from the first // (remaining) temporary footnote and remove the temporary footnote. // FIXME: add footnote IDs to handle possible differences in footnote serialization uno::Reference< text::XFootnotesSupplier> xFootnotesSupplier( GetTextDocument(), uno::UNO_QUERY ); - if ( bIsFootnote && GetFootnoteCount() > -1 && xFootnotesSupplier.is() ) + uno::Reference< text::XEndnotesSupplier> xEndnotesSupplier( GetTextDocument(), uno::UNO_QUERY ); + if ( IsInFootOrEndnote() && ( ( IsInFootnote() && GetFootnoteCount() > -1 && xFootnotesSupplier.is() ) || + ( !IsInFootnote() && GetEndnoteCount() > -1 && xEndnotesSupplier.is() ) ) ) { - auto xFootnotes = xFootnotesSupplier->getFootnotes(); uno::Reference< text::XFootnote > xFootnoteFirst, xFootnoteLast; - xFootnotes->getByIndex(xFootnotes->getCount()-1) >>= xFootnoteLast; - if ( xFootnotes->getCount() > 1 && xFootnoteLast->getLabel().isEmpty() ) + auto xFootnotes = xFootnotesSupplier->getFootnotes(); + auto xEndnotes = xEndnotesSupplier->getEndnotes(); + if (IsInFootnote()) + xFootnotes->getByIndex(xFootnotes->getCount()-1) >>= xFootnoteLast; + else + xEndnotes->getByIndex(xEndnotes->getCount()-1) >>= xFootnoteLast; + if ( ( ( IsInFootnote() && xFootnotes->getCount() > 1 ) || + ( !IsInFootnote() && xEndnotes->getCount() > 1 ) ) && + xFootnoteLast->getLabel().isEmpty() ) { // copy content of the first remaining temporary footnote - xFootnotes->getByIndex(1) >>= xFootnoteFirst; + if ( IsInFootnote() ) + xFootnotes->getByIndex(1) >>= xFootnoteFirst; + else + xEndnotes->getByIndex(1) >>= xFootnoteFirst; 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; @@ -2858,61 +2940,14 @@ void DomainMapper_Impl::PopFootOrEndnote( bool bIsFootnote ) // copy its redlines std::vector<sal_Int32> redPos, redLen; - sal_Int32 nFirstRedline = -1; - for( size_t i = 0; i < aFootnoteRedlines.size(); i+=3) - { - uno::Reference< text::XTextRange > xRange; - aFootnoteRedlines[i] >>= xRange; - - // is this a redline of the temporary footnote? - uno::Reference<text::XTextCursor> xRangeCursor; - try - { - xRangeCursor = xSrc->createTextCursorByRange( xRange ); - } - catch( const uno::Exception& ) - { - } - if (xRangeCursor.is()) - { - nFirstRedline = i; - sal_Int32 nLen = xRange->getString().getLength(); - redLen.push_back(nLen); - xRangeCursor->gotoRange(xSrc->getStart(), true); - redPos.push_back(xRangeCursor->getString().getLength() - nLen); - } - else - { - // we have already found all redlines of the footnote, - // skip checking the redlines of the other footnotes - if (nFirstRedline > -1) - break; - // failed createTextCursorByRange(), for example, table inside the frame - redLen.push_back(-1); - redPos.push_back(-1); - } - } - - // create redlines in the copied footnote - for( size_t i = 0; nFirstRedline > -1 && i <= sal::static_int_cast<size_t>(nFirstRedline); i+=3) - { - OUString sType; - beans::PropertyValues aRedlineProperties( 3 ); - // skip failed createTextCursorByRange() - if (redPos[i/3] == -1) - continue; - aFootnoteRedlines[i+1] >>= sType; - aFootnoteRedlines[i+2] >>= aRedlineProperties; - uno::Reference< text::XTextCursor > xCrsr = xDest->getText()->createTextCursor(); - xCrsr->goRight(redPos[i/3], false); - xCrsr->goRight(redLen[i/3], true); - uno::Reference < text::XRedline > xRedline( xCrsr, uno::UNO_QUERY_THROW ); - xRedline->makeRedline( sType, aRedlineProperties ); - } + 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; nFirstRedline > -1 && i <= sal::static_int_cast<size_t>(nFirstRedline) + 2; i++) - aFootnoteRedlines.pop_front(); + for( size_t i = 0; redIdx > -1 && i <= sal::static_int_cast<size_t>(redIdx) + 2; i++) + m_aStoredRedlines[eType].pop_front(); // remove temporary footnote xFootnoteFirst->getAnchor()->setString(""); @@ -7055,49 +7090,16 @@ void DomainMapper_Impl::ExecuteFrameConversion() { uno::Reference< text::XTextAppendAndConvert > xTextAppendAndConvert( GetTopTextAppend(), uno::UNO_QUERY_THROW ); // convert redline ranges to cursor movement and character length - - for( size_t i = 0; i < aFramedRedlines.size(); i+=3) - { - uno::Reference< text::XTextRange > xRange; - aFramedRedlines[i] >>= xRange; - uno::Reference<text::XTextCursor> xRangeCursor = GetTopTextAppend()->createTextCursorByRange( xRange ); - if (xRangeCursor.is()) - { - sal_Int32 nLen = xRange->getString().getLength(); - redLen.push_back(nLen); - xRangeCursor->gotoRange(m_xFrameStartRange, true); - redPos.push_back(xRangeCursor->getString().getLength() - nLen); - } - else - { - // failed createTextCursorByRange(), for example, table inside the frame - redLen.push_back(-1); - redPos.push_back(-1); - } - } + sal_Int32 redIdx; + lcl_CopyRedlines(GetTopTextAppend(), m_aStoredRedlines[StoredRedlines::FRAME], redPos, redLen, redIdx); const uno::Reference< text::XTextContent >& xTextContent = xTextAppendAndConvert->convertToTextFrame( m_xFrameStartRange, m_xFrameEndRange, comphelper::containerToSequence(m_aFrameProperties) ); - // create redlines in the previous frame - for( size_t i = 0; i < aFramedRedlines.size(); i+=3) - { - OUString sType; - beans::PropertyValues aRedlineProperties( 3 ); - // skip failed createTextCursorByRange() - if (redPos[i/3] == -1) - continue; - aFramedRedlines[i+1] >>= sType; - aFramedRedlines[i+2] >>= aRedlineProperties; - uno::Reference< text::XTextFrame > xFrame( xTextContent, uno::UNO_QUERY_THROW ); - uno::Reference< text::XTextCursor > xCrsr = xFrame->getText()->createTextCursor(); - xCrsr->goRight(redPos[i/3], false); - xCrsr->goRight(redLen[i/3], true); - uno::Reference < text::XRedline > xRedline( xCrsr, uno::UNO_QUERY_THROW ); - xRedline->makeRedline( sType, aRedlineProperties ); - } + uno::Reference< text::XText > xDest( xTextContent, uno::UNO_QUERY_THROW ); + lcl_PasteRedlines(xDest, m_aStoredRedlines[StoredRedlines::FRAME], redPos, redLen, redIdx); } catch( const uno::Exception&) { @@ -7106,19 +7108,19 @@ void DomainMapper_Impl::ExecuteFrameConversion() m_bIsActualParagraphFramed = false; - if (redPos.size() == aFramedRedlines.size()/3) + if (redPos.size() == m_aStoredRedlines[StoredRedlines::FRAME].size()/3) { - for( sal_Int32 i = aFramedRedlines.size() - 1; i >= 0; --i) + for( sal_Int32 i = m_aStoredRedlines[StoredRedlines::FRAME].size() - 1; i >= 0; --i) { // keep redlines of floating tables to process them in CloseSectionGroup() if ( redPos[i/3] != -1 ) { - aFramedRedlines.erase(aFramedRedlines.begin() + i); + m_aStoredRedlines[StoredRedlines::FRAME].erase(m_aStoredRedlines[StoredRedlines::FRAME].begin() + i); } } } else - aFramedRedlines.clear(); + m_aStoredRedlines[StoredRedlines::FRAME].clear(); } m_xFrameStartRange = nullptr; m_xFrameEndRange = nullptr; @@ -7229,7 +7231,7 @@ void DomainMapper_Impl::RemoveTopRedline( ) { if (m_aRedlines.top().empty()) { - if (GetFootnoteCount() > -1) + if (GetFootnoteCount() > -1 || GetEndnoteCount() > -1) return; SAL_WARN("writerfilter.dmapper", "RemoveTopRedline called with empty stack"); throw uno::Exception("RemoveTopRedline failed", nullptr); @@ -7610,7 +7612,7 @@ void DomainMapper_Impl::substream(Id rName, break; case NS_ooxml::LN_footnote: case NS_ooxml::LN_endnote: - PopFootOrEndnote( NS_ooxml::LN_footnote == rName ); + PopFootOrEndnote(); break; case NS_ooxml::LN_annotation : PopAnnotation(); diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx index 8441b4012152..58cf59b0852c 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx @@ -125,6 +125,15 @@ enum class SkipFootnoteSeparator SKIPPING }; +// type of stored redlines +enum StoredRedlines +{ + FRAME = 0, + FOOTNOTE, + ENDNOTE, + NONE +}; + /** * Storage for state that is relevant outside a header/footer, but not inside it. * @@ -520,13 +529,15 @@ private: } m_eInHeaderFooterImport; bool m_bDiscardHeaderFooter; bool m_bInFootOrEndnote; + bool m_bInFootnote; PropertyMapPtr m_pFootnoteContext; bool m_bHasFootnoteStyle; bool m_bCheckFootnoteStyle; /// Skip paragraphs from the <w:separator/> footnote SkipFootnoteSeparator m_eSkipFootnoteState; - /// preload footnotes + /// preload footnotes and endnotes sal_Int32 m_nFootnotes; + sal_Int32 m_nEndnotes; bool m_bLineNumberingSet; bool m_bIsInFootnoteProperties; @@ -708,6 +719,7 @@ public: void PopProperties(ContextType eId); ContextType GetTopContextType() const { return m_aContextStack.top(); } + int GetCContext() const { return m_aContextStack.size(); } const PropertyMapPtr& GetTopContext() const { return m_pTopContext; @@ -792,8 +804,9 @@ public: bool IsInTOC() const { return m_bStartTOC; } void PushFootOrEndnote( bool bIsFootnote ); - void PopFootOrEndnote( bool bIsFootnote ); + void PopFootOrEndnote(); bool IsInFootOrEndnote() const { return m_bInFootOrEndnote; } + bool IsInFootnote() const { return m_bInFootnote; } void StartCustomFootnote(const PropertyMapPtr pContext); void EndCustomFootnote(); @@ -808,6 +821,8 @@ public: void SetSkipFootnoteState(SkipFootnoteSeparator eId) { m_eSkipFootnoteState = eId; } sal_Int32 GetFootnoteCount() const { return m_nFootnotes; } void IncrementFootnoteCount() { ++m_nFootnotes; } + sal_Int32 GetEndnoteCount() const { return m_nEndnotes; } + void IncrementEndnoteCount() { ++m_nEndnotes; } void PushAnnotation(); void PopAnnotation(); @@ -1091,11 +1106,10 @@ public: /// start/end node. void ClearPreviousParagraph(); - /// Handle redline text portions in frames: - /// store their data, and create them after frame creation + /// Handle redline text portions in a frame, footnotes and redlines: + /// store their data, and create them after frame creation or footnote/endnote copying bool m_bIsActualParagraphFramed; - std::vector<css::uno::Any> aFramedRedlines; - std::deque<css::uno::Any> aFootnoteRedlines; + std::deque<css::uno::Any> m_aStoredRedlines[StoredRedlines::NONE]; bool IsParaWithInlineObject() const { return m_bParaWithInlineObject; } diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx index 6f8f8d5a9b1e..50af6463fcf1 100644 --- a/writerfilter/source/dmapper/PropertyMap.cxx +++ b/writerfilter/source/dmapper/PropertyMap.cxx @@ -1374,7 +1374,7 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl ) rInfo.m_nBreakType = m_nBreakType; if ( FloatingTableConversion( rDM_Impl, rInfo ) ) { - std::vector<css::uno::Any> aFramedRedlines = rDM_Impl.aFramedRedlines; + std::deque<css::uno::Any> aFramedRedlines = rDM_Impl.m_aStoredRedlines[StoredRedlines::FRAME]; try { // convert redline ranges to cursor movement and character length diff --git a/writerfilter/source/ooxml/OOXMLDocumentImpl.cxx b/writerfilter/source/ooxml/OOXMLDocumentImpl.cxx index 938d977d3348..d8b520cb2e1a 100644 --- a/writerfilter/source/ooxml/OOXMLDocumentImpl.cxx +++ b/writerfilter/source/ooxml/OOXMLDocumentImpl.cxx @@ -293,8 +293,8 @@ void OOXMLDocumentImpl::resolveEndnote(Stream & rStream, Id aType, const sal_Int32 nNoteId) { - writerfilter::Reference<Stream>::Pointer_t pStream = - getXNoteStream(OOXMLStream::ENDNOTES, nNoteId); + if (!mpXNoteStream) + mpXNoteStream = getXNoteStream(OOXMLStream::ENDNOTES, nNoteId); Id nId; switch (aType) @@ -308,7 +308,7 @@ void OOXMLDocumentImpl::resolveEndnote(Stream & rStream, break; } - resolveFastSubStreamWithId(rStream, pStream, nId); + resolveFastSubStreamWithId(rStream, mpXNoteStream, nId); } void OOXMLDocumentImpl::resolveComment(Stream & rStream, diff --git a/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx b/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx index fe02597775bd..f04391bd95db 100644 --- a/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx +++ b/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx @@ -166,7 +166,7 @@ void SAL_CALL OOXMLFastContextHandler::startFastElement mbPreserveSpace = Attribs->getValue(oox::NMSP_xml | oox::XML_space) == "preserve"; mbPreserveSpaceSet = true; } - if (Element == W_TOKEN(footnote)) + if (Element == W_TOKEN(footnote) || Element == W_TOKEN(endnote)) { // send uFtnSep to sign new footnote content, but skip footnote separators if (!Attribs->hasAttribute(W_TOKEN(type)) || |