summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAttila Bakos (NISZ) <bakos.attilakaroly@nisz.hu>2022-06-14 10:38:46 +0200
committerMiklos Vajna <vmiklos@collabora.com>2023-01-17 09:25:29 +0000
commitd981737bcebf825949cd8b13c2c609a109abc984 (patch)
tree7737be9406e777e52d5568848df6e8cab9bd8cf2
parent044c63c631f0af832aa8452bc4a8b0b38dc91c23 (diff)
tdf#149550 sw: fix crash by implementing nested textbox copy
Grouped shapes with a nested textbox were copied without the textbox with frequent crashing. Regression from commit 2951cbdf3a6e2b62461665546b47e1d253fcb834 "tdf#143574 OOXML export/import of textboxes in group shapes". Change-Id: Ie2cc24f10706d8999026dc92ebad21f2c5673003 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135815 Tested-by: László Németh <nemeth@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143370 Tested-by: Miklos Vajna <vmiklos@collabora.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
-rw-r--r--sw/inc/textboxhelper.hxx11
-rw-r--r--sw/qa/extras/uiwriter/data/tdf149550.docxbin0 -> 29727 bytes
-rw-r--r--sw/qa/extras/uiwriter/uiwriter4.cxx25
-rw-r--r--sw/source/core/doc/DocumentLayoutManager.cxx65
-rw-r--r--sw/source/core/doc/textboxhelper.cxx81
5 files changed, 120 insertions, 62 deletions
diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx
index a389634c60eb..b0ab5618b466 100644
--- a/sw/inc/textboxhelper.hxx
+++ b/sw/inc/textboxhelper.hxx
@@ -27,6 +27,7 @@ class SdrObject;
class SfxItemSet;
class SwFrameFormat;
class SwFrameFormats;
+class SwFormatAnchor;
class SwFormatContent;
class SwDoc;
namespace tools
@@ -204,6 +205,8 @@ class SwTextBoxNode
// (and the textboxes)
SwFrameFormat* m_pOwnerShapeFormat;
+ mutable bool m_bIsCloningInProgress;
+
public:
// Not needed.
SwTextBoxNode() = delete;
@@ -251,6 +254,14 @@ public:
size_t GetTextBoxCount() const { return m_pTextBoxes.size(); };
// Returns with a const collection of textboxes owned by this node.
std::map<SdrObject*, SwFrameFormat*> GetAllTextBoxes() const;
+
+ void Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget, bool bSetAttr,
+ bool bMakeFrame) const;
+
+private:
+ void Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ const SdrObject* pSrcObj, SdrObject* pDestObj, bool bSetAttr,
+ bool bMakeFrame) const;
};
#endif // INCLUDED_SW_INC_TEXTBOXHELPER_HXX
diff --git a/sw/qa/extras/uiwriter/data/tdf149550.docx b/sw/qa/extras/uiwriter/data/tdf149550.docx
new file mode 100644
index 000000000000..3434fc1fff93
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data/tdf149550.docx
Binary files differ
diff --git a/sw/qa/extras/uiwriter/uiwriter4.cxx b/sw/qa/extras/uiwriter/uiwriter4.cxx
index fc7bd27e3124..76f0506d52fa 100644
--- a/sw/qa/extras/uiwriter/uiwriter4.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter4.cxx
@@ -292,6 +292,7 @@ public:
void testInsertPdf();
void testTdf143760WrapContourToOff();
void testHatchFill();
+ void testNestedGroupTextBoxCopyCrash();
CPPUNIT_TEST_SUITE(SwUiWriterTest4);
CPPUNIT_TEST(testTdf96515);
@@ -417,6 +418,7 @@ public:
CPPUNIT_TEST(testInsertPdf);
CPPUNIT_TEST(testTdf143760WrapContourToOff);
CPPUNIT_TEST(testHatchFill);
+ CPPUNIT_TEST(testNestedGroupTextBoxCopyCrash);
CPPUNIT_TEST_SUITE_END();
};
@@ -4140,6 +4142,29 @@ void SwUiWriterTest4::testHatchFill()
CPPUNIT_ASSERT_EQUAL(sal_Int32(30), getProperty<sal_Int32>(getShape(1), "FillTransparence"));
}
+void SwUiWriterTest4::testNestedGroupTextBoxCopyCrash()
+{
+ createSwDoc(DATA_DIRECTORY, "tdf149550.docx");
+
+ dispatchCommand(mxComponent, ".uno:SelectAll", {});
+ Scheduler::ProcessEventsToIdle();
+ dispatchCommand(mxComponent, ".uno:Copy", {});
+ Scheduler::ProcessEventsToIdle();
+ // This crashed here before the fix.
+ SwXTextDocument* pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+ CPPUNIT_ASSERT(pXTextDocument);
+ pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_ESCAPE);
+ Scheduler::ProcessEventsToIdle();
+ dispatchCommand(mxComponent, ".uno:Paste", {});
+ Scheduler::ProcessEventsToIdle();
+
+ CPPUNIT_ASSERT_MESSAGE("Where is the doc, it crashed, isn't it?!", mxComponent);
+
+ auto pLayout = parseLayoutDump();
+ // There must be 2 textboxes!
+ assertXPath(pLayout, "/root/page/body/txt/anchored/fly[2]");
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(SwUiWriterTest4);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/doc/DocumentLayoutManager.cxx b/sw/source/core/doc/DocumentLayoutManager.cxx
index 006501b3aa36..a03d5dc1d60d 100644
--- a/sw/source/core/doc/DocumentLayoutManager.cxx
+++ b/sw/source/core/doc/DocumentLayoutManager.cxx
@@ -463,67 +463,6 @@ SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat(
if( bMakeFrames )
pDest->MakeFrames();
- // If the draw format has a TextBox, then copy its fly format as well.
- if (rSource.Which() == RES_DRAWFRMFMT && rSource.GetOtherTextBoxFormats())
- {
- auto pObj = rSource.FindRealSdrObject();
- auto pTextBoxNd = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pDest));
- pDest->SetOtherTextBoxFormats(pTextBoxNd);
-
- if (pObj)
- {
- const bool bIsGroupObj = pObj->getChildrenOfSdrObject();
- for (size_t it = 0;
- it < (bIsGroupObj ? pObj->getChildrenOfSdrObject()->GetObjCount() : 1); it++)
- {
- auto pChild = bIsGroupObj ? pObj->getChildrenOfSdrObject()->GetObj(it)
- : const_cast<SdrObject*>(pObj);
- if (auto pSourceTextBox
- = SwTextBoxHelper::getOtherTextBoxFormat(&rSource, RES_DRAWFRMFMT, pChild))
- {
- SwFormatAnchor boxAnchor(rNewAnchor);
- if (RndStdIds::FLY_AS_CHAR == boxAnchor.GetAnchorId())
- {
- // AS_CHAR *must not* be set on textbox fly-frame
- boxAnchor.SetType(RndStdIds::FLY_AT_CHAR);
- }
- // presumably these anchors are supported though not sure
- assert(RndStdIds::FLY_AT_CHAR == boxAnchor.GetAnchorId()
- || RndStdIds::FLY_AT_PARA == boxAnchor.GetAnchorId()
- || boxAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE);
-
- if (!bMakeFrames && rNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
- {
- // If the draw format is as-char, then it will be copied with bMakeFrames=false, but
- // doing the same for the fly format would result in not making fly frames at all.
- bMakeFrames = true;
- }
- SwFrameFormat* pDestTextBox
- = CopyLayoutFormat(*pSourceTextBox, boxAnchor, bSetTextFlyAtt, bMakeFrames);
-
- SwAttrSet aSet(pDest->GetAttrSet());
- SwFormatContent aContent(
- pDestTextBox->GetContent().GetContentIdx()->GetNode().GetStartNode());
- aSet.Put(aContent);
- pDest->SetFormatAttr(aSet);
-
- // Link FLY and DRAW formats, so it becomes a text box
- SdrObject* pNewObj = pDest->FindRealSdrObject();
- if (bIsGroupObj && pNewObj
- && pNewObj->getChildrenOfSdrObject()
- && (pNewObj->getChildrenOfSdrObject()->GetObjCount() > it)
- && pNewObj->getChildrenOfSdrObject()->GetObj(it))
- pNewObj = pNewObj->getChildrenOfSdrObject()->GetObj(it);
- pTextBoxNd->AddTextBox(pNewObj, pDestTextBox);
- pDestTextBox->SetOtherTextBoxFormats(pTextBoxNd);
- }
-
- if (!bIsGroupObj)
- break;
- }
- }
- }
-
if (pDest->GetName().isEmpty())
{
// Format name should have unique name. Let's use object name as a fallback
@@ -532,6 +471,10 @@ SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat(
pDest->SetName(pObj->GetName());
}
+ // If the draw format has a TextBox, then copy its fly format as well.
+ if (const auto& pTextBoxes = rSource.GetOtherTextBoxFormats())
+ pTextBoxes->Clone(&m_rDoc, rNewAnchor, pDest, bSetTextFlyAtt, bMakeFrames);
+
return pDest;
}
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
index 41ca905f5d19..bcdf25002305 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -247,7 +247,7 @@ void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
{
// If the shape do not have a texbox node and textbox,
// create that for the shape.
- auto pTextBox = std::shared_ptr<SwTextBoxNode>(new SwTextBoxNode(pShapeFormat));
+ auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShapeFormat));
pTextBox->AddTextBox(pObj, pFormat);
pShapeFormat->SetOtherTextBoxFormats(pTextBox);
pFormat->SetOtherTextBoxFormats(pTextBox);
@@ -1525,6 +1525,8 @@ SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
assert(pOwnerShape);
assert(pOwnerShape->Which() == RES_DRAWFRMFMT);
+ m_bIsCloningInProgress = false;
+
m_pOwnerShapeFormat = pOwnerShape;
if (!m_pTextBoxes.empty())
m_pTextBoxes.clear();
@@ -1683,4 +1685,81 @@ std::map<SdrObject*, SwFrameFormat*> SwTextBoxNode::GetAllTextBoxes() const
return aRet;
}
+void SwTextBoxNode::Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ bool bSetAttr, bool bMakeFrame) const
+{
+ if (!o_pTarget || !pDoc)
+ return;
+
+ if (o_pTarget->Which() != RES_DRAWFRMFMT)
+ return;
+
+ if (m_bIsCloningInProgress)
+ return;
+
+ m_bIsCloningInProgress = true;
+
+ Clone_Impl(pDoc, rNewAnc, o_pTarget, m_pOwnerShapeFormat->FindSdrObject(),
+ o_pTarget->FindSdrObject(), bSetAttr, bMakeFrame);
+
+ m_bIsCloningInProgress = false;
+}
+
+void SwTextBoxNode::Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ const SdrObject* pSrcObj, SdrObject* pDestObj, bool bSetAttr,
+ bool bMakeFrame) const
+{
+ if (!pSrcObj || !pDestObj)
+ return;
+
+ auto pSrcList = pSrcObj->getChildrenOfSdrObject();
+ auto pDestList = pDestObj->getChildrenOfSdrObject();
+
+ if (pSrcList && pDestList)
+ {
+ if (pSrcList->GetObjCount() != pDestList->GetObjCount())
+ return;
+
+ for (size_t i = 0; i < pSrcList->GetObjCount(); ++i)
+ {
+ Clone_Impl(pDoc, rNewAnc, o_pTarget, pSrcList->GetObj(i), pDestList->GetObj(i),
+ bSetAttr, bMakeFrame);
+ }
+ return;
+ }
+
+ if (!pSrcList && !pDestList)
+ {
+ if (auto pSrcFormat = GetTextBox(pSrcObj))
+ {
+ SwFormatAnchor aNewAnchor(rNewAnc);
+ if (aNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ aNewAnchor.SetType(RndStdIds::FLY_AT_CHAR);
+
+ if (!bMakeFrame)
+ bMakeFrame = true;
+ }
+
+ if (auto pTargetFormat = pDoc->getIDocumentLayoutAccess().CopyLayoutFormat(
+ *pSrcFormat, aNewAnchor, bSetAttr, bMakeFrame))
+ {
+ if (!o_pTarget->GetOtherTextBoxFormats())
+ {
+ auto pNewTextBoxes = std::make_shared<SwTextBoxNode>(SwTextBoxNode(o_pTarget));
+ o_pTarget->SetOtherTextBoxFormats(pNewTextBoxes);
+ pNewTextBoxes->AddTextBox(pDestObj, pTargetFormat);
+ pTargetFormat->SetOtherTextBoxFormats(pNewTextBoxes);
+ }
+ else
+ {
+ o_pTarget->GetOtherTextBoxFormats()->AddTextBox(pDestObj, pTargetFormat);
+ pTargetFormat->SetOtherTextBoxFormats(o_pTarget->GetOtherTextBoxFormats());
+ }
+ o_pTarget->SetFormatAttr(pTargetFormat->GetContent());
+ }
+ }
+ }
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */