sw/qa/extras/rtfexport/data/tdf136445-1-min.rtf   |   17 ++
 sw/qa/extras/rtfexport/rtfexport8.cxx             |   14 ++
 sw/qa/extras/rtfimport/rtfimport.cxx              |    7 -
 writerfilter/source/dmapper/DomainMapper_Impl.cxx |  133 +++++++++-------------
 writerfilter/source/dmapper/DomainMapper_Impl.hxx |   85 +++++++-------
 writerfilter/source/rtftok/rtfdispatchsymbol.cxx  |    2 
 6 files changed, 141 insertions(+), 117 deletions(-)

New commits:
commit 31b91266357b2215372596181108aa6cd2513182
Author:     Michael Stahl <[email protected]>
AuthorDate: Thu Feb 1 12:53:07 2024 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Fri Feb 9 14:54:12 2024 +0100

    writerfilter: fix missing paragraph break on tdf136445-1.rtf
    
    This causes an assert:
    crossrefbookmark.cxx:44: sw::mark::CrossRefBookmark::CrossRefBookmark(): 
Assertion `IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark(rPaM) && 
"<CrossRefBookmark::CrossRefBookmark(..)>" "- creation of cross-reference 
bookmark with an illegal PaM that does not expand over exactly one whole 
paragraph."' failed.
    
    The problem is that there is an annotation at the end of a paragraph,
    and reading the annotation changes various members of DomainMapper_Impl,
    in particular m_bParaSectpr and m_bParaChanged, causing "bRemove" in
    DomainMapper::lcl_utext() to be erroneously true, removing the just
    inserted paragraph break again.
    
    Move all flags that are evaluated for bRemove to SubstreamContext.
    
    This causes one test failure, but it turns out that the new result is
    the same as in Word 2013.
    
      Test name: (anonymous namespace)::testTdf108947::TestBody
      equality assertion failed
      - Expected: Header Page 2 ?
      - Actual  :
      Header Page 2 ?
    
    (regression from commit 15b886f460919ea3dce425a621dc017c2992a96b)
    
    Change-Id: I44a7a8928ab04c600d4d3c43bc4e4deeafe57d89
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162932
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 86ad08f9d25110e91e92a0badf6de75e785b3644)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162937
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/sw/qa/extras/rtfexport/data/tdf136445-1-min.rtf 
b/sw/qa/extras/rtfexport/data/tdf136445-1-min.rtf
new file mode 100644
index 000000000000..c0abd0d293be
--- /dev/null
+++ b/sw/qa/extras/rtfexport/data/tdf136445-1-min.rtf
@@ -0,0 +1,17 @@
+{ tf1
+{\*\listtable
+{\list\listtemplateid8
+{\listlevel\levelnfc0\leveljc0\levelstartat1\levelfollow0{\leveltext 
\'02E\'00;}{\levelnumbers\'02;}i-720\li720}\listid8}
+}
+{\listoverridetable{\listoverride\listid8\listoverridecount0\ls8}}
+
+\ltrpar \sectd
+\pard\plain \ltrpar{ tlch\hich \ltrch\loch
+I ax xoixx xuxixx xxe xixxx. (Xaxxexx 1989 x.x. xaxax a)}{ tlch\hich 
\ltrch\loch
+{\*tnid CL}{\*tnauthor Christian}+sic!}}}
+\par \pard\plain {\listtext\pard\plain  E119   ab}\ilvl0\ls8 \li720 i0\lin720 
in0i-720\ql\ltrpar{{\*kmkstart __RefNumPara__395941_134077278}{\*kmkend 
__RefNumPara__395941_134077278} tlch\hich \ltrch\loch
+Xix    ab xaxa ab x-a  ab      ab      ab xix  ab }{ tlch\hich 
\ltrch\langfe0\dbch\loch\lang255\loch
+x xi = xa.}
+\par
+}
diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx 
b/sw/qa/extras/rtfexport/rtfexport8.cxx
index d0b2ed0f2c2e..9e76fb153cb0 100644
--- a/sw/qa/extras/rtfexport/rtfexport8.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport8.cxx
@@ -106,6 +106,20 @@ DECLARE_RTFEXPORT_TEST(testTdf158586_lostFrame, 
"tdf158586_lostFrame.rtf")
     CPPUNIT_ASSERT_EQUAL(2, getPages());
 }
 
+DECLARE_RTFEXPORT_TEST(testAnnotationPar, "tdf136445-1-min.rtf")
+{
+    // the problem was that the paragraph break following annotation was 
missing
+    CPPUNIT_ASSERT_EQUAL(2, getParagraphs());
+    CPPUNIT_ASSERT_EQUAL(
+        OUString("Annotation"),
+        getProperty<OUString>(
+            getRun(getParagraph(1, "I ax xoixx xuxixx xxe xixxx. (Xaxxexx 1989 
x.x. xaxax a)"), 2),
+            "TextPortionType"));
+    CPPUNIT_ASSERT(
+        !getProperty<OUString>(getParagraph(2, "Xix    xaxa    x-a             
        xix     x xi = xa."), "ListId")
+             .isEmpty());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx 
b/sw/qa/extras/rtfimport/rtfimport.cxx
index 203bce99df23..9615c32f9b05 100644
--- a/sw/qa/extras/rtfimport/rtfimport.cxx
+++ b/sw/qa/extras/rtfimport/rtfimport.cxx
@@ -1570,7 +1570,12 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf108947)
     uno::Reference<text::XText> xHeaderTextLeft = 
getProperty<uno::Reference<text::XText>>(
         getStyles("PageStyles")->getByName("Default Page Style"), 
"HeaderTextLeft");
     aActual = xHeaderTextLeft->getString();
-    CPPUNIT_ASSERT_EQUAL(OUString(SAL_NEWLINE_STRING "Header Page 2 ?"), 
aActual);
+    CPPUNIT_ASSERT_EQUAL(
+        OUString(
+            SAL_NEWLINE_STRING
+                // note: there should be *one* break here - as it is in the 
24.2 branch - but somehow in the 7.6 branch there are 2; most likely it was 
fixed by commit 4b0fa253a4540f5461397815d290586f9ddabe61 which is a bit large 
to backport
+                SAL_NEWLINE_STRING "Header Page 2 ?"),
+        aActual);
 #endif
 }
 
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 5a008bd868bf..e1a4d04631db 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -367,16 +367,10 @@ DomainMapper_Impl::DomainMapper_Impl(
         m_bIsParaMarkerChange( false ),
         m_bIsParaMarkerMove( false ),
         m_bRedlineImageInPreviousRun( false ),
-        m_bParaChanged( false ),
-        m_bIsFirstParaInSection( true ),
-        m_bIsFirstParaInSectionAfterRedline( true ),
         m_bDummyParaAddedForTableInSection( false ),
         m_bDummyParaAddedForTableInSectionPage( false ),
         m_bTextFrameInserted(false),
-        m_bIsPreviousParagraphFramed( false ),
-        m_bIsLastParaInSection( false ),
         m_bIsLastSectionGroup( false ),
-        m_bParaSectpr( false ),
         m_bUsingEnhancedFields( false ),
         m_bSdt(false),
         m_bIsFirstRun(false),
@@ -398,7 +392,6 @@ DomainMapper_Impl::DomainMapper_Impl(
         m_bIsSplitPara(false),
         m_bIsActualParagraphFramed( false ),
         m_bParaAutoBefore(false),
-        m_bParaWithInlineObject(false),
         m_bSaxError(false)
 {
     m_StreamStateStack.emplace(); // add state for document body
@@ -894,25 +887,27 @@ void DomainMapper_Impl::SetIsLastSectionGroup( bool 
bIsLast )
 
 void DomainMapper_Impl::SetIsLastParagraphInSection( bool bIsLast )
 {
-    m_bIsLastParaInSection = bIsLast;
+    m_StreamStateStack.top().bIsLastParaInSection = bIsLast;
 }
 
 
 void DomainMapper_Impl::SetIsFirstParagraphInSection( bool bIsFirst )
 {
-    m_bIsFirstParaInSection = bIsFirst;
+    m_StreamStateStack.top().bIsFirstParaInSection = bIsFirst;
 }
 
 void DomainMapper_Impl::SetIsFirstParagraphInSectionAfterRedline( bool 
bIsFirstAfterRedline )
 {
-    m_bIsFirstParaInSectionAfterRedline = bIsFirstAfterRedline;
+    m_StreamStateStack.top().bIsFirstParaInSectionAfterRedline = 
bIsFirstAfterRedline;
 }
 
 bool DomainMapper_Impl::GetIsFirstParagraphInSection( bool bAfterRedline ) 
const
 {
     // Anchored objects may include multiple paragraphs,
     // and none of them should be considered the first para in section.
-    return ( bAfterRedline ? m_bIsFirstParaInSectionAfterRedline : 
m_bIsFirstParaInSection )
+    return (bAfterRedline
+                    ? 
m_StreamStateStack.top().bIsFirstParaInSectionAfterRedline
+                    : m_StreamStateStack.top().bIsFirstParaInSection)
                 && !IsInShape()
                 && !IsInComments()
                 && !IsInFootOrEndnote();
@@ -940,13 +935,11 @@ void DomainMapper_Impl::SetIsTextFrameInserted( bool 
bIsInserted )
     m_bTextFrameInserted  = bIsInserted;
 }
 
-
 void DomainMapper_Impl::SetParaSectpr(bool bParaSectpr)
 {
-    m_bParaSectpr = bParaSectpr;
+    m_StreamStateStack.top().bParaSectpr = bParaSectpr;
 }
 
-
 void DomainMapper_Impl::SetSdt(bool bSdt)
 {
     m_bSdt = bSdt;
@@ -2787,7 +2780,7 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
 
                     xCur->goLeft( 1 , true );
                     // Extend the redline ranges for empty paragraphs
-                    if ( !m_bParaChanged && m_previousRedline )
+                    if (!m_StreamStateStack.top().bParaChanged && 
m_previousRedline)
                         CreateRedline( xCur, m_previousRedline );
                     CheckParaMarkerRedline( xCur );
                 }
@@ -2931,21 +2924,21 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
     else
         SetIsPreviousParagraphFramed(false);
 
-    m_bRemoveThisParagraph = false;
+    m_StreamStateStack.top().bRemoveThisParagraph = false;
     if( !IsInHeaderFooter() && !IsInShape()
         && (!pParaContext || !pParaContext->props().IsFrameMode()) )
     { // If the paragraph is in a frame, shape or header/footer, it's not a 
paragraph of the section itself.
         SetIsFirstParagraphInSection(false);
         // don't count an empty deleted paragraph as first paragraph in 
section to avoid of
         // the deletion of the next empty paragraph later, resulting loss of 
the associated page break
-        if (!m_previousRedline || m_bParaChanged)
+        if (!m_previousRedline || m_StreamStateStack.top().bParaChanged)
         {
             SetIsFirstParagraphInSectionAfterRedline(false);
             SetIsLastParagraphInSection(false);
         }
     }
     m_previousRedline.clear();
-    m_bParaChanged = false;
+    m_StreamStateStack.top().bParaChanged = false;
 
     if (IsInComments() && pParaContext)
     {
@@ -2982,7 +2975,7 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
     }
 
     m_bParaAutoBefore = false;
-    m_bParaWithInlineObject = false;
+    m_StreamStateStack.top().bParaWithInlineObject = false;
 
 #ifdef DBG_UTIL
     TagLogger::getInstance().endElement();
@@ -3120,7 +3113,7 @@ void DomainMapper_Impl::appendTextPortion( const 
OUString& rString, const Proper
             m_pParaMarkerRedlineMove.clear();
         }
         CheckRedline( xTextRange );
-        m_bParaChanged = true;
+        m_StreamStateStack.top().bParaChanged = true;
 
         //getTableManager( ).handle(xTextRange);
     }
@@ -3770,7 +3763,8 @@ void DomainMapper_Impl::CheckRedline( uno::Reference< 
text::XTextRange > const&
 
     // only export ParagraphFormat, when there is no other redline in the same 
text portion to avoid missing redline compression,
     // but always export the first ParagraphFormat redline in a paragraph to 
keep the paragraph style change data for rejection
-    if( (!bUsedRange || !m_bParaChanged) && 
GetTopContextOfType(CONTEXT_PARAGRAPH) )
+    if ((!bUsedRange || !m_StreamStateStack.top().bParaChanged)
+        && GetTopContextOfType(CONTEXT_PARAGRAPH))
     {
         std::vector<RedlineParamsPtr>& avRedLines = 
GetTopContextOfType(CONTEXT_PARAGRAPH)->Redlines();
         for( const auto& rRedline : avRedLines )
@@ -4415,7 +4409,7 @@ void DomainMapper_Impl::PushShapeContext( const 
uno::Reference< drawing::XShape
                         getPropertyName( PROP_OPAQUE ),
                         uno::Any( true ) );
         }
-        m_bParaChanged = true;
+        m_StreamStateStack.top().bParaChanged = true;
         getTableManager().setIsInShape(true);
     }
     catch ( const uno::Exception& )
@@ -6398,15 +6392,15 @@ OUString DomainMapper_Impl::extractTocTitle()
 css::uno::Reference<css::beans::XPropertySet>
 DomainMapper_Impl::StartIndexSectionChecked(const OUString& sServiceName)
 {
-    if (m_bParaChanged)
+    if (m_StreamStateStack.top().bParaChanged)
     {
-        finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH), false); // 
resets m_bParaChanged
+        finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH), false); // 
resets bParaChanged
         PopProperties(CONTEXT_PARAGRAPH);
         PushProperties(CONTEXT_PARAGRAPH);
         SetIsFirstRun(true);
         // The first paragraph of the index that is continuation of just 
finished one needs to be
-        // removed when finished (unless more content will arrive, which will 
set m_bParaChanged)
-        m_bRemoveThisParagraph = true;
+        // removed when finished (unless more content will arrive, which will 
set bParaChanged)
+        m_StreamStateStack.top().bRemoveThisParagraph = true;
     }
     const auto& xTextAppend = GetTopTextAppend();
     const auto xTextRange = xTextAppend->getEnd();
@@ -8184,7 +8178,7 @@ void DomainMapper_Impl::PopFieldContext()
                     if (m_bStartedTOC || m_bStartIndex || m_bStartBibliography)
                     {
                         // inside SDT, last empty paragraph is also part of 
index
-                        if (!m_bParaChanged && !m_xSdtEntryStart)
+                        if (!m_StreamStateStack.top().bParaChanged && 
!m_xSdtEntryStart)
                         {
                             // End of index is the first item on a new 
paragraph - this paragraph
                             // should not be part of index
@@ -8210,7 +8204,7 @@ void DomainMapper_Impl::PopFieldContext()
                         m_bStartedTOC = false;
                         m_aTextAppendStack.pop();
                         m_StreamStateStack.top().bTextInserted = false;
-                        m_bParaChanged = true; // the paragraph must stay 
anyway
+                        m_StreamStateStack.top().bParaChanged = true; // the 
paragraph must stay anyway
                     }
                     m_bStartTOC = false;
                     m_bStartIndex = false;
@@ -8799,7 +8793,7 @@ void  DomainMapper_Impl::ImportGraphic(const 
writerfilter::Reference<Properties>
         }
         else if (m_eGraphicImportType == IMPORT_AS_DETECTED_INLINE)
         {
-            m_bParaWithInlineObject = true;
+            m_StreamStateStack.top().bParaWithInlineObject = true;
 
             // store inline images with track changes, because the anchor point
             // to set redlining is not available yet
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 24477d96ac5d..2cffecf47fb9 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -177,14 +177,26 @@ struct SubstreamContext
      * inTbl SPRM or not).
      */
     sal_Int32 nTableDepth = 0;
-    // deferred breaks need to be saved for RTF, probably not for DOCX
-    bool      bIsColumnBreakDeferred = false;
-    bool      bIsPageBreakDeferred = false;
+    // deferred breaks need to be saved for RTF, also for DOCX annotations
+    bool bIsColumnBreakDeferred = false;
+    bool bIsPageBreakDeferred = false;
     sal_Int32 nLineBreaksDeferred = 0;
     /// Current paragraph had at least one field in it.
-    bool      bParaHadField = false;
+    bool bParaHadField = false;
     /// Current paragraph in a table is first paragraph of a cell
-    bool      bFirstParagraphInCell = true;
+    bool bFirstParagraphInCell = true;
+    /// If the current paragraph has any runs.
+    bool bParaChanged = false;
+    bool bIsFirstParaInSectionAfterRedline = true;
+    bool bIsFirstParaInSection = true;
+    bool bIsLastParaInSection = false;
+    /// If the current paragraph contains section property definitions.
+    bool bParaSectpr = false;
+    bool bIsPreviousParagraphFramed = false;
+    /// Current paragraph had at least one inline object in it.
+    bool bParaWithInlineObject = false;
+    /// This is a continuation of already finished paragraph - e.g., first in 
an index section
+    bool bRemoveThisParagraph = false;
 };
 
 /// Information about a paragraph to be finished after a field end.
@@ -597,26 +609,16 @@ private:
     // text ZWSPs to keep the change tracking of the image in Writer.)
     bool                            m_bRedlineImageInPreviousRun;
 
-    /// If the current paragraph has any runs.
-    bool                            m_bParaChanged;
-    bool                            m_bIsFirstParaInSection;
-    bool                            m_bIsFirstParaInSectionAfterRedline;
     bool                            m_bIsFirstParaInShape = false;
     bool                            m_bDummyParaAddedForTableInSection;
     bool                            m_bDummyParaAddedForTableInSectionPage;
     bool                            m_bTextFrameInserted;
-    bool                            m_bIsPreviousParagraphFramed;
-    bool                            m_bIsLastParaInSection;
     bool                            m_bIsLastSectionGroup;
-    /// If the current paragraph contains section property definitions.
-    bool                            m_bParaSectpr;
     bool                            m_bUsingEnhancedFields;
     /// If the current paragraph is inside a structured document element.
     bool                            m_bSdt;
     bool                            m_bIsFirstRun;
     bool                            m_bIsOutsideAParagraph;
-    /// This is a continuation of already finished paragraph - e.g., first in 
an index section
-    bool                            m_bRemoveThisParagraph = false;
 
     css::uno::Reference< css::text::XTextCursor > m_xTOCMarkerCursor;
 
@@ -708,7 +710,7 @@ public:
     void RemoveLastParagraph( );
     void SetIsDecimalComma() { m_bIsDecimalComma = true; };
     void SetIsLastParagraphInSection( bool bIsLast );
-    bool GetIsLastParagraphInSection() const { return m_bIsLastParaInSection;}
+    bool GetIsLastParagraphInSection() const { return 
m_StreamStateStack.top().bIsLastParaInSection; }
     void SetRubySprmId( sal_uInt32 nSprmId) { m_aRubyInfo.nSprmId = nSprmId ; }
     void SetRubyText( OUString const &sText, OUString const &sStyle) {
         m_aRubyInfo.sRubyText = sText;
@@ -734,10 +736,11 @@ public:
     bool GetIsTextFrameInserted() const { return m_bTextFrameInserted;}
     void SetIsTextDeleted(bool bIsTextDeleted) { m_bTextDeleted = 
bIsTextDeleted; }
 
-    void SetIsPreviousParagraphFramed( bool bIsFramed ) { 
m_bIsPreviousParagraphFramed = bIsFramed; }
-    bool GetIsPreviousParagraphFramed() const { return 
m_bIsPreviousParagraphFramed; }
+    void SetIsPreviousParagraphFramed(bool const bIsFramed)
+    { m_StreamStateStack.top().bIsPreviousParagraphFramed = bIsFramed; }
+    bool GetIsPreviousParagraphFramed() const { return 
m_StreamStateStack.top().bIsPreviousParagraphFramed; }
     void SetParaSectpr(bool bParaSectpr);
-    bool GetParaSectpr() const { return m_bParaSectpr;}
+    bool GetParaSectpr() const { return m_StreamStateStack.top().bParaSectpr; }
 
     void SetSymbolChar( sal_Int32 nSymbol) { m_aSymbolData.cSymbol = 
sal_Unicode(nSymbol); }
     void SetSymbolFont( OUString const &rName ) { m_aSymbolData.sFont = rName; 
}
@@ -753,9 +756,9 @@ public:
 
     /// Getter method for m_bSdt.
     bool GetSdt() const { return m_bSdt;}
-    bool GetParaChanged() const { return m_bParaChanged;}
+    bool GetParaChanged() const { return 
m_StreamStateStack.top().bParaChanged; }
     bool GetParaHadField() const { return 
m_StreamStateStack.top().bParaHadField; }
-    bool GetRemoveThisPara() const { return m_bRemoveThisParagraph; }
+    bool GetRemoveThisPara() const { return 
m_StreamStateStack.top().bRemoveThisParagraph; }
 
     void deferBreak( BreakType deferredBreakType );
     bool isBreakDeferred( BreakType deferredBreakType );
@@ -1187,7 +1190,7 @@ public:
     bool m_bIsActualParagraphFramed;
     std::deque<css::uno::Any> m_aStoredRedlines[StoredRedlines::NONE];
 
-    bool IsParaWithInlineObject() const { return m_bParaWithInlineObject; }
+    bool IsParaWithInlineObject() const { return 
m_StreamStateStack.top().bParaWithInlineObject; }
 
     css::uno::Reference< css::embed::XStorage > m_xDocumentStorage;
 
@@ -1217,8 +1220,6 @@ private:
     css::uno::Reference<css::beans::XPropertySet> m_xPreviousParagraph;
     /// Current paragraph has automatic before spacing.
     bool m_bParaAutoBefore;
-    /// Current paragraph had at least one inline object in it.
-    bool m_bParaWithInlineObject;
     /// SAXException was seen so document will be abandoned
     bool m_bSaxError;
 
diff --git a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx 
b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
index c7eee5b0f4b1..b879894d5d6b 100644
--- a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
+++ b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
@@ -410,6 +410,8 @@ RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword 
nKeyword)
                 }
                 sal_uInt8 const sBreak[] = { 0xc };
                 Mapper().text(sBreak, 1);
+                // testFdo81892 don't do another \par break directly; because 
of
+                // GetSplitPgBreakAndParaMark() it does finishParagraph *twice*
                 m_bNeedCr = true;
             }
         }
commit 90e6bbc335e551e2f67e70c70d5b58afa23bc8e8
Author:     Michael Stahl <[email protected]>
AuthorDate: Wed Jan 31 14:47:22 2024 +0100
Commit:     Caolán McNamara <[email protected]>
CommitDate: Fri Feb 9 14:54:02 2024 +0100

    writerfilter: move members to SubstreamContext (squashed)
    
    writerfilter: move m_bParaHadField to SubstreamContext
    
    Change-Id: Ie15e35d304a423bfa3d7b7ead71015d5ec1228d4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162839
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 4913812baeabd44b46302e54b73a227e760c688a)
    
    writerfilter: use SubstreamContext for all substreams
    
    <vmiklos> possibly just nobody needed that so far. could be some more
    general SubstreamContext, i don't see an obvious problem reusing that at
    more places.
    
    Change-Id: If0749155452f65f8dfc4ac2b10f91bb8e48a6b2b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162840
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 95b01848b18283fd2f903c982108ccdb8efee022)
    
    writerfilter: move m_bFirstParagraphInCell to SubstreamContext
    
    This is a change to set it for all substreams.
    
    Change-Id: I44ed9a5485000f40f8ccfe3ec885ef8f05f5aab2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162841
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 30323c813977eb0127251848fecd2532dce75749)
    
    writerfilter: replace members w/ SubstreamContext::eSubstreamType
    
    This should not change any behaviour.
    
    Change-Id: Ic970f0e1b6401119d875c9e811589b9c210e0c34
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162842
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit 992f7114ab8645fb5b7a22b5f974a95fe7be7712)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162934
    Reviewed-by: Caolán McNamara <[email protected]>

diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 2461c62b023b..5a008bd868bf 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -354,10 +354,7 @@ DomainMapper_Impl::DomainMapper_Impl(
         m_bInStyleSheetImport( false ),
         m_bInNumberingImport(false),
         m_bInAnyTableImport( false ),
-        m_eInHeaderFooterImport( HeaderFooterImportState::none ),
         m_bDiscardHeaderFooter( false ),
-        m_bInFootOrEndnote(false),
-        m_bInFootnote(false),
         m_bHasFootnoteStyle(false),
         m_bCheckFootnoteStyle(false),
         m_eSkipFootnoteState(SkipFootnoteSeparator::OFF),
@@ -379,7 +376,6 @@ DomainMapper_Impl::DomainMapper_Impl(
         m_bIsPreviousParagraphFramed( false ),
         m_bIsLastParaInSection( false ),
         m_bIsLastSectionGroup( false ),
-        m_bIsInComments( false ),
         m_bParaSectpr( false ),
         m_bUsingEnhancedFields( false ),
         m_bSdt(false),
@@ -401,11 +397,7 @@ DomainMapper_Impl::DomainMapper_Impl(
         m_bIgnoreNextTab(false),
         m_bIsSplitPara(false),
         m_bIsActualParagraphFramed( false ),
-        m_bParaHadField(false),
-        m_bSaveParaHadField(false),
         m_bParaAutoBefore(false),
-        m_bFirstParagraphInCell(true),
-        m_bSaveFirstParagraphInCell(false),
         m_bParaWithInlineObject(false),
         m_bSaxError(false)
 {
@@ -922,7 +914,7 @@ bool DomainMapper_Impl::GetIsFirstParagraphInSection( bool 
bAfterRedline ) const
     // and none of them should be considered the first para in section.
     return ( bAfterRedline ? m_bIsFirstParaInSectionAfterRedline : 
m_bIsFirstParaInSection )
                 && !IsInShape()
-                && !m_bIsInComments
+                && !IsInComments()
                 && !IsInFootOrEndnote();
 }
 
@@ -2318,7 +2310,7 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
         {
             if ( GetIsFirstParagraphInShape() ||
                  (GetIsFirstParagraphInSection() && GetSectionContext() && 
GetSectionContext()->IsFirstSection()) ||
-                (m_bFirstParagraphInCell
+                (m_StreamStateStack.top().bFirstParagraphInCell
                  && 0 < m_StreamStateStack.top().nTableDepth
                  && m_StreamStateStack.top().nTableDepth == m_nTableCellDepth))
             {
@@ -2573,7 +2565,8 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
                 else
                 {
                     uno::Reference<text::XTextCursor> xCursor;
-                    if (m_bParaHadField && !m_bIsInComments && 
!m_xTOCMarkerCursor.is())
+                    if (m_StreamStateStack.top().bParaHadField
+                        && !IsInComments() && !m_xTOCMarkerCursor.is())
                     {
                         // Workaround to make sure char props of the field are 
not lost.
                         // Not relevant for editeng-based comments.
@@ -2615,9 +2608,10 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
                                 TOOLS_WARN_EXCEPTION("writerfilter", 
"DomainMapper_Impl::finishParagraph NumberingRules");
                             }
                         }
-                        else if ( 
m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("NumberingStyleName")
 &&
+                        else if 
(m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("NumberingStyleName")
                                 // don't update before tables
-                                (m_StreamStateStack.top().nTableDepth == 0 || 
!m_bFirstParagraphInCell))
+                            && (m_StreamStateStack.top().nTableDepth == 0
+                                || 
!m_StreamStateStack.top().bFirstParagraphInCell))
                         {
                             aCurrentNumberingName = GetListStyleName(nListId);
                             
m_xPreviousParagraph->getPropertyValue("NumberingStyleName") >>= 
aPreviousNumberingName;
@@ -2801,7 +2795,7 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
                 css::uno::Reference<css::beans::XPropertySet> 
xParaProps(xTextRange, uno::UNO_QUERY);
 
                 // table style precedence and not hidden shapes anchored to 
hidden empty table paragraphs
-                if (xParaProps && !m_bIsInComments
+                if (xParaProps && !IsInComments()
                     && (0 < m_StreamStateStack.top().nTableDepth
                         || !m_aAnchoredObjectAnchors.empty()))
                 {
@@ -2953,7 +2947,7 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
     m_previousRedline.clear();
     m_bParaChanged = false;
 
-    if (m_bIsInComments && pParaContext)
+    if (IsInComments() && pParaContext)
     {
         if (const OUString sParaId = pParaContext->props().GetParaId(); 
!sParaId.isEmpty())
         {
@@ -2976,15 +2970,15 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
     }
 
     SetIsOutsideAParagraph(true);
-    m_bParaHadField = false;
+    m_StreamStateStack.top().bParaHadField = false;
 
     // don't overwrite m_bFirstParagraphInCell in table separator nodes
     // and in text boxes anchored to the first paragraph of table cells
     if (0 < m_StreamStateStack.top().nTableDepth
         && m_StreamStateStack.top().nTableDepth == m_nTableCellDepth
-        && !IsInShape() && !m_bIsInComments)
+        && !IsInShape() && !IsInComments())
     {
-        m_bFirstParagraphInCell = false;
+        m_StreamStateStack.top().bFirstParagraphInCell = false;
     }
 
     m_bParaAutoBefore = false;
@@ -3014,7 +3008,7 @@ void DomainMapper_Impl::appendTextPortion( const 
OUString& rString, const Proper
     try
     {
         // If we are in comments, then disable CharGrabBag, comment text 
doesn't support that.
-        uno::Sequence< beans::PropertyValue > aValues = 
pPropertyMap->GetPropertyValues(/*bCharGrabBag=*/!m_bIsInComments);
+        uno::Sequence<beans::PropertyValue> aValues = 
pPropertyMap->GetPropertyValues(/*bCharGrabBag=*/!IsInComments());
 
         if (IsInTOC() || m_bStartIndex || m_bStartBibliography)
             for( auto& rValue : asNonConstRange(aValues) )
@@ -3484,17 +3478,13 @@ void 
DomainMapper_Impl::ConvertHeaderFooterToTextFrame(bool bDynamicHeightTop, b
 
 void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader, 
SectionPropertyMap::PageType eType)
 {
-    m_bSaveParaHadField = m_bParaHadField;
-    m_StreamStateStack.emplace();
-
     const PropertyIds ePropIsOn = bHeader? PROP_HEADER_IS_ON: 
PROP_FOOTER_IS_ON;
     const PropertyIds ePropShared = bHeader? PROP_HEADER_IS_SHARED: 
PROP_FOOTER_IS_SHARED;
     const PropertyIds ePropTextLeft = bHeader? PROP_HEADER_TEXT_LEFT: 
PROP_FOOTER_TEXT_LEFT;
     const PropertyIds ePropText = bHeader? PROP_HEADER_TEXT: PROP_FOOTER_TEXT;
 
     m_bDiscardHeaderFooter = true;
-    m_eInHeaderFooterImport
-        = bHeader ? HeaderFooterImportState::header : 
HeaderFooterImportState::footer;
+    m_StreamStateStack.top().eSubstreamType = bHeader ? SubstreamType::Header 
: SubstreamType::Footer;
 
     //get the section context
     PropertyMapPtr pContext = 
DomainMapper_Impl::GetTopContextOfType(CONTEXT_SECTION);
@@ -3602,21 +3592,13 @@ void DomainMapper_Impl::PopPageHeaderFooter()
         }
         m_bDiscardHeaderFooter = false;
     }
-    m_eInHeaderFooterImport = HeaderFooterImportState::none;
-
-    assert(!m_StreamStateStack.empty());
-    m_StreamStateStack.pop();
-
-    m_bParaHadField = m_bSaveParaHadField;
 }
 
 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;
+    SAL_WARN_IF(m_StreamStateStack.top().eSubstreamType != 
SubstreamType::Body, "writerfilter.dmapper", "PushFootOrEndnote() is called 
from another foot or endnote");
+    m_StreamStateStack.top().eSubstreamType = bIsFootnote ? 
SubstreamType::Footnote : SubstreamType::Endnote;
     m_bCheckFirstFootnoteTab = true;
-    m_bSaveFirstParagraphInCell = m_bFirstParagraphInCell;
     try
     {
         // Redlines outside the footnote should not affect footnote content
@@ -3847,7 +3829,7 @@ void DomainMapper_Impl::PushAnnotation()
 {
     try
     {
-        m_bIsInComments = true;
+        m_StreamStateStack.top().eSubstreamType = SubstreamType::Annotation;
         if (!GetTextFactory().is())
             return;
         m_xAnnotationField.set( GetTextFactory()->createInstance( 
"com.sun.star.text.TextField.Annotation" ),
@@ -4109,16 +4091,13 @@ void DomainMapper_Impl::PopFootOrEndnote()
     }
     m_aRedlines.pop();
     m_eSkipFootnoteState = SkipFootnoteSeparator::OFF;
-    m_bInFootOrEndnote = m_bInFootnote = false;
     m_pFootnoteContext = nullptr;
-    m_bFirstParagraphInCell = m_bSaveFirstParagraphInCell;
 }
 
 void DomainMapper_Impl::PopAnnotation()
 {
     RemoveLastParagraph();
 
-    m_bIsInComments = false;
     m_aTextAppendStack.pop();
 
     try
@@ -4598,7 +4577,7 @@ void DomainMapper_Impl::ClearPreviousParagraph()
     m_xPreviousParagraph.clear();
 
     // next table paragraph will be first paragraph in a cell
-    m_bFirstParagraphInCell = true;
+    m_StreamStateStack.top().bFirstParagraphInCell = true;
 }
 
 void DomainMapper_Impl::HandleAltChunk(const OUString& rStreamName)
@@ -5548,7 +5527,7 @@ uno::Reference<beans::XPropertySet> 
DomainMapper_Impl::FindOrCreateFieldMaster(c
 
 void DomainMapper_Impl::PushFieldContext()
 {
-    m_bParaHadField = true;
+    m_StreamStateStack.top().bParaHadField = true;
     if(m_bDiscardHeaderFooter)
         return;
 #ifdef DBG_UTIL
@@ -6662,7 +6641,7 @@ void DomainMapper_Impl::handleToc
 
     m_bStartTOC = true;
     pContext->SetTOC(xTOC);
-    m_bParaHadField = false;
+    m_StreamStateStack.top().bParaHadField = false;
 
     if (!xTOC)
         return;
@@ -6895,7 +6874,7 @@ void DomainMapper_Impl::handleBibliography
         xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), 
uno::Any(OUString()));
 
     pContext->SetTOC( xTOC );
-    m_bParaHadField = false;
+    m_StreamStateStack.top().bParaHadField = false;
 
     uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
     appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() );
@@ -6938,7 +6917,7 @@ void DomainMapper_Impl::handleIndex
         }
     }
     pContext->SetTOC( xTOC );
-    m_bParaHadField = false;
+    m_StreamStateStack.top().bParaHadField = false;
 
     uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
     appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() );
@@ -7908,7 +7887,7 @@ void DomainMapper_Impl::CloseFieldCommand()
                 }
             }
             else
-                m_bParaHadField = false;
+                m_StreamStateStack.top().bParaHadField = false;
         }
     }
     catch( const uno::Exception& )
@@ -8470,8 +8449,11 @@ void DomainMapper_Impl::StartOrEndBookmark( const 
OUString& rId )
                     // keep bookmark range, if it doesn't exceed cell boundary
                     uno::Reference< text::XTextRange > xStart = 
xCursor->getStart();
                     xCursor->goLeft( 1, false );
-                    if (m_StreamStateStack.top().nTableDepth == 0 || 
!m_bFirstParagraphInCell)
+                    if (m_StreamStateStack.top().nTableDepth == 0
+                        || !m_StreamStateStack.top().bFirstParagraphInCell)
+                    {
                         xCursor->gotoRange(xStart, true );
+                    }
                 }
                 uno::Reference< container::XNamed > xBkmNamed( xBookmark, 
uno::UNO_QUERY_THROW );
                 SAL_WARN_IF(aBookmarkIter->second.m_sBookmarkName.isEmpty(), 
"writerfilter.dmapper", "anonymous bookmark");
@@ -9304,6 +9286,8 @@ void DomainMapper_Impl::substream(Id rName,
     appendTableHandler();
     getTableManager().startLevel();
 
+    m_StreamStateStack.emplace();
+
     //import of page header/footer
     //Ensure that only one header/footer per section is pushed
 
@@ -9334,8 +9318,12 @@ void DomainMapper_Impl::substream(Id rName,
     case NS_ooxml::LN_annotation :
         PushAnnotation();
     break;
+    default:
+        assert(false); // unexpected?
     }
 
+    assert(m_StreamStateStack.top().eSubstreamType != SubstreamType::Body);
+
     try
     {
         ref->resolve(m_rDMapper);
@@ -9365,6 +9353,9 @@ void DomainMapper_Impl::substream(Id rName,
     break;
     }
 
+    assert(!m_StreamStateStack.empty());
+    m_StreamStateStack.pop();
+
     getTableManager().endLevel();
     popTableManager();
     m_bHasFtn = bHasFtn;
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 7b6c504bc3a9..24477d96ac5d 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -146,6 +146,16 @@ enum StoredRedlines
     NONE
 };
 
+enum class SubstreamType
+{
+    Body,
+    Header,
+    Footer,
+    Footnote,
+    Endnote,
+    Annotation,
+};
+
 /**
  * Storage for state that is relevant outside a header/footer, but not inside 
it.
  *
@@ -157,6 +167,7 @@ enum StoredRedlines
  */
 struct SubstreamContext
 {
+    SubstreamType eSubstreamType = SubstreamType::Body;
     bool      bTextInserted = false;
     /**
      * This contains the raw table depth. nTableDepth > 0 is the same as
@@ -170,6 +181,10 @@ struct SubstreamContext
     bool      bIsColumnBreakDeferred = false;
     bool      bIsPageBreakDeferred = false;
     sal_Int32 nLineBreaksDeferred = 0;
+    /// Current paragraph had at least one field in it.
+    bool      bParaHadField = false;
+    /// Current paragraph in a table is first paragraph of a cell
+    bool      bFirstParagraphInCell = true;
 };
 
 /// Information about a paragraph to be finished after a field end.
@@ -544,15 +559,7 @@ private:
     bool                            m_bInStyleSheetImport; //in import of 
fonts, styles, lists or lfos
     bool                            m_bInNumberingImport; //in import of 
numbering (i.e. numbering.xml)
     bool                            m_bInAnyTableImport; //in import of fonts, 
styles, lists or lfos
-    enum class HeaderFooterImportState
-    {
-        none,
-        header,
-        footer,
-    }                               m_eInHeaderFooterImport;
     bool                            m_bDiscardHeaderFooter;
-    bool                            m_bInFootOrEndnote;
-    bool                            m_bInFootnote;
     PropertyMapPtr m_pFootnoteContext;
     bool m_bHasFootnoteStyle;
     bool m_bCheckFootnoteStyle;
@@ -601,7 +608,6 @@ private:
     bool                            m_bIsPreviousParagraphFramed;
     bool                            m_bIsLastParaInSection;
     bool                            m_bIsLastSectionGroup;
-    bool                            m_bIsInComments;
     /// If the current paragraph contains section property definitions.
     bool                            m_bParaSectpr;
     bool                            m_bUsingEnhancedFields;
@@ -748,7 +754,7 @@ public:
     /// Getter method for m_bSdt.
     bool GetSdt() const { return m_bSdt;}
     bool GetParaChanged() const { return m_bParaChanged;}
-    bool GetParaHadField() const { return m_bParaHadField; }
+    bool GetParaHadField() const { return 
m_StreamStateStack.top().bParaHadField; }
     bool GetRemoveThisPara() const { return m_bRemoveThisParagraph; }
 
     void deferBreak( BreakType deferredBreakType );
@@ -870,7 +876,7 @@ public:
     void PushPageFooter(SectionPropertyMap::PageType eType);
 
     void PopPageHeaderFooter();
-    bool IsInHeaderFooter() const { return m_eInHeaderFooterImport != 
HeaderFooterImportState::none; }
+    bool IsInHeaderFooter() const { auto const 
type(m_StreamStateStack.top().eSubstreamType); return type == 
SubstreamType::Header || type == SubstreamType::Footer; }
     void ConvertHeaderFooterToTextFrame(bool, bool);
     static void 
fillEmptyFrameProperties(std::vector<css::beans::PropertyValue>& 
rFrameProperties, bool bSetAnchorToChar);
 
@@ -878,8 +884,8 @@ public:
 
     void PushFootOrEndnote( bool bIsFootnote );
     void PopFootOrEndnote();
-    bool IsInFootOrEndnote() const { return m_bInFootOrEndnote; }
-    bool IsInFootnote() const { return IsInFootOrEndnote() && m_bInFootnote; }
+    bool IsInFootOrEndnote() const { auto const 
type(m_StreamStateStack.top().eSubstreamType); return type == 
SubstreamType::Footnote || type == SubstreamType::Endnote; }
+    bool IsInFootnote() const { return m_StreamStateStack.top().eSubstreamType 
== SubstreamType::Footnote; }
 
     void StartCustomFootnote(const PropertyMapPtr pContext);
     void EndCustomFootnote();
@@ -1025,7 +1031,7 @@ public:
     void SetInFootnoteProperties(bool bSet) { m_bIsInFootnoteProperties = 
bSet;}
     bool IsInFootnoteProperties() const { return m_bIsInFootnoteProperties;}
 
-    bool IsInComments() const { return m_bIsInComments; };
+    bool IsInComments() const { return m_StreamStateStack.top().eSubstreamType 
== SubstreamType::Annotation; };
 
     std::vector<css::beans::PropertyValue> MakeFrameProperties(const 
ParagraphProperties& rProps);
     void CheckUnregisteredFrameConversion(bool bPreventOverlap = false);
@@ -1208,15 +1214,9 @@ private:
     // Start a new index section; if needed, finish current paragraph
     css::uno::Reference<css::beans::XPropertySet> 
StartIndexSectionChecked(const OUString& sServiceName);
     std::vector<css::uno::Reference< css::drawing::XShape > > 
m_vTextFramesForChaining ;
-    /// Current paragraph had at least one field in it.
-    bool m_bParaHadField;
-    bool m_bSaveParaHadField;
     css::uno::Reference<css::beans::XPropertySet> m_xPreviousParagraph;
     /// Current paragraph has automatic before spacing.
     bool m_bParaAutoBefore;
-    /// Current paragraph in a table is first paragraph of a cell
-    bool m_bFirstParagraphInCell;
-    bool m_bSaveFirstParagraphInCell;
     /// Current paragraph had at least one inline object in it.
     bool m_bParaWithInlineObject;
     /// SAXException was seen so document will be abandoned

Reply via email to