sw/qa/extras/layout/data/section-break-hidden-paragraphs.rtf | 68 +++++++++++ sw/qa/extras/layout/layout2.cxx | 44 +++++++ sw/source/core/text/porlay.cxx | 15 ++ sw/source/core/text/txtfrm.cxx | 13 +- 4 files changed, 134 insertions(+), 6 deletions(-)
New commits: commit 7a9a1e8ef5ad9dd3e9b7d286a8f85d2f18088d29 Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Fri Mar 7 19:05:15 2025 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Tue Mar 11 12:48:35 2025 +0100 sw: layout: hide text frame before table like Word There are several empty paragraphs before a table, all have the paragraph end marker hidden, and the first one is preceded by a section break, which is represented as a break item on the first text node in Writer. Word hides the paragraphs, so that the following table starts at the top of the page. Writer merges the paragraphs into one text frame, but it is not considered hidden because it doesn't contain any hidden characters; if you insert a character into a paragraph, the merged text frame will disappear! The first idea was to adapt SwTextFrame::IsHiddenNowImpl() to check if there isn't any text in the text frame and the paragraph end marker is hidden. But it turns out that the problem is more general: it's a problem of the sw_redlinehide merging, because an un-merged paragraph is hidden via a check of RES_CHRATR_HIDDEN in SwScriptInfo::selectHiddenTextProperty(). It's a bit nonobvious why this didn't work, and how it would interact with delete redlines, but 2 problems are that the case of the empty text frame needs a special case handling to get to this check in selectHiddenTextProperty(), and then GetBoundsOfHiddenRange() doesn't assign its outparameters in the special case of the empty paragraph; it looks like the values in m_HiddenChg should be 0, 0 then. Change-Id: I2ba85e98c02fb69159ee7ba77a7e4694fdf6fd52 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182642 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/sw/qa/extras/layout/data/section-break-hidden-paragraphs.rtf b/sw/qa/extras/layout/data/section-break-hidden-paragraphs.rtf new file mode 100644 index 000000000000..f8526dafe5ea --- /dev/null +++ b/sw/qa/extras/layout/data/section-break-hidden-paragraphs.rtf @@ -0,0 +1,68 @@ +{ tf1deflang1025nsinsicpg1252\uc1deff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang2057\deflangfe1028 hemelang2057 hemelangfe1028 hemelangcs1025 +{onttbl{1bidi swisscharset0prq2{\*\panose 020b0604020202020204}Arial;} +{14bidi nilcharset136prq2{\*\panose 02010601000101010101}PMingLiU{\*alt Arial Unicode MS};}{34bidi romancharset0prq2{\*\panose 02040503050406030204}Cambria Math;} +{37bidi swisscharset0prq2{\*\panose 020f0502020204030204}Calibri;}{294 bidi nilcharset136prq2{\*\panose 00000000000000000000}@PMingLiU;} +{lomajor31500bidi romancharset0prq2{\*\panose 02020603050405020304}Times New Roman{\*alt Arial};}{dbmajor31501bidi nilcharset136prq2{\*\panose 02010601000101010101}PMingLiU{\*alt Arial Unicode MS};} +{himajor31502bidi swisscharset0prq2{\*\panose 020f0302020204030204}Calibri Light;}{bimajor31503bidi romancharset0 prq2{\*\panose 02020603050405020304}Times New Roman{\*alt Arial};} +{lominor31504bidi romancharset0prq2{\*\panose 02020603050405020304}Times New Roman{\*alt Arial};}{dbminor31505bidi nilcharset136prq2{\*\panose 02010601000101010101}PMingLiU{\*alt Arial Unicode MS};} +{himinor31506bidi swisscharset0prq2{\*\panose 020f0502020204030204}Calibri;}{biminor31507bidi swisscharset0 prq2{\*\panose 020b0604020202020204}Arial;}{305bidi swisscharset238prq2 Arial CE;} +{306bidi swisscharset204prq2 Arial Cyr;}{308bidi swisscharset161prq2 Arial Greek;}{309bidi swisscharset162prq2 Arial Tur;}{310bidi swiss charset177prq2 Arial (Hebrew);} +{311bidi swisscharset178prq2 Arial (Arabic);}{312bidi swisscharset186 prq2 Arial Baltic;}{313bidi swisscharset163prq2 Arial (Vietnamese);}{635 bidi romancharset238prq2 Cambria Math CE;} +{636bidi romancharset204prq2 Cambria Math Cyr;}{638bidi roman charset161prq2 Cambria Math Greek;}{639bidi romancharset162prq2 Cambria Math Tur;}{642bidi romancharset186prq2 Cambria Math Baltic;} +{643bidi romancharset163prq2 Cambria Math (Vietnamese);}{665bidi swiss charset238prq2 Calibri CE;}{666bidi swisscharset204prq2 Calibri Cyr;}{ 668bidi swisscharset161prq2 Calibri Greek;} +{669bidi swisscharset162prq2 Calibri Tur;}{670bidi swisscharset177 prq2 Calibri (Hebrew);}{671bidi swisscharset178prq2 Calibri (Arabic);}{ 672bidi swisscharset186prq2 Calibri Baltic;} +{673bidi swisscharset163prq2 Calibri (Vietnamese);}{lomajor31508bidi romancharset238prq2 Times New Roman CE{\*alt Arial};}{lomajor31509bidi romancharset204prq2 Times New Roman Cyr{\*alt Arial};} +{lomajor31511bidi romancharset161prq2 Times New Roman Greek{\*alt Arial};}{lomajor31512bidi romancharset162prq2 Times New Roman Tur{\*alt Arial};} +{lomajor31513bidi romancharset177prq2 Times New Roman (Hebrew){\*alt Arial};}{lomajor31514bidi romancharset178prq2 Times New Roman (Arabic){\* alt Arial};} +{lomajor31515bidi romancharset186prq2 Times New Roman Baltic{\*alt Arial};}{lomajor31516bidi romancharset163prq2 Times New Roman (Vietnamese){\*alt Arial};}{himajor31528bidi swisscharset238prq2 Calibri Light CE;} +{himajor31529bidi swisscharset204prq2 Calibri Light Cyr;}{himajor31531 bidi swisscharset161prq2 Calibri Light Greek;}{himajor31532bidi swiss charset162prq2 Calibri Light Tur;} +{himajor31533bidi swisscharset177prq2 Calibri Light (Hebrew);}{himajor 31534bidi swisscharset178prq2 Calibri Light (Arabic);}{himajor31535bidi swisscharset186prq2 Calibri Light Baltic;} +{himajor31536bidi swisscharset163prq2 Calibri Light (Vietnamese);}{ bimajor31538bidi romancharset238prq2 Times New Roman CE{\*alt Arial};}{ bimajor31539bidi romancharset204prq2 Times New Roman Cyr{\*alt Arial};} +{bimajor31541bidi romancharset161prq2 Times New Roman Greek{\*alt Arial};}{bimajor31542bidi romancharset162prq2 Times New Roman Tur{\*alt Arial};} +{bimajor31543bidi romancharset177prq2 Times New Roman (Hebrew){\*alt Arial};}{bimajor31544bidi romancharset178prq2 Times New Roman (Arabic){\* alt Arial};} +{bimajor31545bidi romancharset186prq2 Times New Roman Baltic{\*alt Arial};}{bimajor31546bidi romancharset163prq2 Times New Roman (Vietnamese){\*alt Arial};} +{lominor31548bidi romancharset238prq2 Times New Roman CE{\*alt Arial};}{lominor31549bidi romancharset204prq2 Times New Roman Cyr{\*alt Arial};} +{lominor31551bidi romancharset161prq2 Times New Roman Greek{\*alt Arial};}{lominor31552bidi romancharset162prq2 Times New Roman Tur{\*alt Arial};} +{lominor31553bidi romancharset177prq2 Times New Roman (Hebrew){\*alt Arial};}{lominor31554bidi romancharset178prq2 Times New Roman (Arabic){\* alt Arial};} +{lominor31555bidi romancharset186prq2 Times New Roman Baltic{\*alt Arial};}{lominor31556bidi romancharset163prq2 Times New Roman (Vietnamese){\*alt Arial};}{himinor31568bidi swisscharset238prq2 Calibri CE;} +{himinor31569bidi swisscharset204prq2 Calibri Cyr;}{himinor31571bidi swisscharset161prq2 Calibri Greek;}{himinor31572bidi swisscharset162 prq2 Calibri Tur;} +{himinor31573bidi swisscharset177prq2 Calibri (Hebrew);}{himinor31574 bidi swisscharset178prq2 Calibri (Arabic);}{himinor31575bidi swiss charset186prq2 Calibri Baltic;} +{himinor31576bidi swisscharset163prq2 Calibri (Vietnamese);}{biminor 31578bidi swisscharset238prq2 Arial CE;}{biminor31579bidi swiss charset204prq2 Arial Cyr;} +{biminor31581bidi swisscharset161prq2 Arial Greek;}{biminor31582bidi swisscharset162prq2 Arial Tur;}{biminor31583bidi swisscharset177prq2 Arial (Hebrew);} +{biminor31584bidi swisscharset178prq2 Arial (Arabic);}{biminor31585 bidi swisscharset186prq2 Arial Baltic;}{biminor31586bidi swiss charset163prq2 Arial (Vietnamese);} +{295bidi romancharset238prq2 Times New Roman CE{\*alt Arial};}{296bidi romancharset204prq2 Times New Roman Cyr{\*alt Arial};}{298bidi roman charset161prq2 Times New Roman Greek{\*alt Arial};} +{299bidi romancharset162prq2 Times New Roman Tur{\*alt Arial};}{300 bidi romancharset177prq2 Times New Roman (Hebrew){\*alt Arial};}{301bidi romancharset178prq2 Times New Roman (Arabic){\*alt Arial};} +{302bidi romancharset186prq2 Times New Roman Baltic{\*alt Arial};}{303 bidi romancharset163prq2 Times New Roman (Vietnamese){\*alt Arial};}} +{\*\defchp s22\lochf31506\hichf31506\dbchf31505 }{\*\defpap \ql \li0 i0\sa160\sl259\slmult1\widctlpar\wrapdefaultspalphaspnumaautodjustright in0\lin0\itap0 } oqfpromote +{\stylesheet{ +\ql \li0 i0\sa160\sl259\slmult1\widctlpar\wrapdefaultspalphaspnum aautodjustright in0\lin0\itap0 tlchcs1 f31507fs22lang1025 \ltrchcs0 s22\lang2057\langfe1028\loch31506\hichf31506\dbchf31505+\snext0 \sqformat \spriority0 Normal;}{\*+ s11 srowd rftsWidthB3 rpaddl108 rpaddr108 rpaddfl3 rpaddft3 rpaddfb3 rpaddfr3 blind0 blindtype3 svertalt sbrdrt sbrdrl sbrdrb sbrdrr sbrdrdgl sbrdrdgr sbrdrh sbrdrv \ql \li0 i0\sa160\sl259\slmult1 +\widctlpar\wrapdefaultspalphaspnumaautodjustright in0\lin0\itap0 tlchcs1 f31507fs22lang1025 \ltrchcs0 s22\lang2057\langfe1028\loch 31506\hichf31506\dbchf31505+Normal Table;}{\* s15 srowd rbrdrtrdrsrdrw10 rbrdrlrdrsrdrw10 rbrdrbrdrsrdrw10 rbrdrrrdrsrdrw10 rbrdrhrdrsrdrw10 rbrdrvrdrsrdrw10 + rftsWidthB3 rpaddl108 rpaddr108 rpaddfl3 rpaddft3 rpaddfb3 rpaddfr3 blind0 blindtype3 svertalt sbrdrt sbrdrl sbrdrb sbrdrr sbrdrdgl sbrdrdgr sbrdrh sbrdrv +\ql \li0 i0\widctlpar\wrapdefaultspalphaspnumaautodjustright in0\lin0\itap0 tlchcs1 f31507fs22lang1025 \ltrchcs0 s22\lang2057\langfe1028\loch31506\hichf31506\dbchf31505+\sbasedon11 \snext15 \spriority39 Table Grid;}} + +\paperw11906\paperh16838\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrltnbjenddoc rackmoves0 rackformatting1\donotembedsysfont1 elyonvml0\donotembedlingdata0\grfdocevents0 alidatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1 oxlattoyen +xpshrtn oultrlspc\dntblnsbdb ospaceforul ormshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1440\dgvorigin1440\dghshow1\dgvshow1 +\jexpandiewkind1iewscale100\pgbrdrhead\pgbrdrfoot\splytwnine tnlytwnine\htmautsp olnhtadjtbl\useltbalnlntblind\lytcalctblwd\lyttblrtgr\lnbrkrule obrkwrptbl\snaptogridincellllowfieldendsel\wrppunct +sianbrkrule ewtblstyruls ogrowautofit\usenormstyforlist oindnmbrtselnbrelev ocxsptable\indrlsweleven oafcnsttblfelev\utinl\hwelev\spltpgpar otcvasp otbrkcnstfrctbl otvatxbx\krnprsnet+{\*\wgrffmtfilter 2450} ofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708 ootery708+\pard\plain \ltrpar\ql \li0 i0\sa160\sl259\slmult1\widctlpar\wrapdefaultspalphaspnumaautodjustright in0\lin0\itap0 tlchcs1 f31507fs22lang1025 \ltrchcs0 + s22\lang2057\langfe1028\lochf31506\hichf31506\dbchf31505+\linex0\headery708 ootery708+\ltrchcs0 s22\lang2057\langfe1028\lochf31506\hichf31506\dbchf31505+\par \par }{ tlch cs1 f31507 \ltrchcs0 \lang1031\langfe1028\langnp1031 +\par \ltrrow} rowd \irow0\irowband0\lastrow \ltrrow s15 rgaph108 rleft5 rbrdrtrdrsrdrw10 rbrdrlrdrsrdrw10 rbrdrbrdrsrdrw10 rbrdrrrdrsrdrw10 rbrdrhrdrsrdrw10 rbrdrvrdrsrdrw10 + rftsWidth1 rftsWidthB3 rautofit1 rpaddl108 rpaddr108 rpaddfl3 rpaddft3 rpaddfb3 rpaddfr3 bllkhdrrows bllkhdrcols bllknocolband blind0 blindtype3 ++ s22\lang2057\langfe1028\lochf31506\hichf31506\dbchf31505+\ql \li0 i0\sa160\sl259\slmult1\widctlpar\intbl\wrapdefaultspalphaspnum aautodjustright in0\lin0 tlchcs1 f31507fs22lang1025 \ltrchcs0 s22\lang2057\langfe1028\lochf31506\hichf31506\dbchf31505+ tlchcs1 f31507 \ltrchcs0 \lang1031\langfe1028\langnp1031 rowd \irow0\irowband0\lastrow \ltrrow s15 rgaph108 rleft5 rbrdrtrdrsrdrw10 rbrdrlrdrsrdrw10 rbrdrbrdrsrdrw10 rbrdrrrdrsrdrw10 rbrdrh +rdrsrdrw10 rbrdrvrdrsrdrw10 rftsWidth1 rftsWidthB3 rautofit1 rpaddl108 rpaddr108 rpaddfl3 rpaddft3 rpaddfb3 rpaddfr3 bllkhdrrows bllkhdrcols bllknocolband blind0 blindtype3 +rdrsrdrw10 + tlchcs1 f31507 \ltrchcs0 \lang1031\langfe1028\langnp1031\hichf31506\dbchf31505\loch31506 End}{ tlch cs1 f31507 \ltrchcs0 \lang1031\langfe1028\langnp1031 +\par } +} diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx index a9cd34f17432..9b0401f42aa9 100644 --- a/sw/qa/extras/layout/layout2.cxx +++ b/sw/qa/extras/layout/layout2.cxx @@ -18,6 +18,7 @@ #include <editeng/unolingu.hxx> #include <i18nlangtag/languagetag.hxx> #include <o3tl/string_view.hxx> +#include <vcl/scheduler.hxx> #include <rootfrm.hxx> #include <pagefrm.hxx> @@ -758,6 +759,49 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf152872) assertXPath(pXmlDoc, "/root/page/body/txt[2]/infos/bounds", "height", u"0"); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testHiddenParaBreaks) +{ + createSwDoc("section-break-hidden-paragraphs.rtf"); + + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + SwViewOption aViewOptions(*pWrtShell->GetViewOptions()); + aViewOptions.SetShowHiddenChar(true); + aViewOptions.SetViewMetaChars(true); + pWrtShell->ApplyViewOptions(aViewOptions); + Scheduler::ProcessEventsToIdle(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt", 1); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout", "portion", + u"First"); + // actually Word shows an additional paragraph before the table + assertXPath(pXmlDoc, "/root/page[2]/body/txt", 3); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/SwParaPortion/SwLineLayout", "portion", u""); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/infos/bounds", "top", u"18846"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/infos/bounds", "height", u"269"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[2]/infos/bounds", "top", u"19115"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[2]/infos/bounds", "height", u"450"); + assertXPath(pXmlDoc, "/root/page[2]/body/tab[1]/infos/bounds", "top", u"19565"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[3]/SwParaPortion/SwLineLayout", "portion", u"End"); + + aViewOptions.SetViewMetaChars(false); + pWrtShell->ApplyViewOptions(aViewOptions); + Scheduler::ProcessEventsToIdle(); + + pXmlDoc = parseLayoutDump(); + + assertXPath(pXmlDoc, "/root/page[1]/body/txt", 1); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[1]/SwParaPortion/SwLineLayout", "portion", + u"First"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt", 2); + // this one is merged; if it were 2 0-height frames that would work too + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/infos/bounds", "top", u"18846"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/infos/bounds", "height", u"0"); + assertXPath(pXmlDoc, "/root/page[2]/body/tab[1]/infos/bounds", "top", u"18846"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[2]/SwParaPortion/SwLineLayout", "portion", u"End"); +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testHiddenParaProps) { createSwDoc("merge_hidden_redline.docx"); diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx index 362167d6e89b..405ed7f526cc 100644 --- a/sw/source/core/text/porlay.cxx +++ b/sw/source/core/text/porlay.cxx @@ -1149,8 +1149,19 @@ void SwScriptInfo::InitScriptInfoHidden(const SwTextNode& rNode, SwTextNode const* pNode(nullptr); TextFrameIndex nOffset(0); std::optional<std::vector<sw::Extent>::const_iterator> oPrevIter; - for (auto iter = pMerged->extents.begin(); iter != pMerged->extents.end(); - oPrevIter = iter) + if (pMerged->extents.empty()) + { + Range aRange(0, pMerged->pLastNode->Len() > 0 ? pMerged->pLastNode->Len() - 1 : 0); + MultiSelection aHiddenMulti(aRange); + CalcHiddenRanges(*pMerged->pLastNode, aHiddenMulti, nullptr); + if (aHiddenMulti.GetRangeCount() != 0) + { + m_HiddenChg.push_back(TextFrameIndex(0)); + m_HiddenChg.push_back(TextFrameIndex(0)); + } + } + else for (auto iter = pMerged->extents.begin(); + iter != pMerged->extents.end(); oPrevIter = iter) { if (iter->pNode == pNode) { diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx index 9ed922e99656..7eb35cb29e81 100644 --- a/sw/source/core/text/txtfrm.cxx +++ b/sw/source/core/text/txtfrm.cxx @@ -1521,20 +1521,25 @@ bool SwTextFrame::IsHiddenNowImpl() const { TextFrameIndex nHiddenStart(COMPLETE_STRING); TextFrameIndex nHiddenEnd(0); + bool hasHidden{false}; if (auto const pScriptInfo = GetScriptInfo()) { - pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0), + hasHidden = pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0), nHiddenStart, nHiddenEnd); } else // ParaPortion is created in Format, but this is called earlier { SwScriptInfo aInfo; aInfo.InitScriptInfoHidden(*m_pMergedPara->pFirstNode, m_pMergedPara.get()); - aInfo.GetBoundsOfHiddenRange(TextFrameIndex(0), + hasHidden = aInfo.GetBoundsOfHiddenRange(TextFrameIndex(0), nHiddenStart, nHiddenEnd); } - if (TextFrameIndex(0) == nHiddenStart && - TextFrameIndex(GetText().getLength()) <= nHiddenEnd) + if ((TextFrameIndex(0) == nHiddenStart + && TextFrameIndex(GetText().getLength()) <= nHiddenEnd) + // special case: GetBoundsOfHiddenRange doesn't assign! + // but it does return that there *is* something hidden, in case + // the frame is empty then the whole thing must be hidden + || (hasHidden && m_pMergedPara->mergedText.isEmpty())) { bHiddenCharsHidePara = true; }