sw/qa/extras/layout/data/tdf167540.docx |binary sw/qa/extras/layout/layout5.cxx | 86 ++++++++++++++++++++++++++++++++ sw/source/core/text/frmpaint.cxx | 9 ++- 3 files changed, 93 insertions(+), 2 deletions(-)
New commits: commit d680c2be8bafcac13fdd05c99c0cdcc06b55ce3d Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Thu Jul 17 20:44:23 2025 +0500 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Jul 18 09:27:20 2025 +0200 tdf#167538, tdf#167540: better check if the line is dummy The check in SwTextFrame::PaintExtraData is made the same as in SwTextFormatter::CalcRealHeight (except for bNewLine, which was specific to the latter). It allows to recognize real dummy lines uniformly, and produce line numbering for trailing empty lines. Change-Id: If2bad4b998b0c48f8f79da52a081e6df7565eee3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188016 Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Tested-by: Jenkins (cherry picked from commit 5355b3ec3925ddd146a6e3dd6ba28a816b39c66c) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188022 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/extras/layout/data/tdf167540.docx b/sw/qa/extras/layout/data/tdf167540.docx new file mode 100644 index 000000000000..80aba5d98cf8 Binary files /dev/null and b/sw/qa/extras/layout/data/tdf167540.docx differ diff --git a/sw/qa/extras/layout/layout5.cxx b/sw/qa/extras/layout/layout5.cxx index f6e69743866e..21c022b10b16 100644 --- a/sw/qa/extras/layout/layout5.cxx +++ b/sw/qa/extras/layout/layout5.cxx @@ -1848,6 +1848,92 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf167526) } } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf167540) +{ + // Given a document with a paragraph with a hard line break followed by nothing (tdf#167538); + // and two floating tables with an empty paragraph between them (tdf#167540). The strings in + // paragraphs all have different lengths, to allow XPath matching: + createSwDoc("tdf167540.docx"); + + // check line numbering + auto verify_me = [this]() { + // Dump the rendering of the first page as an XML file. + SwDocShell* pShell = getSwDocShell(); + std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile(); + MetafileXmlDump dumper; + xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile); + + // line 1 + + assertXPathContent(pXmlDoc, "//textarray[@length=1][1]/text", u"1"); + auto ln1_y = getXPath(pXmlDoc, "//textarray[@length=1][1]", "y"); + + assertXPathContent(pXmlDoc, "//textarray[@length=4]/text", u"Text "); + auto text1_y = getXPath(pXmlDoc, "//textarray[@length=4]", "y"); + + // line numbering Y coordinate is the same as its line + CPPUNIT_ASSERT_EQUAL(text1_y, ln1_y); + + // line 2 (empty) + + assertXPathContent(pXmlDoc, "//textarray[@length=1][2]/text", u"2"); + auto ln2_y = getXPath(pXmlDoc, "//textarray[@length=1][2]", "y"); + + // line numbering for line 2 is indeed lower than for line 1 + CPPUNIT_ASSERT_GREATER(ln1_y.toInt32(), ln2_y.toInt32()); + + // first floating table + + assertXPathContent(pXmlDoc, "//textarray[@length=20]/text", u"First floating table"); + auto table1_y = getXPath(pXmlDoc, "//textarray[@length=20]", "y"); + + // the first floating table is indeed lower than line numbering for line 2 + CPPUNIT_ASSERT_GREATER(ln2_y.toInt32(), table1_y.toInt32()); + + // line 3 (empty) + + assertXPathContent(pXmlDoc, "//textarray[@length=1][3]/text", u"3"); + auto ln3_y = getXPath(pXmlDoc, "//textarray[@length=1][3]", "y"); + + // line numbering for line 3 is indeed lower than the first floating table + CPPUNIT_ASSERT_GREATER(table1_y.toInt32(), ln3_y.toInt32()); + + // second floating table + + assertXPathContent(pXmlDoc, "//textarray[@length=21]/text", u"Second floating table"); + auto table2_y = getXPath(pXmlDoc, "//textarray[@length=21]", "y"); + + // the second floating table is indeed lower than line numbering for line 3 + CPPUNIT_ASSERT_GREATER(ln3_y.toInt32(), table2_y.toInt32()); + + // Inline table + + assertXPathContent(pXmlDoc, "//textarray[@length=14]/text", u"A normal table"); + auto table3_y = getXPath(pXmlDoc, "//textarray[@length=14]", "y"); + + // the inline table is indeed lower than second floating table + CPPUNIT_ASSERT_GREATER(table2_y.toInt32(), table3_y.toInt32()); + + // line 4 + + assertXPathContent(pXmlDoc, "//textarray[@length=1][4]/text", u"4"); + auto ln4_y = getXPath(pXmlDoc, "//textarray[@length=1][4]", "y"); + + assertXPathContent(pXmlDoc, "//textarray[@length=9]/text", u"More text"); + auto text4_y = getXPath(pXmlDoc, "//textarray[@length=9]", "y"); + + // line numbering Y coordinate is the same as its line + CPPUNIT_ASSERT_EQUAL(text4_y, ln4_y); + }; + + verify_me(); + + // DOCX roundtrip: + saveAndReload(u"Office Open XML Text"_ustr); + + verify_me(); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx index 6d116cdbc156..f768d212a62f 100644 --- a/sw/source/core/text/frmpaint.cxx +++ b/sw/source/core/text/frmpaint.cxx @@ -361,7 +361,6 @@ void SwTextFrame::PaintExtraData( const SwRect &rRect ) const aLayoutModeModifier.Modify( false ); SwTextPainter aLine( const_cast<SwTextFrame*>(this), &aInf ); - bool bNoDummy = !aLine.GetNext(); // Only one empty line! while( aLine.Y() + aLine.GetLineHeight() <= rRect.Top() ) { @@ -381,7 +380,13 @@ void SwTextFrame::PaintExtraData( const SwRect &rRect ) const const bool bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin(); do { - if( bNoDummy || !aLine.GetCurr()->IsDummy() ) + // A comment from SwTextFormatter::CalcRealHeight: + // The dummy flag is set on lines that only contain flyportions. Unfortunately an empty + // line can be at the end of a paragraph (empty paragraphs or behind a Shift-Return). + if (!aLine.GetCurr()->IsDummy() + || (!aLine.GetCurr()->GetNext() + && aLine.GetStart() + >= TextFrameIndex(aLine.GetTextFrame()->GetText().getLength()))) { bool bRed = bRedLine && aLine.GetCurr()->HasRedline(); if( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() )