tdf#148687 tdf#149173 tdf#149546 Sw: Fix crash of textboxes

Regressions from:
tdf#147485 sw: fix group shape crash using std::shared_ptr
2110597ac730fa07dbbdd603fda82b182ed27c9e

Fix list:

1) Using reference instead of copy assignment in textboxheper
2) Cleanup: Unused parts of textboheper were removed
3) Fixing destruction of textboxes, in case when first the shape
is removed, with clearing all textboxes from the doc and the shape
before the pointer is released.
4) In Dmapper missing style conversion were fixed.
5) Dont import sections in textboxes, unless the hack of dummy
para before tables in sections will be applied and the para
with anchored objects inside will be removed with the objects.
6) ConvertTextRangeToTextFrame also fixed, so embed frames in
frames are imported from now. (Also textboxes in frames, this
can be useful when there is a floating table having group
textbox with nested complex content inside, or floating table
in floating table, etc...) Note: Follow up commit will enable
group textbox import in frames and tables.
7) Import of group textboxes with complex content in header/footer
were impossible, from now this also supported both from docx and
odt. (Test included)
8) Guard class for blocking unwanted recursive textbox sync
has been introduced, and maybe some speedup with group
textbox import have been achieved.
9) The anchor sync method got a new function what avoids
uneeded sync when the anchor is the same.
10) Sync of As_char textboxes during layout calc caused crash
so that has a workaround from now, for docx import anhor change
and undo. That syncs before the layout calc starts so later sync
not needed.
11) A memory leak was found in undo, what has been fixed.

Change-Id: I69d5d79cc120e3a70ba9285be32ec36a434b2a04
diff --git a/sw/inc/textboxhelper.hxx b/sw/inc/textboxhelper.hxx
index 112b312..c024ee1 100644
--- a/sw/inc/textboxhelper.hxx
+++ b/sw/inc/textboxhelper.hxx
@@ -179,6 +179,9 @@ public:
    /// vector filled with the textboxes.
    static std::vector<SwFrameFormat*> CollectTextBoxes(const SdrObject* pGroupObject,
                                                        SwFrameFormat* pFormat);

    // Compares the anchor of the fist and second given format, and decides whether sync needed.
    static bool isAnchorSyncNeeded(const SwFrameFormat* pFirst, const SwFrameFormat* pSecond);
};

/// Textboxes are basically textframe + shape pairs. This means one shape has one frame.
@@ -187,6 +190,8 @@ public:
/// it can have multiple textboxes.
class SwTextBoxNode
{
    friend class SwTextBoxLockGuard;

    // One TextBox-entry
    struct SwTextBoxElement
    {
@@ -194,8 +199,6 @@ class SwTextBoxNode
        SwFrameFormat* m_pTextBoxFormat;
        // The Draw object where the textbox belongs to
        SdrObject* m_pDrawObject;
        // This is for indicating if the textbox is in special case: for example during undo.
        bool m_bIsActive;
    };

    // This vector stores the textboxes what belongs to this node
@@ -204,8 +207,12 @@ class SwTextBoxNode
    // (and the textboxes)
    SwFrameFormat* m_pOwnerShapeFormat;

    // Prevents oscillating during recursive clone calling.
    mutable bool m_bIsCloningInProgress;

    // Protection againist looping
    bool m_bLock;

public:
    // Not needed.
    SwTextBoxNode() = delete;
@@ -238,22 +245,24 @@ public:
    // to the given shape (pDrawObject)
    SwFrameFormat* GetTextBox(const SdrObject* pDrawObject) const;

    // Is this textbox has special state, undo for example?
    bool IsTextBoxActive(const SdrObject* pDrawObject) const;

    // Setters for the state flag.
    void SetTextBoxInactive(const SdrObject* pDrawObject);
    void SetTextBoxActive(const SdrObject* pDrawObject);
    // Clears all textboxes of this node from the doc and also from here.
    void ClearAll();

    // If this is a group shape, that returns true.
    bool IsGroupTextBox() const;

    // This returns with the shape what this class belongs to.
    SwFrameFormat* GetOwnerShape() { return m_pOwnerShapeFormat; };

    // This will give the current number of textboxes.
    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;

    // Does the copy, and assign of all textboxes of this node to the given format.
    // Important: The given format have to be a shape-format, and must have same structure
    // as the Owner shape has. If the stucture different, the cloning will be aborted.
    void Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget, bool bSetAttr,
               bool bMakeFrame) const;

@@ -263,6 +272,21 @@ private:
                    bool bMakeFrame) const;
};

// Helper class for preventing unwanted sync calls.
class SwTextBoxLockGuard
{
    SwTextBoxNode& m_rTextBoxes;

public:
    SwTextBoxLockGuard(SwTextBoxNode& rTextBoxes)
        : m_rTextBoxes(rTextBoxes)
    {
        m_rTextBoxes.m_bLock = true;
    }

    ~SwTextBoxLockGuard() { m_rTextBoxes.m_bLock = false; }
};

#endif // INCLUDED_SW_INC_TEXTBOXHELPER_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ooxmlexport/data/tdf149546.docx b/sw/qa/extras/ooxmlexport/data/tdf149546.docx
new file mode 100644
index 0000000..2811ec1
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf149546.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
index cb654d4..337926c 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport14.cxx
@@ -873,6 +873,12 @@ CPPUNIT_TEST_FIXTURE(SwModelTestBase, testTdf147485)
    load(DATA_DIRECTORY, "Tdf147485.docx");
}

CPPUNIT_TEST_FIXTURE(SwModelTestBase, testTdf149546)
{
    // Before the fix this was impossible.
    load(DATA_DIRECTORY, "tdf149546.docx");
}

CPPUNIT_TEST_FIXTURE(SwModelTestBase, testUserField)
{
    // Create an in-memory empty document with a user field.
diff --git a/sw/source/core/doc/docfly.cxx b/sw/source/core/doc/docfly.cxx
index 985cf6b..41756ff 100644
--- a/sw/source/core/doc/docfly.cxx
+++ b/sw/source/core/doc/docfly.cxx
@@ -916,11 +916,10 @@ bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList,
                    pNd->InsertItem( aFormat, aPos.nContent.GetIndex(), 0 );

                    // Has a textbox attached to the format? Sync it as well!
                    if (SwTextBoxHelper::getOtherTextBoxFormat(pContact->GetFormat(),
                                                               RES_DRAWFRMFMT))
                    if (pContact->GetFormat() && pContact->GetFormat()->GetOtherTextBoxFormats())
                    {
                        SwTextBoxHelper::syncFlyFrameAttr(*pContact->GetFormat(),
                                                          pContact->GetFormat()->GetAttrSet(), pObj);
                        SwTextBoxHelper::synchronizeGroupTextBoxProperty(
                            SwTextBoxHelper::changeAnchor, pContact->GetFormat(), pObj);
                    }
                }
                break;
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
index 1ac5ccf..1360ac7 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -66,8 +66,6 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCo
    assert(pShape);
    assert(pObject);

    const bool bIsGroupObj = dynamic_cast<SdrObjGroup*>(pObject->getParentSdrObjectFromSdrObject());

    // If TextBox wasn't enabled previously
    if (pShape->GetOtherTextBoxFormats() && pShape->GetOtherTextBoxFormats()->GetTextBox(pObject))
        return;
@@ -124,9 +122,8 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCo
    }
    else
    {
        auto pTextBox = pShape->GetOtherTextBoxFormats();
        auto& pTextBox = pShape->GetOtherTextBoxFormats();
        pTextBox->AddTextBox(pObject, pFormat);
        pShape->SetOtherTextBoxFormats(pTextBox);
        pFormat->SetOtherTextBoxFormats(pTextBox);
    }
    // Initialize properties.
@@ -188,8 +185,8 @@ void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCo
    if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
        syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObject);

    if (bIsGroupObj)
        doTextBoxPositioning(pShape, pObject);
    changeAnchor(pShape, pObject);
    syncTextBoxSize(pShape, pObject);

    // Check if the shape had text before and move it to the new textframe
    if (!bCopyText || sCopyableText.isEmpty())
@@ -220,35 +217,13 @@ void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
        pFormat = pTextFrame->GetFrameFormat();
    if (!pFormat)
        return;
    std::vector<std::pair<beans::Property, uno::Any>> aOldProps;

    // If there is a format, check if the shape already has a textbox assigned to.
    if (auto pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
    if (auto& pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
    {
        // If it has a texbox, destroy it.
        if (pTextBoxNode->GetTextBox(pObj))
        {
            auto xOldFrame
                = pObj->getUnoShape()->queryInterface(cppu::UnoType<text::XTextRange>::get());
            if (xOldFrame.hasValue())
            {
                uno::Reference<beans::XPropertySet> xOldprops(xOldFrame, uno::UNO_QUERY);
                uno::Reference<beans::XPropertyState> xOldPropStates(xOldFrame, uno::UNO_QUERY);
                for (auto& rProp : xOldprops->getPropertySetInfo()->getProperties())
                {
                    try
                    {
                        if (xOldPropStates->getPropertyState(rProp.Name)
                            == beans::PropertyState::PropertyState_DIRECT_VALUE)
                            aOldProps.push_back(
                                std::pair(rProp, xOldprops->getPropertyValue(rProp.Name)));
                    }
                    catch (...)
                    {
                    }
                }
            }
            destroy(pShapeFormat, pObj);
        }
            pTextBoxNode->DelTextBox(pObj, true);
        // And set the new one.
        pTextBoxNode->AddTextBox(pObj, pFormat);
        pFormat->SetOtherTextBoxFormats(pTextBoxNode);
@@ -307,24 +282,7 @@ void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
    text::WritingMode eMode;
    if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
        syncProperty(pShapeFormat, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
    if (aOldProps.size())
    {
        for (auto& rProp : aOldProps)
        {
            try
            {
                xPropertySet->setPropertyValue(rProp.first.Name, rProp.second);
            }
            catch (...)
            {
            }
        }
    }
    if (pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
        && pFormat->GetAnchor().GetPageNum() == 0)
    {
        pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
    }

    // Do sync for the new textframe.
    synchronizeGroupTextBoxProperty(&changeAnchor, pShapeFormat, pObj);
    synchronizeGroupTextBoxProperty(&syncTextBoxSize, pShapeFormat, pObj);
@@ -335,12 +293,10 @@ void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject)
{
    // If a TextBox was enabled previously
    auto pTextBox = pShape->GetOtherTextBoxFormats();
    if (pTextBox && pTextBox->IsTextBoxActive(pObject))
    auto& pTextBox = pShape->GetOtherTextBoxFormats();
    if (pTextBox)
    {
        // Unlink the TextBox's text range from the original shape.
        pTextBox->SetTextBoxInactive(pObject);

        // Delete the associated TextFrame.
        pTextBox->DelTextBox(pObject, true);
    }
@@ -354,7 +310,7 @@ bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
    if (!pFormat || pFormat->Which() != nType)
        return false;

    auto pTextBox = pFormat->GetOtherTextBoxFormats();
    auto& pTextBox = pFormat->GetOtherTextBoxFormats();
    if (!pTextBox)
        return false;

@@ -995,7 +951,7 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_u
            }
        }
    }

    auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
    uno::Reference<beans::XPropertySet> const xPropertySet(
        SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
    xPropertySet->setPropertyValue(aPropertyName, aValue);
@@ -1192,8 +1148,10 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& 
    } while (pItem && (0 != pItem->Which()));

    if (aTextBoxSet.Count())
    {
        auto aGuard = SwTextBoxLockGuard(*rShape.GetOtherTextBoxFormats());
        pFormat->SetFormatAttr(aTextBoxSet);

    }
    DoTextBoxZOrderCorrection(&rShape, pObj);
}

@@ -1245,6 +1203,22 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
{
    if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
    {
        if (!isAnchorSyncNeeded(pShape, pFormat))
        {
            doTextBoxPositioning(pShape, pObj);
            DoTextBoxZOrderCorrection(pShape, pObj);
            if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
                && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR
                && pFormat->GetVertOrient().GetRelationOrient() != text::RelOrientation::PRINT_AREA)
            {
                SwFormatVertOrient aTmp = pFormat->GetVertOrient();
                aTmp.SetRelationOrient(text::RelOrientation::PRINT_AREA);
                pFormat->SetFormatAttr(aTmp);
            }

            return false;
        }

        const SwFormatAnchor& rOldAnch = pFormat->GetAnchor();
        const SwFormatAnchor& rNewAnch = pShape->GetAnchor();

@@ -1255,6 +1229,7 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)

        try
        {
            auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
            ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
            uno::Reference<beans::XPropertySet> const xPropertySet(
                SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
@@ -1271,6 +1246,7 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
            {
                if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
                {
                    assert(pNewCnt);
                    uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
                    xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
                    xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
@@ -1294,6 +1270,7 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
            {
                if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
                {
                    assert(pNewCnt);
                    uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
                    xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
                    xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
@@ -1308,7 +1285,13 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
                {
                    xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
                                                   aShapeHorRelOrient);
                    pFormat->SetFormatAttr(pShape->GetAnchor());
                    if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
                        && rNewAnch.GetPageNum() == 0)
                    {
                        pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
                    }
                    else
                        pFormat->SetFormatAttr(pShape->GetAnchor());
                }
            }
        }
@@ -1317,7 +1300,9 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
            SAL_WARN("sw.core", "SwTextBoxHelper::changeAnchor(): " << e.Message);
        }

        return doTextBoxPositioning(pShape, pObj) && DoTextBoxZOrderCorrection(pShape, pObj);
        doTextBoxPositioning(pShape, pObj);
        DoTextBoxZOrderCorrection(pShape, pObj);
        return true;
    }

    return false;
