oox/source/export/drawingml.cxx | 14 +-- sw/qa/extras/layout/data/hidden-para-before-table.rtf | 5 + sw/qa/extras/layout/layout3.cxx | 14 +++ sw/source/core/text/redlnitr.cxx | 74 ++++++++++++------ 4 files changed, 76 insertions(+), 31 deletions(-)
New commits: commit 56c029f7d880cbc605349510ea6898fdc33b24be Author: Mike Kaganski <[email protected]> AuthorDate: Thu Oct 9 18:11:17 2025 +0500 Commit: Thorsten Behrens <[email protected]> CommitDate: Tue Oct 14 23:46:40 2025 +0200 tdf#168116: an ugly hack to handle linebreak-before-hidden-paragraph-mark Word eliminates a completely empty line ending with a hidden paragraph mark, which may appear when a paragraph has a line break followed by such a break, even when the following is not another paragraph, but a table. This change detects this event, and removes the linebreak instead. Known problems: 1. If there is a completely hidden paragraph between the paragraph with trailing line break and hidden mark, this doesn't work. E.g., this RTF markup: AAAA\line {\par} {\par} \intbl BBBB Still, this change allows to fix the bugdoc layout. 2. When formatting marks are shown, the paragraph ends with pilcrow, not a linebreak symbol. This is minor. Change-Id: I66b06cdac9ab41bcccffab669f7caffe0a23b686 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192104 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192179 Tested-by: allotropia jenkins <[email protected]> Reviewed-by: Thorsten Behrens <[email protected]> diff --git a/sw/qa/extras/layout/data/hidden-para-before-table.rtf b/sw/qa/extras/layout/data/hidden-para-before-table.rtf new file mode 100644 index 000000000000..1456504bec84 --- /dev/null +++ b/sw/qa/extras/layout/data/hidden-para-before-table.rtf @@ -0,0 +1,5 @@ +{ tf1 +AAAA\line +{\par} +\intbl BBBB+} \ No newline at end of file diff --git a/sw/qa/extras/layout/layout3.cxx b/sw/qa/extras/layout/layout3.cxx index b0b80dcd41e7..c300b9a69787 100644 --- a/sw/qa/extras/layout/layout3.cxx +++ b/sw/qa/extras/layout/layout3.cxx @@ -4059,6 +4059,20 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, TestTdf152839_firstRows) CPPUNIT_ASSERT_LESSEQUAL(sal_Int32(230), nHeight); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter3, testTdf168116) +{ + // Given a paragraph with a line break immediately followed by a hidden paragraph mark: + createSwDoc("hidden-para-before-table.rtf"); + auto pXmlDoc = parseLayoutDump(); + + assertXPath(pXmlDoc, "//body"); + assertXPathChildren(pXmlDoc, "//body", 4); + // It must have a "merged" data; without the fix, this failed: + assertXPath(pXmlDoc, "//body/txt[1]/merged"); + // It must only have one line; without the fix, there were two (the second empty): + assertXPath(pXmlDoc, "//body/txt[1]/SwParaPortion/SwLineLayout", 1); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx index 82def89f5f6a..df35d2d6836c 100644 --- a/sw/source/core/text/redlnitr.cxx +++ b/sw/source/core/text/redlnitr.cxx @@ -110,6 +110,17 @@ public: // Note: caller is responsible for checking for immediately adjacent hides bool Next() { + // A special case (tdf#168116 hack): the node should hide its paragraph break, but it was + // impossible; yet, the last paragraph character ( ) was hidden instead. + if (m_oParagraphBreak + && m_oParagraphBreak->first.GetNodeIndex() == m_oParagraphBreak->second.GetNodeIndex()) + { + // This is a call to Next after we returned the fake position for the last hidden + // paragraph mark. We are done. + m_pStartPos = nullptr; + m_pEndPos = nullptr; + return false; + } SwPosition const* pNextRedlineHide(nullptr); assert(m_pEndPos); if (m_isHideRedlines) @@ -250,30 +261,49 @@ public: } return false; }; - if (m_isHideParagraphBreaks - && m_pEndPos->GetNode().IsTextNode() // ooo27109-1.sxw - // only merge if next node is also text node - && m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->IsTextNode() - && hasHiddenItem(*m_pEndPos->GetNode().GetTextNode()) - // no merge if there's a page break on any node - && !hasBreakBefore(*m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->GetTextNode()) - // first node, see SwTextFrame::GetBreak() - && !hasBreakAfter(*m_Start.GetNode().GetTextNode())) - { - m_oParagraphBreak.emplace( - SwPosition(*m_pEndPos->GetNode().GetTextNode(), m_pEndPos->GetNode().GetTextNode()->Len()), - SwPosition(*m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex()+1]->GetTextNode(), 0)); - m_pStartPos = &m_oParagraphBreak->first; - m_pEndPos = &m_oParagraphBreak->second; - return true; - } - else // nothing + + if (auto pTextNode = m_pEndPos->GetNode().GetTextNode(); + pTextNode // ooo27109-1.sxw + && m_isHideParagraphBreaks + && hasHiddenItem(*pTextNode)) { - m_pStartPos = nullptr; - m_pEndPos = nullptr; - m_oParagraphBreak.reset(); - return false; + auto pNextNode = m_pEndPos->GetNodes()[m_pEndPos->GetNodeIndex() + 1]; + if (pNextNode->IsTextNode() // only merge if next node is also text node + // no merge if there's a page break on any node + && !hasBreakBefore(*pNextNode->GetTextNode()) + // first node, see SwTextFrame::GetBreak() + && !hasBreakAfter(*m_Start.GetNode().GetTextNode())) + { + m_oParagraphBreak.emplace(SwPosition(*pTextNode, pTextNode->Len()), + SwPosition(*pNextNode->GetTextNode(), 0)); + m_pStartPos = &m_oParagraphBreak->first; + m_pEndPos = &m_oParagraphBreak->second; + return true; + } + // A special case (tdf#168116 hack): a line break immediately before hidden marker, + // which itself cannot be hidden. Hide the preceding line break instead. + if (pTextNode->Len() > 0 + && pTextNode->GetText()[pTextNode->Len() - 1] == ' ') + { + SwPosition nodeEnd(*pTextNode, pTextNode->Len()); + // Only if we didn't include the line break in the previous result + if (*m_pEndPos < nodeEnd) + { + // This fake m_oParagraphBreak can easily be distinguished from the proper + // one created a few lined above: both positions are inside the same node. + m_oParagraphBreak.emplace(SwPosition(*pTextNode, pTextNode->Len() - 1), + nodeEnd); + m_pStartPos = &m_oParagraphBreak->first; + m_pEndPos = &m_oParagraphBreak->second; + return true; + } + } } + + m_pStartPos = nullptr; + m_pEndPos = nullptr; + m_oParagraphBreak.reset(); + return false; } } }; commit 345c06dffd0eb96e282c7f0ce5c915d111a76c5a Author: Mike Kaganski <[email protected]> AuthorDate: Thu Oct 9 12:25:09 2025 +0500 Commit: Thorsten Behrens <[email protected]> CommitDate: Tue Oct 14 23:46:27 2025 +0200 Related: tdf#80224 A blind attempt to fix conditions ... introduced in commit 43679f94b45f4d9e120c64a3fb5cc3ee77f12b11 (Fix tdf#80224 Custom text color changed to black on .PPTX export, 2015-08-25). Those conditions, presumably, intended to use GetProperty in the non-bCheckDirect case. But the implementation effectively made the bCheckDirect case also call GetProperty, when state check fails, which, in the end, has the same effect, as calling GetProperty in all cases. I attempted to fix it like this: bCheckDirect ? GetDirectProperty(rXPropSet, rXPropState, u"Foo"_ustr) : GetProperty(rXPropSet, u"Foo"_ustr) but it failed CppunitTest_chart2_export3 then: Test name: testFormattedChartTitles::TestBody equality assertion failed - Expected: 1 - Actual : 0 - In <>, XPath '/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr/a:uFillTx' number of nodes is incorrect So this just simplifies the calls to what they effectively did all this time. There is no failing document; I found this accidentally, reading the code. Change-Id: I6581c23e395a74140daa30a06cdca27b57f4e6a6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192111 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192178 Tested-by: allotropia jenkins <[email protected]> Reviewed-by: Thorsten Behrens <[email protected]> diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index cd90db98f0bd..09555e364740 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -2468,7 +2468,7 @@ static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xMo } void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement, - bool bCheckDirect,bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, + bool /*bCheckDirect*/,bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, sal_Int16 nScriptType, const Reference< XPropertySet >& rXShapePropSet) { Reference< XPropertySet > rXPropSet = rRun; @@ -2528,8 +2528,7 @@ void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool break; } - if ((bCheckDirect && GetDirectProperty(rXPropSet, rXPropState, u"CharUnderline"_ustr)) - || GetProperty(rXPropSet, u"CharUnderline"_ustr)) + if (GetProperty(rXPropSet, u"CharUnderline"_ustr)) { switch ( *o3tl::doAccess<sal_Int16>(mAny) ) { @@ -2587,8 +2586,7 @@ void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool } } - if ((bCheckDirect && GetDirectProperty(rXPropSet, rXPropState, u"CharStrikeout"_ustr)) - || GetProperty(rXPropSet, u"CharStrikeout"_ustr)) + if (GetProperty(rXPropSet, u"CharStrikeout"_ustr)) { switch ( *o3tl::doAccess<sal_Int16>(mAny) ) { @@ -2698,8 +2696,7 @@ void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool else { // mso doesn't like text color to be placed after typeface - if ((bCheckDirect && GetDirectProperty(rXPropSet, rXPropState, u"CharColor"_ustr)) - || GetProperty(rXPropSet, u"CharColor"_ustr)) + if (GetProperty(rXPropSet, u"CharColor"_ustr)) { ::Color color( ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny) ); SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO)); @@ -2784,8 +2781,7 @@ void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool } if (underline - && ((bCheckDirect && GetDirectProperty(rXPropSet, rXPropState, u"CharUnderlineColor"_ustr)) - || GetProperty(rXPropSet, u"CharUnderlineColor"_ustr))) + && (GetProperty(rXPropSet, u"CharUnderlineColor"_ustr))) { ::Color color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny)); // if color is automatic, then we shouldn't write information about color but to take color from character
