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 );