@@ -1331,7 +1316,7 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
    {
        // Do not create undo entry for the positioning
        ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());

        auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
        // Special treatment for AS_CHAR textboxes:
        if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
        {
@@ -1514,6 +1499,7 @@ bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)

    if (auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
    {
        auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
        const auto& rSize = getRelativeTextRectangle(pObj).GetSize();
        if (!rSize.IsEmpty())
        {
@@ -1536,6 +1522,8 @@ bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const Sdr
    if (pShpObj)
    {
        auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
        if (!pTextBox)
            return false;
        SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
        if (!pFrmObj)
        {
@@ -1623,19 +1611,103 @@ std::vector<SwFrameFormat*> SwTextBoxHelper::CollectTextBoxes(const SdrObject* p
    return vRet;
}

bool SwTextBoxHelper::isAnchorSyncNeeded(const SwFrameFormat* pFirst, const SwFrameFormat* pSecond)
{
    if (!pFirst)
        return false;

    if (!pSecond)
        return false;

    if (pFirst == pSecond)
        return false;

    if (!pFirst->GetOtherTextBoxFormats())
        return false;

    if (!pSecond->GetOtherTextBoxFormats())
        return false;

    if (pFirst->GetOtherTextBoxFormats() != pSecond->GetOtherTextBoxFormats())
        return false;

    if (pFirst->GetOtherTextBoxFormats()->GetOwnerShape() == pSecond
        || pFirst == pSecond->GetOtherTextBoxFormats()->GetOwnerShape())
    {
        const auto& rShapeAnchor
            = pFirst->Which() == RES_DRAWFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
        const auto& rFrameAnchor
            = pFirst->Which() == RES_FLYFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();

        if (rShapeAnchor.GetAnchorId() == rFrameAnchor.GetAnchorId())
        {
            if (rShapeAnchor.GetContentAnchor() && rFrameAnchor.GetContentAnchor())
            {
                if (rShapeAnchor.GetContentAnchor()->nContent
                    != rFrameAnchor.GetContentAnchor()->nContent)
                    return true;

                if (rShapeAnchor.GetContentAnchor()->nNode
                    != rFrameAnchor.GetContentAnchor()->nNode)
                    return true;

                return false;
            }

            if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE
                && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
            {
                if (rShapeAnchor.GetPageNum() == rFrameAnchor.GetPageNum())
                    return false;
                else
                    return true;
            }

            return true;
        }

        if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
            && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
        {
            if (rShapeAnchor.GetContentAnchor() && rFrameAnchor.GetContentAnchor())
            {
                if (rShapeAnchor.GetContentAnchor()->nContent
                    != rFrameAnchor.GetContentAnchor()->nContent)
                    return true;

                if (rShapeAnchor.GetContentAnchor()->nNode
                    != rFrameAnchor.GetContentAnchor()->nNode)
                    return true;

                return false;
            }
        }
        return true;
    }
    return false;
}

SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
{
    assert(pOwnerShape);
    assert(pOwnerShape->Which() == RES_DRAWFRMFMT);

    m_bIsCloningInProgress = false;
    m_bLock = false;

    m_pOwnerShapeFormat = pOwnerShape;
    if (!m_pTextBoxes.empty())
        m_pTextBoxes.clear();
}

SwTextBoxNode::~SwTextBoxNode() { m_pTextBoxes.clear(); }
SwTextBoxNode::~SwTextBoxNode()
{
    if (m_pTextBoxes.size() != 0)
    {
        SAL_WARN("sw.core", "SwTextBoxNode::~SwTextBoxNode(): Text-Box-Vector still not empty!");
        assert(false);
    }
}

void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBox)
{
@@ -1645,9 +1717,18 @@ void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBo
    assert(pDrawObject);

    SwTextBoxElement aElem;
    aElem.m_bIsActive = true;
    aElem.m_pDrawObject = pDrawObject;
    aElem.m_pTextBoxFormat = pNewTextBox;

    for (const auto& rE : m_pTextBoxes)
    {
        if (rE.m_pDrawObject == pDrawObject || rE.m_pTextBoxFormat == pNewTextBox)
        {
            SAL_WARN("sw.core", "SwTextBoxNode::AddTextBox(): Already exist!");
            return;
        }
    }

    auto pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject);
    if (pSwFlyDraw)
    {
@@ -1668,20 +1749,22 @@ void SwTextBoxNode::DelTextBox(const SdrObject* pDrawObject, bool bDelFromDoc)
        {
            if (bDelFromDoc)
            {
                m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
                it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
                    it->m_pTextBoxFormat);
                // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
                // then the ~SwFrameFormat() will call this method again to remove the entry.
                break;
                return;
            }
            else
            {
                it = m_pTextBoxes.erase(it);
                break;
                return;
            }
        }
        ++it;
    }

    SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
}

void SwTextBoxNode::DelTextBox(const SwFrameFormat* pTextBox, bool bDelFromDoc)
@@ -1695,25 +1778,41 @@ void SwTextBoxNode::DelTextBox(const SwFrameFormat* pTextBox, bool bDelFromDoc)
        {
            if (bDelFromDoc)
            {
                m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
                it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
                    it->m_pTextBoxFormat);
                // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
                // then the ~SwFrameFormat() will call this method again to remove the entry.
                break;
                return;
            }
            else
            {
                it = m_pTextBoxes.erase(it);
                break;
                return;
            }
        }
        ++it;
    }

    SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
}

SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
{
    assert(pDrawObject);
    assert(m_pOwnerShapeFormat);

    if (auto& pTextBoxes = m_pOwnerShapeFormat->GetOtherTextBoxFormats())
    {
        if (size_t(pTextBoxes.use_count()) != pTextBoxes->GetTextBoxCount() + size_t(1))
        {
            SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): RefCount and TexBox count mismatch!");
            assert(false);
        }
    }

    if (m_bLock)
        return nullptr;

    if (!m_pTextBoxes.empty())
    {
        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
@@ -1723,56 +1822,46 @@ SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
                return it->m_pTextBoxFormat;
            }
        }
        SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): Not found!");
    }

    return nullptr;
}

bool SwTextBoxNode::IsTextBoxActive(const SdrObject* pDrawObject) const
void SwTextBoxNode::ClearAll()
{
    assert(pDrawObject);
    // For loop control
    sal_uInt16 nLoopCount = 0;

    if (!m_pTextBoxes.empty())
    // Reference not enought, copy needed.
    const size_t nTextBoxCount = m_pTextBoxes.size();

    // For loop has problems: When one entry deleted, the iterator have
    // to be refreshed according to the new situation. So using While() instead.
    while (!m_pTextBoxes.empty())
    {
        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
        // Delete the last textbox of the vector from the doc
        // (what will call deregister in ~SwFrameFormat()
        m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
            m_pTextBoxes.back().m_pTextBoxFormat);

        // Check if we are looping
        if (nLoopCount > (nTextBoxCount + 1))
        {
            if (it->m_pDrawObject == pDrawObject)
            {
                return it->m_bIsActive;
            }
            SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Maximum loop count reached!");
            break;
        }
        else
        {
            nLoopCount++;
        }
    }
    return false;
}

void SwTextBoxNode::SetTextBoxActive(const SdrObject* pDrawObject)
{
    assert(pDrawObject);

    // Ensure the vector is empty.
    if (!m_pTextBoxes.empty())
    {
        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
        {
            if (it->m_pDrawObject == pDrawObject)
            {
                it->m_bIsActive = true;
            }
        }
    }
}

