sw/qa/extras/uiwriter/uiwriter3.cxx | 8 ++- sw/qa/writerfilter/ooxml/data/floattable-anchorpos.docx |binary sw/qa/writerfilter/ooxml/ooxml.cxx | 37 +++++++++++++++ sw/source/writerfilter/ooxml/OOXMLFastContextHandler.cxx | 19 +++++++ 4 files changed, 63 insertions(+), 1 deletion(-)
New commits: commit 86b0957cf18ea6705c8afa09bf4aae1fbb7db22e Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Wed Jul 9 13:37:02 2025 +0200 Commit: Caolán McNamara <caolan.mcnam...@collabora.com> CommitDate: Thu Jul 10 09:43:16 2025 +0200 tdf#167379 sw floattable: make dummy paragraph from DOCX import less visible Open the bugdoc, the text C and D appears to have the same vertical position in Word, but D is shifted down in Writer. What happens is that since commit 01ad8ec4bb5425446e95dbada81de435646824b4 (sw floattable: fix lost tables around a floating table from DOCX, 2023-06-05), an empty paragraph is inserted between two floating tables immediately following each other, to have a suitable anchor point for all floating tables. But it wasn't considered that the anchor point takes vertical space, which has an impact on layout. Fix the problem by keeping the empty paragraph in the model & layout (the split fly frame needs those), but setting the paragraph properties (spacing, line spacing) in a way that the created text frame has 0 height, so it has no negative impact on the position of the text after the text frame. This is similar to what the DOC import does in WW8TabDesc::CreateSwTable(), though there we set the font size of the paragraph to 1pt, which improves the layout but still has a small impact. An alternative I considered is to set the "hidden" character property on the text node's item set, but that would create no text frame at all, so we would again have no anchor point for the floating table, so that would not work. Change-Id: Iceb2f51ad42028434822cb9c0701a7e6d6545845 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187566 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx index de126574ca78..466a2664e065 100644 --- a/sw/qa/extras/uiwriter/uiwriter3.cxx +++ b/sw/qa/extras/uiwriter/uiwriter3.cxx @@ -844,7 +844,13 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf134227) CPPUNIT_ASSERT_EQUAL(4, getShapes()); - dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {}); + // Go to the end of the document, outside the dummy paragraph that just serves as an anchor for + // the floating table. + uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier( + xModel->getCurrentController(), uno::UNO_QUERY); + uno::Reference<text::XTextViewCursor> xViewCursor = xTextViewCursorSupplier->getViewCursor(); + xViewCursor->gotoEnd(/*bExpand=*/false); dispatchCommand(mxComponent, u".uno:SelectAll"_ustr, {}); // Without the fix in place, it would have crashed here diff --git a/sw/qa/writerfilter/ooxml/data/floattable-anchorpos.docx b/sw/qa/writerfilter/ooxml/data/floattable-anchorpos.docx new file mode 100644 index 000000000000..d93e5772aa3f Binary files /dev/null and b/sw/qa/writerfilter/ooxml/data/floattable-anchorpos.docx differ diff --git a/sw/qa/writerfilter/ooxml/ooxml.cxx b/sw/qa/writerfilter/ooxml/ooxml.cxx index c5f9af177a13..9a9d2745ba7c 100644 --- a/sw/qa/writerfilter/ooxml/ooxml.cxx +++ b/sw/qa/writerfilter/ooxml/ooxml.cxx @@ -17,6 +17,14 @@ #include <fmtcntnt.hxx> #include <ndtxt.hxx> +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <txtfrm.hxx> +#include <sortedobjs.hxx> +#include <anchoredobject.hxx> +#include <flyfrm.hxx> using namespace ::com::sun::star; @@ -109,6 +117,35 @@ CPPUNIT_TEST_FIXTURE(Test, testNestedRuns) // Without the accompanying fix in place, this test would have failed, the shape was empty. CPPUNIT_ASSERT_EQUAL(u"Test text box"_ustr, pTextNode->GetText()); } + +CPPUNIT_TEST_FIXTURE(Test, testFloatingTableAnchorPos) +{ + // Given a document with a floating table (text: C) and a visually first body paragraph (text: + // D): + // When loading that document: + createSwDoc("floattable-anchorpos.docx"); + + // Then make sure that D is not shifted down vertically, compared to C: + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + SwRootFrame* pLayout = pWrtShell->GetLayout(); + SwPageFrame* pPage = pLayout->GetLower()->DynCastPageFrame(); + SwTextFrame* pBodyPara1 = pPage->FindFirstBodyContent()->DynCastTextFrame(); + CPPUNIT_ASSERT(pBodyPara1->HasSplitFlyDrawObjs()); + SwSortedObjs& rFlys1 = *pBodyPara1->GetDrawObjs(); + SwFlyFrame* pFly1 = rFlys1[0]->DynCastFlyFrame(); + SwTextFrame* pFly1Text = pFly1->ContainsContent()->DynCastTextFrame(); + CPPUNIT_ASSERT_EQUAL(u"C"_ustr, pFly1Text->GetText()); + SwTwips nFlyTop = pFly1Text->getFrameArea().Top(); + SwTextFrame* pBodyPara2 = pBodyPara1->GetNext()->DynCastTextFrame(); + CPPUNIT_ASSERT_EQUAL(u"D"_ustr, pBodyPara2->GetText()); + SwTwips nBodyTop = pBodyPara2->getFrameArea().Top(); + SwTwips nDiff = std::abs(nBodyTop - nFlyTop); + // Without the accompanying fix in place, this test would have failed with: + // - Expected less or equal than: 1 + // - Actual : 243 + // i.e. the vertical position of D was too big. + CPPUNIT_ASSERT_LESSEQUAL(static_cast<SwTwips>(1), nDiff); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/writerfilter/ooxml/OOXMLFastContextHandler.cxx b/sw/source/writerfilter/ooxml/OOXMLFastContextHandler.cxx index 046d8c6aacb0..d8754cc6d2a6 100644 --- a/sw/source/writerfilter/ooxml/OOXMLFastContextHandler.cxx +++ b/sw/source/writerfilter/ooxml/OOXMLFastContextHandler.cxx @@ -1618,6 +1618,25 @@ void OOXMLFastContextHandlerTextTable::lcl_startFastElement // subset of '<resource name="CT_P" resource="Stream">' in model.xml: startParagraphGroup(); sendTableDepth(); + + OOXMLPropertySet::Pointer_t pSprms(new OOXMLPropertySet); + { + // Configure the dummy paragraph to have a zero height, so it has no impact on the + // layout. + OOXMLPropertySet::Pointer_t pAttributes(new OOXMLPropertySet); + OOXMLValue pSBVal = OOXMLValue::createInteger(0); + pAttributes->add(NS_ooxml::LN_CT_Spacing_before, pSBVal, OOXMLProperty::ATTRIBUTE); + OOXMLValue pSAVal = OOXMLValue::createInteger(0); + pAttributes->add(NS_ooxml::LN_CT_Spacing_after, pSAVal, OOXMLProperty::ATTRIBUTE); + OOXMLValue pSLRVal = OOXMLValue::createInteger(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_exact); + pAttributes->add(NS_ooxml::LN_CT_Spacing_lineRule, pSLRVal, OOXMLProperty::ATTRIBUTE); + OOXMLValue pSLVal = OOXMLValue::createInteger(0); + pAttributes->add(NS_ooxml::LN_CT_Spacing_line, pSLVal, OOXMLProperty::ATTRIBUTE); + OOXMLValue pSprm = OOXMLValue::createPropertySet(pAttributes); + pSprms->add(NS_ooxml::LN_CT_PPrBase_spacing, pSprm, OOXMLProperty::SPRM); + } + mpStream->props(pSprms.get()); + endOfParagraph(); }