tdf#73499 DOCX import: fix grouped linked textbox
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 <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/extras/ooxmlexport/data/tdf73499.docx b/sw/qa/extras/ooxmlexport/data/tdf73499.docx
new file mode 100644
index 0000000..605c01e
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf73499.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 486dc6c..53e80df 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<drawing::XShapes> xGroup(getShape(1), uno::UNO_QUERY_THROW);
// Get the textboxes of the groupshape
uno::Reference<text::XText> xTextBox1(xGroup->getByIndex(0), uno::UNO_QUERY_THROW);
uno::Reference<text::XText> xTextBox2(xGroup->getByIndex(1), uno::UNO_QUERY_THROW);
// Get the properties of the textboxes
uno::Reference<beans::XPropertySet> xTextBox1Properties(xTextBox1, uno::UNO_QUERY_THROW);
uno::Reference<beans::XPropertySet> xTextBox2Properties(xTextBox2, uno::UNO_QUERY_THROW);
// Get the name of the textboxes
uno::Reference<container::XNamed> xTextBox1Name(xTextBox1, uno::UNO_QUERY_THROW);
uno::Reference<container::XNamed> 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<OUString>());
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"Link name missing!", xTextBox1Name->getName(),
xTextBox2Properties->getPropertyValue("ChainPrevName").get<OUString>());
}
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 a3d6a4f..f8700fa 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<css::drawing::XShape> 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<drawing::XShape>(xGroup->getByIndex(i),uno::UNO_QUERY_THROW));
AttachTextBoxContentToShape(
uno::Reference<drawing::XShape>(xGroup->getByIndex(i), uno::UNO_QUERY));
if (xProps->getPropertyValue("TextBox").get<bool>())
// if this shape has to be a textbox, attach the frame
if (!xProps->getPropertyValue("TextBox").get<bool>())
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(m_xPendingTextBoxFrames.front()));
m_xPendingTextBoxFrames.pop();
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<container::XNamed> xName(xTextBox, uno::UNO_QUERY);
if (xName && !xName->getName().isEmpty())
sTextBoxName = xName->getName();
// Try to get the grabbag
uno::Sequence<beans::PropertyValue> 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<bool>())
{
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 (...)
{
SAL_WARN("writerfilter.dmapper", "Exception while trying to link textboxes!");
}
}