void SwTextBoxNode::SetTextBoxInactive(const SdrObject* pDrawObject)
{
    assert(pDrawObject);

    if (!m_pTextBoxes.empty())
    {
        for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
        {
            if (it->m_pDrawObject == pDrawObject)
            {
                it->m_bIsActive = false;
            }
        }
        SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Text-Box-Vector still not empty!");
        assert(false);
    }
}

@@ -1806,6 +1895,14 @@ void SwTextBoxNode::Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFor
               o_pTarget->FindSdrObject(), bSetAttr, bMakeFrame);

    m_bIsCloningInProgress = false;

    for (auto& rElem : m_pTextBoxes)
    {
        SwTextBoxHelper::changeAnchor(m_pOwnerShapeFormat, rElem.m_pDrawObject);
        SwTextBoxHelper::doTextBoxPositioning(m_pOwnerShapeFormat, rElem.m_pDrawObject);
        SwTextBoxHelper::DoTextBoxZOrderCorrection(m_pOwnerShapeFormat, rElem.m_pDrawObject);
        SwTextBoxHelper::syncTextBoxSize(m_pOwnerShapeFormat, rElem.m_pDrawObject);
    }
}

void SwTextBoxNode::Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
@@ -1821,7 +1918,10 @@ void SwTextBoxNode::Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFra
    if (pSrcList && pDestList)
    {
        if (pSrcList->GetObjCount() != pDestList->GetObjCount())
        {
            SAL_WARN("sw.core", "SwTextBoxNode::Clone_Impl(): Difference between the shapes!");
            return;
        }

        for (size_t i = 0; i < pSrcList->GetObjCount(); ++i)
        {
diff --git a/sw/source/core/edit/edundo.cxx b/sw/source/core/edit/edundo.cxx
index df45c8b..2614e50 100644
--- a/sw/source/core/edit/edundo.cxx
+++ b/sw/source/core/edit/edundo.cxx
@@ -31,6 +31,8 @@
#include <frmfmt.hxx>
#include <docsh.hxx>
#include <pagefrm.hxx>
#include <textboxhelper.hxx>
#include <fmtanchr.hxx>

#include <wrtsh.hxx>

@@ -61,6 +63,13 @@ void SwEditShell::HandleUndoRedoContext(::sw::UndoRedoContext & rContext)
        if (RES_DRAWFRMFMT == pSelFormat->Which())
        {
            SdrObject* pSObj = pSelFormat->FindSdrObject();

            // Before layout calc, inline anchored textboxes have to be synced unless crash.
            if (pSelFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
                && pSelFormat->GetOtherTextBoxFormats() && pSObj)
                SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::changeAnchor,
                                                                 pSelFormat, pSObj);

            static_cast<SwFEShell*>(this)->SelectObj(
                    pSObj->GetCurrentBoundRect().Center() );
        }
diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx
index 2fcc798..99905bd 100644
--- a/sw/source/core/layout/atrfrm.cxx
+++ b/sw/source/core/layout/atrfrm.cxx
@@ -2547,14 +2547,23 @@ SwFrameFormat::~SwFrameFormat()
        }
    }

    // Check if there any textboxes attached to this format.
    if( nullptr == m_pOtherTextBoxFormats )
        return;

    // This is a fly-frame-format just delete this
    // textbox entry from the textbox collection.
    // Note: Do not delete it from the doc, as that
    // is already in progress.
    if (Which() == RES_FLYFRMFMT)
        m_pOtherTextBoxFormats->DelTextBox(this);

    // This is a draw-frame-format what belongs to
    // a shape with textbox(es). Del them all.
    if (Which() == RES_DRAWFRMFMT)
        m_pOtherTextBoxFormats->ClearAll();

    // Release the pointer.
    m_pOtherTextBoxFormats.reset();
}

diff --git a/sw/source/core/text/porfly.cxx b/sw/source/core/text/porfly.cxx
index 8a4b8be..9abef0c 100644
--- a/sw/source/core/text/porfly.cxx
+++ b/sw/source/core/text/porfly.cxx
@@ -364,12 +364,17 @@ void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase,

    if (auto pFormat = FindFrameFormat(pSdrObj))
    {
        if (pFormat->GetOtherTextBoxFormats())
        if (pFormat->GetOtherTextBoxFormats()
            && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
        {
            // TODO: Improve security with moving this sync call to other place,
            // where it works for typing but not during layout calc.
            const bool bModified = pFormat->GetDoc()->getIDocumentState().IsEnableSetModified();
            pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(false);
            SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::changeAnchor, pFormat,
                                                             pFormat->FindRealSdrObject());
            SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::syncTextBoxSize,
                                                             pFormat, pFormat->FindRealSdrObject());
            pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(bModified);
        }
    }
