sw: fix assert on tdf140219-2.odt

itrpaint.cxx:421: void SwTextPainter::DrawTextLine: Assertion `!roTaggedParagraph' failed.

The unexpected situation is that the SwTextFrame contains no numbering
portions but only fly portions, and the follow SwTextFrame contains the
numbering portion.

Delay producing the numbering and paragraph structured elements until
the numbering portions - it's the only way to create the list structure
correctly.

Unfortunately this means that the fly anchored on the paragraph won't be
nested inside the paragraph structured element any more, because it's
created later, but that seems rather difficult to do and the paint order
in sw already prevents this from working for hell layer flys.

(regression from commit 9b38beadf9eaf027b201cdf0ecb2bce5611014dd)

Change-Id: I9b40b85edd6f4b9920beac5602a5d3a6d4de5dd3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149058
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx
index f7daeef..4db59dc 100644
--- a/sw/source/core/text/EnhancedPDFExportHelper.cxx
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -976,10 +976,19 @@ void SwTaggedPDFHelper::BeginNumberedListStructureElements()
    const SwTextFrame& rTextFrame = static_cast<const SwTextFrame&>(rFrame);

    // Lowers of NonStructureElements should not be considered:

    if ( lcl_IsInNonStructEnv( rTextFrame ) || rTextFrame.IsFollow() )
    if (lcl_IsInNonStructEnv(rTextFrame))
        return;

    // do it for the first one in the follow chain that has content
    for (SwFlowFrame const* pPrecede = rTextFrame.GetPrecede(); pPrecede; pPrecede = pPrecede->GetPrecede())
    {
        SwTextFrame const*const pText(static_cast<SwTextFrame const*>(pPrecede));
        if (!pText->HasPara() || pText->GetPara()->HasContentPortions())
        {
            return;
        }
    }

    const SwTextNode *const pTextNd = rTextFrame.GetTextNodeForParaProps();
    const SwNumRule* pNumRule = pTextNd->GetNumRule();
    const SwNodeNum* pNodeNum = pTextNd->GetNum(rTextFrame.getRootFrame());
@@ -1099,7 +1108,7 @@ void SwTaggedPDFHelper::BeginNumberedListStructureElements()
    {
        BeginTag( vcl::PDFWriter::ListItem, aListItemString );
        assert(rTextFrame.GetPara());
        // check whether to open LIBody now or delay until after Lbl
        // check whether to open LBody now or delay until after Lbl
        if (!rTextFrame.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering))
        {
            BeginTag(vcl::PDFWriter::LIBody, aListBodyString);
@@ -1203,10 +1212,9 @@ void SwTaggedPDFHelper::BeginBlockStructureElements()
        case SwFrameType::Txt :
            {
                SwTextFrame const& rTextFrame(*static_cast<const SwTextFrame*>(pFrame));
                // lazy open LIBody after Lbl
                // lazy open LBody after Lbl
                if (rTextFrame.GetPara()->HasNumberingPortion(SwParaPortion::OnlyNumbering))
                {
                    assert(!rTextFrame.IsFollow());
                    BeginTag(vcl::PDFWriter::LIBody, aListBodyString);
                }

diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx
index 5f004b4..74ff8c9 100644
--- a/sw/source/core/text/frmpaint.cxx
+++ b/sw/source/core/text/frmpaint.cxx
@@ -673,8 +673,15 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
        }
    }

    Num_Info aNumInfo( *this );
    SwTaggedPDFHelper aTaggedPDFHelperNumbering( &aNumInfo, nullptr, nullptr, rRenderContext );
    // tdf140219-2.odt text frame with only fly portions and a follow is not
    // actually a paragraph - delay creating all structured elements to follow.
    bool const isPDFTaggingEnabled(!HasFollow() || GetPara()->HasContentPortions());
    ::std::optional<SwTaggedPDFHelper> oTaggedPDFHelperNumbering;
    if (isPDFTaggingEnabled)
    {
        Num_Info aNumInfo(*this);
        oTaggedPDFHelperNumbering.emplace(&aNumInfo, nullptr, nullptr, rRenderContext);
    }

    // Lbl unfortunately must be able to contain multiple numbering portions
    // that may be on multiple lines of text (but apparently always in the
@@ -683,7 +690,7 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
    // Paragraph tag - if there is a list label, opening should be delayed.
    ::std::optional<SwTaggedPDFHelper> oTaggedParagraph;

    if (IsFollow() || !GetPara()->HasNumberingPortion(SwParaPortion::FootnoteToo))
    if (isPDFTaggingEnabled && !GetPara()->HasNumberingPortion(SwParaPortion::FootnoteToo))
    {   // no Lbl needed => open paragraph tag now
        Frame_Info aFrameInfo(*this);
        oTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, rRenderContext);
@@ -773,7 +780,7 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
        {
            do
            {
                aLine.DrawTextLine(rRect, aClip, IsUndersized(), oTaggedLabel, oTaggedParagraph);
                aLine.DrawTextLine(rRect, aClip, IsUndersized(), oTaggedLabel, oTaggedParagraph, isPDFTaggingEnabled);

            } while( aLine.Next() && aLine.Y() <= nBottom );
        }
@@ -791,7 +798,7 @@ void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const&
    OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Paint" );

    assert(!oTaggedLabel); // must have been closed if opened
    assert(oTaggedParagraph || rRect.GetIntersection(getFrameArea()) != getFrameArea()); // must have been created during complete paint (PDF export is always complete paint)
    assert(!isPDFTaggingEnabled || oTaggedParagraph || rRect.GetIntersection(getFrameArea()) != getFrameArea()); // must have been created during complete paint (PDF export is always complete paint)
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
index 8f21298..c2a4556 100644
--- a/sw/source/core/text/itrpaint.cxx
+++ b/sw/source/core/text/itrpaint.cxx
@@ -121,7 +121,8 @@ SwLinePortion *SwTextPainter::CalcPaintOfst( const SwRect &rPaint )
void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
    const bool bUnderSized,
    ::std::optional<SwTaggedPDFHelper> & roTaggedLabel,
    ::std::optional<SwTaggedPDFHelper> & roTaggedParagraph)
    ::std::optional<SwTaggedPDFHelper> & roTaggedParagraph,
    bool const isPDFTaggingEnabled)
{
#if OSL_DEBUG_LEVEL > 1
//    sal_uInt16 nFntHeight = GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), GetInfo().GetOut() );
@@ -394,7 +395,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
            && !static_cast<SwNumberPortion const*>(pPor)->IsFollow())
        {
            assert(!roTaggedLabel);
            assert(!m_pFrame->IsFollow());
            assert(isPDFTaggingEnabled); (void) isPDFTaggingEnabled;
            Por_Info aPorInfo(*pPor, *this, true); // open Lbl
            roTaggedLabel.emplace(nullptr, nullptr, &aPorInfo, *pOut);
        }
@@ -410,7 +411,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
                pPor->Paint( GetInfo() );
        }

        // lazy open LIBody and paragraph tag after num portions have been painted to Lbl
        // lazy open LBody and paragraph tag after num portions have been painted to Lbl
        if (pPor->InNumberGrp() // also footnote label
            // note: numbering portion may be split if it has multiple scripts
            && !static_cast<SwNumberPortion const*>(pPor)->HasFollow()) // so wait for the last one
@@ -418,8 +419,7 @@ void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
            assert(roTaggedLabel);
            roTaggedLabel.reset(); // close Lbl
            assert(!roTaggedParagraph);
            assert(!m_pFrame->IsFollow());
            Frame_Info aFrameInfo(*m_pFrame); // open LIBody
            Frame_Info aFrameInfo(*m_pFrame); // open LBody
            roTaggedParagraph.emplace(nullptr, &aFrameInfo, nullptr, *pOut);
        }

diff --git a/sw/source/core/text/itrpaint.hxx b/sw/source/core/text/itrpaint.hxx
index a941e21..1c614e2 100644
--- a/sw/source/core/text/itrpaint.hxx
+++ b/sw/source/core/text/itrpaint.hxx
@@ -51,7 +51,8 @@ public:
    void DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
        const bool bUnderSz,
        ::std::optional<SwTaggedPDFHelper> & roTaggedLabel,
        ::std::optional<SwTaggedPDFHelper> & roTaggedParagraph);
        ::std::optional<SwTaggedPDFHelper> & roTaggedParagraph,
        bool isPDFTaggingEnabled);
    void PaintDropPortion();
    // if PaintMultiPortion is called recursively, we have to pass the
    // surrounding SwBidiPortion
diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx
index 7535580..271c76e 100644
--- a/sw/source/core/text/porlay.cxx
+++ b/sw/source/core/text/porlay.cxx
@@ -2699,6 +2699,20 @@ bool SwParaPortion::HasNumberingPortion(FootnoteOrNot const eFootnote) const
        && (eFootnote == SwParaPortion::FootnoteToo || !pPortion->IsFootnoteNumPortion());
}

bool SwParaPortion::HasContentPortions() const
{
    SwLinePortion const* pPortion(nullptr);
    for (SwLineLayout const* pLine = this; pLine && !pPortion; pLine = pLine->GetNext())
    {
        pPortion = pLine->GetFirstPortion();
        while (pPortion && (pPortion->InGlueGrp() || pPortion->IsFlyPortion()))
        {   // skip margins and fly spacers
            pPortion = pPortion->GetNextPortion();
        }
    }
    return pPortion != nullptr;
}

const SwDropPortion *SwParaPortion::FindDropPortion() const
{
    const SwLineLayout *pLay = this;
diff --git a/sw/source/core/text/porlay.hxx b/sw/source/core/text/porlay.hxx
index 5a824bd..3b07b70 100644
--- a/sw/source/core/text/porlay.hxx
+++ b/sw/source/core/text/porlay.hxx
@@ -321,6 +321,7 @@ public:
    bool IsMargin() const { return m_bMargin; }
    enum FootnoteOrNot { OnlyNumbering, FootnoteToo };
    bool HasNumberingPortion(FootnoteOrNot) const;
    bool HasContentPortions() const;

    // Set nErgo in the QuoVadisPortion
    void SetErgoSumNum( const OUString &rErgo );