From f81800193a942b3f68c61a5cede634f3eeb47b1f Mon Sep 17 00:00:00 2001 From: "Attila Bakos (NISZ)" Date: Fri, 4 Mar 2022 08:27:49 +0100 Subject: tdf#73499 DOCX import: fix grouped linked textbox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only ungrouped text boxes were imported correctly. Grouped textboxes lost their linking, resulting broken layout. Now the linking is fixed for DrawingML. Note: in old MSO versions, linking needed grouping. To import these DOC documents correctly, convert them to DOCX/DrawingML in MSO before opening them in Writer. Change-Id: Ib5a8744d783a9c95c42447d204e17891b3aea7bd Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130950 Tested-by: László Németh Reviewed-by: László Németh --- sw/qa/extras/ooxmlexport/data/tdf73499.docx | Bin 0 -> 16470 bytes sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 28 +++++++ writerfilter/source/dmapper/DomainMapper_Impl.cxx | 91 +++++++++++++++++++++- 3 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 sw/qa/extras/ooxmlexport/data/tdf73499.docx diff --git a/sw/qa/extras/ooxmlexport/data/tdf73499.docx b/sw/qa/extras/ooxmlexport/data/tdf73499.docx new file mode 100644 index 000000000000..605c01e2b3ac Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf73499.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx index 486dc6c3d1e9..53e80df5a301 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx @@ -346,6 +346,34 @@ DECLARE_OOXMLEXPORT_TEST(testTdf148111, "tdf148111.docx") CPPUNIT_ASSERT(!xFields->hasMoreElements()); } +DECLARE_OOXMLEXPORT_TEST(TestTdf73499, "tdf73499.docx") +{ + // Ensure, the bugdoc is opened + CPPUNIT_ASSERT(mxComponent); + // Get the groupshape + uno::Reference xGroup(getShape(1), uno::UNO_QUERY_THROW); + + // Get the textboxes of the groupshape + uno::Reference xTextBox1(xGroup->getByIndex(0), uno::UNO_QUERY_THROW); + uno::Reference xTextBox2(xGroup->getByIndex(1), uno::UNO_QUERY_THROW); + + // Get the properties of the textboxes + uno::Reference xTextBox1Properties(xTextBox1, uno::UNO_QUERY_THROW); + uno::Reference xTextBox2Properties(xTextBox2, uno::UNO_QUERY_THROW); + + // Get the name of the textboxes + uno::Reference xTextBox1Name(xTextBox1, uno::UNO_QUERY_THROW); + uno::Reference xTextBox2Name(xTextBox2, uno::UNO_QUERY_THROW); + + // Check for the links, before the fix that were missing + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Link name missing!", xTextBox2Name->getName(), + xTextBox1Properties->getPropertyValue("ChainNextName").get()); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Link name missing!", xTextBox1Name->getName(), + xTextBox2Properties->getPropertyValue("ChainPrevName").get()); +} + DECLARE_OOXMLEXPORT_TEST(testTdf81507, "tdf81507.docx") { xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index a3d6a4f69498..f8700faee88c 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -4634,20 +4634,103 @@ void DomainMapper_Impl::PopTextBoxContent() void DomainMapper_Impl::AttachTextBoxContentToShape(css::uno::Reference xShape) { + // Without textbox or shape pointless to continue if (m_xPendingTextBoxFrames.empty() || !xShape) return; uno::Reference< drawing::XShapes >xGroup(xShape, uno::UNO_QUERY); uno::Reference< beans::XPropertySet >xProps(xShape, uno::UNO_QUERY); + // If this is a group go inside if (xGroup) for (sal_Int32 i = 0; i < xGroup->getCount(); ++i) - AttachTextBoxContentToShape(uno::Reference(xGroup->getByIndex(i),uno::UNO_QUERY_THROW)); + AttachTextBoxContentToShape( + uno::Reference(xGroup->getByIndex(i), uno::UNO_QUERY)); - if (xProps->getPropertyValue("TextBox").get()) + // if this shape has to be a textbox, attach the frame + if (!xProps->getPropertyValue("TextBox").get()) + return; + + // if this is a textbox there must be a waiting frame + auto xTextBox = m_xPendingTextBoxFrames.front(); + if (!xTextBox) + return; + + // Pop the pending frames + m_xPendingTextBoxFrames.pop(); + + // Attach the textbox to the shape + try + { + xProps->setPropertyValue("TextBoxContent", uno::Any(xTextBox)); + } + catch (...) + { + SAL_WARN("writerfilter.dmapper", "Exception while trying to attach textboxes!"); + return; + } + + // If attaching is successful, then do the linking + try + { + // Get the name of the textbox + OUString sTextBoxName; + uno::Reference xName(xTextBox, uno::UNO_QUERY); + if (xName && !xName->getName().isEmpty()) + sTextBoxName = xName->getName(); + + // Try to get the grabbag + uno::Sequence aOldGrabBagSeq; + if (xProps->getPropertySetInfo()->hasPropertyByName("InteropGrabBag")) + xProps->getPropertyValue("InteropGrabBag") >>= aOldGrabBagSeq; + + // If the grabbag successfully get... + if (!aOldGrabBagSeq.hasElements()) + return; + + // Check for the existing linking information + bool bSuccess = false; + beans::PropertyValues aNewGrabBagSeq; + const auto& aHasLink = lcl_getGrabBagValue(aOldGrabBagSeq, "TxbxHasLink"); + + // If there must be a link, do it + if (aHasLink.hasValue() && aHasLink.get()) + { + auto aLinkProp = comphelper::makePropertyValue("LinkChainName", sTextBoxName); + for (sal_uInt32 i = 0; i < aOldGrabBagSeq.size(); ++i) + { + aNewGrabBagSeq.realloc(i + 1); + // If this is the link name replace it + if (!aOldGrabBagSeq[i].Name.isEmpty() && !aLinkProp.Name.isEmpty() + && (aOldGrabBagSeq[i].Name == aLinkProp.Name)) + { + aNewGrabBagSeq.getArray()[i] = aLinkProp; + bSuccess = true; + } + // else copy + else + aNewGrabBagSeq.getArray()[i] = aOldGrabBagSeq[i]; + } + + // If there was no replacement, append the linking data + if (!bSuccess) + { + aNewGrabBagSeq.realloc(aNewGrabBagSeq.size() + 1); + aNewGrabBagSeq.getArray()[aNewGrabBagSeq.size() - 1] = aLinkProp; + bSuccess = true; + } + } + + // If the linking changed the grabbag, apply the modifications + if (aNewGrabBagSeq.hasElements() && bSuccess) + { + xProps->setPropertyValue("InteropGrabBag", uno::Any(aNewGrabBagSeq)); + m_vTextFramesForChaining.push_back(xShape); + } + } + catch (...) { - xProps->setPropertyValue("TextBoxContent", uno::Any(m_xPendingTextBoxFrames.front())); - m_xPendingTextBoxFrames.pop(); + SAL_WARN("writerfilter.dmapper", "Exception while trying to link textboxes!"); } } -- cgit