diff --git a/sw/source/core/undo/undobj1.cxx b/sw/source/core/undo/undobj1.cxx
index fb624d6..2b98987 100644
--- a/sw/source/core/undo/undobj1.cxx
+++ b/sw/source/core/undo/undobj1.cxx
@@ -56,8 +56,16 @@ SwUndoFlyBase::~SwUndoFlyBase()
{
    if( m_bDelFormat )       // delete during an Undo?
    {
        if (m_pFrameFormat->GetOtherTextBoxFormats())
        {   // clear that before delete
        if (const auto& pTextBoxes = m_pFrameFormat->GetOtherTextBoxFormats())
        {
            // Clear and unregister before release.
            if (m_pFrameFormat->Which() == RES_FLYFRMFMT)
                pTextBoxes->DelTextBox(m_pFrameFormat);

            if (m_pFrameFormat->Which() == RES_DRAWFRMFMT)
                pTextBoxes->ClearAll();

            // clear that before delete
            m_pFrameFormat->SetOtherTextBoxFormats(nullptr);
        }
        delete m_pFrameFormat;
diff --git a/sw/source/core/unocore/unodraw.cxx b/sw/source/core/unocore/unodraw.cxx
index c2e97c3..8071299 100644
--- a/sw/source/core/unocore/unodraw.cxx
+++ b/sw/source/core/unocore/unodraw.cxx
@@ -1164,19 +1164,22 @@ void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& a
            }
            else if (pEntry->nWID == FN_TEXT_BOX)
            {
                auto pObj = SdrObject::getSdrObjectFromXShape(mxShape);
                if (pEntry->nMemberId == MID_TEXT_BOX)
                {
                    bool bValue(false);
                    aValue >>= bValue;

                    if (bValue)
                        SwTextBoxHelper::create(pFormat, GetSvxShape()->GetSdrObject());
                        SwTextBoxHelper::create(pFormat, pObj);
                    else
                        SwTextBoxHelper::destroy(pFormat, GetSvxShape()->GetSdrObject());
                        SwTextBoxHelper::destroy(pFormat, pObj);
                }
                else if (pEntry->nMemberId == MID_TEXT_BOX_CONTENT)
                {
                    if (aValue.getValueType() == cppu::UnoType<uno::Reference<text::XTextFrame>>::get())
                        SwTextBoxHelper::set(pFormat, GetSvxShape()->GetSdrObject(),
                    if (aValue.getValueType()
                        == cppu::UnoType<uno::Reference<text::XTextFrame>>::get())
                        SwTextBoxHelper::set(pFormat, pObj,
                                             aValue.get<uno::Reference<text::XTextFrame>>());
                    else
                        SAL_WARN( "sw.uno", "This is not a TextFrame!" );
@@ -1356,6 +1359,16 @@ void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& a
                else
                    pFormat->SetFormatAttr(aSet);
            }
            // If this property is an anchor change, and there is a group shape with textboxes
            // do anchor sync in time unless the anchor sync in the porfly will cause crash during
            // layout calculation (When importing an inline shape in docx via dmapper).
            if (pFormat->GetOtherTextBoxFormats()
                && pFormat->GetOtherTextBoxFormats()->GetTextBoxCount() > o3tl::make_unsigned(1)
                && pEntry->nWID == RES_ANCHOR)
                SwTextBoxHelper::synchronizeGroupTextBoxProperty(
                    SwTextBoxHelper::changeAnchor, pFormat,
                    SdrObject::getSdrObjectFromXShape(mxShape));

            // We have a pFormat and a pEntry as well: try to sync TextBox property.
            SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, pEntry->nMemberId, aValue,
                                          SdrObject::getSdrObjectFromXShape(mxShape));
diff --git a/sw/source/core/unocore/unotext.cxx b/sw/source/core/unocore/unotext.cxx
index a837d42..ba5d22f 100644
--- a/sw/source/core/unocore/unotext.cxx
+++ b/sw/source/core/unocore/unotext.cxx
@@ -1759,6 +1759,29 @@ SwXText::convertToTextFrame(
                            aAnchor.SetAnchor(aMovePam.Start());
                            m_pImpl->m_pDoc->SetAttr(aAnchor, *pFrameFormat);
                        }
                        else
                        {
                            // if this frame is a textbox of a shape anchored to us, move this textbox too.
                            const auto& pTextBoxes = pFrameFormat->GetOtherTextBoxFormats();
                            if (pFrameFormat->Which() == RES_FLYFRMFMT && pTextBoxes
                                && pTextBoxes->GetOwnerShape())
                            {
                                const auto& rShapeAnchor = pTextBoxes->GetOwnerShape()->GetAnchor();
                                if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
                                    && rShapeAnchor.GetContentAnchor() && pFrameFormat->GetAnchor().GetContentAnchor()
                                    && pStartPam->ContainsPosition(*pFrameFormat->GetAnchor().GetContentAnchor()))
                                {
                                    const auto& rAnchorNode
                                        = pFrameFormat->GetAnchor().GetContentAnchor()->nNode.GetNode();
                                    if (!(rAnchorNode.FindFooterStartNode() || rAnchorNode.FindHeaderStartNode()))
                                    {
                                        SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
                                        aAnchor.SetAnchor(aMovePam.Start());
                                        m_pImpl->m_pDoc->SetAttr(aAnchor, *pFrameFormat);
                                    }
                                }
                            }
                        }
                    }
                }
            }
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index bbd22d1..9680a76 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -597,8 +597,8 @@ void DomainMapper_Impl::RemoveDummyParaForTableInSection()
}
void DomainMapper_Impl::AddDummyParaForTableInSection()
{
    // Shapes can't have sections.
    if (IsInShape())
    // Shapes and textboxes can't have sections.
    if (IsInShape() || m_bIsInTextBox)
        return;

    if (!m_aTextAppendStack.empty())
@@ -3714,9 +3714,13 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape 
            {
                try
                {
                    uno::Reference<text::XTextRange> xFrame(xShapes->getByIndex(i), uno::UNO_QUERY_THROW);
                    uno::Reference<beans::XPropertySet> xSyncedPropertySet(xFrame, uno::UNO_QUERY_THROW);
                    comphelper::SequenceAsHashMap aGrabBag( xSyncedPropertySet->getPropertyValue("CharInteropGrabBag") );
                    uno::Reference<text::XTextRange> xFrame(xShapes->getByIndex(i), uno::UNO_QUERY);
                    uno::Reference<beans::XPropertySet> xFramePropertySet;
                    if (xFrame)
                        xFramePropertySet.set(xFrame, uno::UNO_QUERY_THROW);
                    uno::Reference<beans::XPropertySet> xShapePropertySet(xShapes->getByIndex(i), uno::UNO_QUERY_THROW);

                    comphelper::SequenceAsHashMap aGrabBag( xShapePropertySet->getPropertyValue("CharInteropGrabBag") );

                    // only VML import has checked for style. Don't apply default parastyle properties to other imported shapes
                    // - except for fontsize - to maintain compatibility with previous versions of LibreOffice.
@@ -3744,7 +3748,7 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape 
                            PROP_CHAR_COLOR,
                            PROP_PARA_ADJUST
                        };
                        const uno::Reference<beans::XPropertyState> xSyncedPropertyState(xSyncedPropertySet, uno::UNO_QUERY_THROW);
                        const uno::Reference<beans::XPropertyState> xShapePropertyState(xShapePropertySet, uno::UNO_QUERY_THROW);
                        for ( const auto& eId : eIds )
                        {
                            try
@@ -3753,11 +3757,16 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape 
                                    continue;

                                const OUString sPropName = getPropertyName(eId);
                                if ( beans::PropertyState_DEFAULT_VALUE == xSyncedPropertyState->getPropertyState(sPropName) )
                                if ( beans::PropertyState_DEFAULT_VALUE == xShapePropertyState->getPropertyState(sPropName) )
                                {
                                    const uno::Any aProp = GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/true, /*bPara=*/true);
                                    if ( aProp.hasValue() )
                                        xSyncedPropertySet->setPropertyValue( sPropName, aProp );
                                    if (aProp.hasValue())
                                    {
                                        if (xFrame)
                                            xFramePropertySet->setPropertyValue(sPropName, aProp);
                                        else
                                            xShapePropertySet->setPropertyValue(sPropName, aProp);
                                    }
                                }
                            }
                            catch (const uno::Exception&)