Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sw/source/core/doc/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 77 kB image not shown  

Quelle  textboxhelper.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */


#include <textboxhelper.hxx>
#include <dcontact.hxx>
#include <fmtcntnt.hxx>
#include <fmtanchr.hxx>
#include <fmtcnct.hxx>
#include <fmtornt.hxx>
#include <fmtfsize.hxx>
#include <doc.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentState.hxx>
#include <docsh.hxx>
#include <unocoll.hxx>
#include <unoframe.hxx>
#include <unodraw.hxx>
#include <unotextrange.hxx>
#include <cmdid.h>
#include <unomid.h>
#include <unoprnms.hxx>
#include <mvsave.hxx>
#include <fmtsrnd.hxx>
#include <fmtfollowtextflow.hxx>
#include <frmfmt.hxx>
#include <frameformats.hxx>
#include <dflyobj.hxx>
#include <swtable.hxx>

#include <editeng/unoprnms.hxx>
#include <editeng/memberids.h>
#include <svx/svdoashp.hxx>
#include <svx/svdpage.hxx>
#include <svl/itemiter.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <sal/log.hxx>
#include <tools/UnitConversion.hxx>
#include <svx/swframetypes.hxx>
#include <drawdoc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <frmatr.hxx>
#include <unotextbodyhf.hxx>

#include <com/sun/star/document/XActionLockable.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/text/SizeType.hpp>
#include <com/sun/star/text/WrapTextMode.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/text/XTextFrame.hpp>
#include <com/sun/star/table/BorderLine2.hpp>
#include <com/sun/star/text/WritingMode.hpp>
#include <com/sun/star/text/WritingMode2.hpp>
#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
#include <com/sun/star/style/ParagraphAdjust.hpp>
#include <unotxdoc.hxx>

using namespace com::sun::star;

void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCopyText)
{
    assert(pShape);
    assert(pObject);
    assert(pShape == ::FindFrameFormat(pObject));

    // If TextBox wasn't enabled previously
    if (pShape->GetOtherTextBoxFormats() && pShape->GetOtherTextBoxFormats()->GetTextBox(pObject))
        return;

    // Store the current text content of the shape
    OUString sCopyableText;

    if (bCopyText)
    {
        if (pObject)
        {
            uno::Reference<text::XText> xSrcCnt(pObject->getWeakUnoShape().get(), uno::UNO_QUERY);
            auto xCur = xSrcCnt->createTextCursor();
            xCur->gotoStart(false);
            xCur->gotoEnd(true);
            sCopyableText = xCur->getText()->getString();
        }
    }

    // Create the associated TextFrame and insert it into the document.
    rtl::Reference<SwXTextFrame> xTextFrame
        = SwXTextFrame::CreateXTextFrame(pShape->GetDoc(), nullptr);

    uno::Reference<text::XTextRange> xAnchor;
    uno::Reference<text::XTextContent> xAnchorProvider(pObject->getWeakUnoShape().get(),
                                                       uno::UNO_QUERY);
    assert(xAnchorProvider.is());
    if (xAnchorProvider.is())
        xAnchor = xAnchorProvider->getAnchor();

    uno::Reference<text::XTextContentAppend> xTextContentAppend;
    if (xAnchor)
        xTextContentAppend.set(xAnchor->getText(), uno::UNO_QUERY);

    if (!xTextContentAppend)
    {
        if (SwDocShell* pShell = pShape->GetDoc().GetDocShell())
        {
            rtl::Reference<SwXTextDocument> xTextDocument(pShell->GetBaseModel());
            xTextContentAppend = xTextDocument->getBodyText();
        }
    }

    if (xAnchor)
    {
        // insertTextContentWithProperties would fail if xAnchor is in a different XText
        assert(xAnchor->getText() == xTextContentAppend);
        xTextContentAppend->insertTextContentWithProperties(
            uno::Reference<text::XTextContent>(cppu::getXWeak(xTextFrame.get()), uno::UNO_QUERY),
            {}, xAnchor);
    }
    else
    {
        xTextContentAppend->appendTextContent(
            uno::Reference<text::XTextContent>(cppu::getXWeak(xTextFrame.get()), uno::UNO_QUERY),
            uno::Sequence<beans::PropertyValue>());
    }

    // Link FLY and DRAW formats, so it becomes a text box (needed for syncProperty calls).
    SwFrameFormat* pFormat = xTextFrame->GetFrameFormat();

    assert(nullptr != dynamic_cast<SwDrawFrameFormat*>(pShape));
    assert(nullptr != dynamic_cast<SwFlyFrameFormat*>(pFormat));

    if (!pShape->GetOtherTextBoxFormats())
    {
        auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShape));
        pTextBox->AddTextBox(pObject, pFormat);
        pShape->SetOtherTextBoxFormats(pTextBox);
        pFormat->SetOtherTextBoxFormats(pTextBox);
    }
    else
    {
        auto& pTextBox = pShape->GetOtherTextBoxFormats();
        pTextBox->AddTextBox(pObject, pFormat);
        pFormat->SetOtherTextBoxFormats(pTextBox);
    }
    // Initialize properties.
    uno::Any aEmptyBorder{ table::BorderLine2() };
    xTextFrame->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
    xTextFrame->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
    xTextFrame->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
    xTextFrame->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);

    xTextFrame->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));

    xTextFrame->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));

    xTextFrame->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));

    assert(!xTextFrame->getName().isEmpty());

    // Link its text range to the original shape.
    SwUnoInternalPaM aInternalPaM(pShape->GetDoc());
    if (sw::XTextRangeToSwPaM(aInternalPaM, xTextFrame))
    {
        SwAttrSet aSet(pShape->GetAttrSet());
        SwFormatContent aContent(aInternalPaM.GetPointNode().StartOfSectionNode());
        aSet.Put(aContent);
        pShape->SetFormatAttr(aSet);
    }

    DoTextBoxZOrderCorrection(pShape, pObject);

    // Also initialize the properties, which are not constant, but inherited from the shape's ones.
    uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
    syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObject);

    uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
    syncProperty(pShape, RES_FOLLOW_TEXT_FLOW, MID_FOLLOW_TEXT_FLOW,
                 xShapePropertySet->getPropertyValue(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), pObject);
    syncProperty(pShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
                 xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObject);
    syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObject);
    syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObject);
    syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObject);
    syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObject);
    syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObject);
    syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObject);
    syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObject);
    // tdf#162075 shape word wrap to frame width type on shape creation.
    bool bTextWordwrap = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
    syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
                 uno::Any(bTextWordwrap ? text::SizeType::FIX : text::SizeType::MIN), pObject);
    syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0,
                 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST), pObject);
    text::WritingMode eMode;
    if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
        syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), 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())
        return;

    if (pObject)
    {
        auto pSourceText = DynCastSdrTextObj(pObject);

        xTextFrame->setString(sCopyableText);

        if (pSourceText)
            pSourceText->SetText(OUString());

        pShape->GetDoc().getIDocumentState().SetModified();
    }
}

void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
                          const uno::Reference<text::XTextFrame>& xNew)
{
    // Do not set invalid data
    assert(pShapeFormat && pObj && xNew);
    // Firstly find the format of the new textbox.
    SwFrameFormat* pFormat = nullptr;
    if (auto pTextFrame = dynamic_cast<SwXTextFrame*>(xNew.get()))
        pFormat = pTextFrame->GetFrameFormat();
    if (!pFormat)
        return;

    // If there is a format, check if the shape already has a textbox assigned to.
    if (auto& pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
    {
        // If it has a texbox, destroy it.
        if (pTextBoxNode->GetTextBox(pObj))
            pTextBoxNode->DelTextBox(pObj, true);
        // And set the new one.
        pTextBoxNode->AddTextBox(pObj, pFormat);
        pFormat->SetOtherTextBoxFormats(pTextBoxNode);
    }
    else
    {
        // If the shape do not have a texbox node and textbox,
        // create that for the shape.
        auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShapeFormat));
        pTextBox->AddTextBox(pObj, pFormat);
        pShapeFormat->SetOtherTextBoxFormats(pTextBox);
        pFormat->SetOtherTextBoxFormats(pTextBox);
    }
    // Initialize its properties
    uno::Reference<beans::XPropertySet> xPropertySet(xNew, uno::UNO_QUERY);
    uno::Any aEmptyBorder{ table::BorderLine2() };
    xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
    xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
    xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
    xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
    xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
    xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
    xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
    // Add a new name to it
    uno::Reference<container::XNamed> xNamed(xNew, uno::UNO_QUERY);
    assert(!xNamed->getName().isEmpty());
    (void)xNamed;
    // And sync. properties.
    uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
    syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObj);
    uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
    syncProperty(pShapeFormat, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
                 xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObj);
    syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObj);
    syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObj);
    syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObj);
    syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObj);
    syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObj);
    syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
                 xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObj);
    syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
                 xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObj);
    drawing::TextVerticalAdjust aVertAdj = drawing::TextVerticalAdjust_CENTER;
    if ((uno::Reference<beans::XPropertyState>(xShape, uno::UNO_QUERY_THROW))
            ->getPropertyState(UNO_NAME_TEXT_VERT_ADJUST)
        != beans::PropertyState::PropertyState_DEFAULT_VALUE)
    {
        aVertAdj = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST)
                       .get<drawing::TextVerticalAdjust>();
    }
    xPropertySet->setPropertyValue(UNO_NAME_TEXT_VERT_ADJUST, uno::Any(aVertAdj));
    text::WritingMode eMode;
    if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
        syncProperty(pShapeFormat, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);

    // Do sync for the new textframe.
    synchronizeGroupTextBoxProperty(&changeAnchor, pShapeFormat, pObj);
    synchronizeGroupTextBoxProperty(&syncTextBoxSize, pShapeFormat, pObj);

    updateTextBoxMargin(pObj);
}

void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject)
{
    // If a TextBox was enabled previously
    auto& pTextBox = pShape->GetOtherTextBoxFormats();
    if (pTextBox)
    {
        // Unlink the TextBox's text range from the original shape.
        // Delete the associated TextFrame.
        pTextBox->DelTextBox(pObject, true);
    }
}

bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
                                const SdrObject* pObject)
{
    DBG_TESTSOLARMUTEX();
    assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT);
    if (!pFormat || pFormat->Which() != nType)
        return false;

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

    if (nType == RES_DRAWFRMFMT)
    {
        if (pObject)
            return pTextBox->GetTextBox(pObject);
        if (auto pObj = pFormat->FindRealSdrObject())
            return pTextBox->GetTextBox(pObj);
    }

    if (nType == RES_FLYFRMFMT)
    {
        return pTextBox->GetOwnerShape();
    }

    return false;
}

bool SwTextBoxHelper::hasTextFrame(const SdrObject* pObj)
{
    if (!pObj)
        return false;

    uno::Reference<drawing::XShape> xShape(pObj->getWeakUnoShape().get(), uno::UNO_QUERY);
    if (!xShape)
        return false;
    return SwTextBoxHelper::getOtherTextBoxFormat(xShape);
}

sal_Int32 SwTextBoxHelper::getCount(SdrPage const* pPage)
{
    sal_Int32 nRet = 0;
    for (const rtl::Reference<SdrObject>& p : *pPage)
    {
        assert(p);
        if (p->IsTextBox())
            continue;
        ++nRet;
    }
    return nRet;
}

sal_Int32 SwTextBoxHelper::getCount(const SwDoc& rDoc)
{
    sal_Int32 nRet = 0;
    for (const sw::SpzFrameFormat* pFormat : *rDoc.GetSpzFrameFormats())
    {
        if (isTextBox(pFormat, RES_FLYFRMFMT))
            ++nRet;
    }
    return nRet;
}

uno::Any SwTextBoxHelper::getByIndex(SdrPage const* pPage, sal_Int32 nIndex)
{
    if (nIndex < 0)
        throw lang::IndexOutOfBoundsException();

    sal_Int32 nCount = 0; // Current logical index.
    for (const rtl::Reference<SdrObject>& p : *pPage)
    {
        assert(p);
        if (p->IsTextBox())
            continue;
        if (nCount == nIndex)
            return uno::Any(p->getUnoShape());
        ++nCount;
    }

    throw lang::IndexOutOfBoundsException();
}

sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* pObject)
{
    if (const SdrPage* pPage = pObject->getSdrPageFromSdrObject())
    {
        sal_Int32 nOrder = 0; // Current logical order.
        for (const rtl::Reference<SdrObject>& p : *pPage)
        {
            assert(p);
            if (p->IsTextBox())
                continue;
            if (p == pObject)
                return nOrder;
            ++nOrder;
        }
    }

    SAL_WARN("sw.core""SwTextBoxHelper::getOrdNum: no page or page doesn't contain the object");
    return pObject->GetOrdNum();
}

void SwTextBoxHelper::getShapeWrapThrough(const SwFrameFormat* pTextBox, bool& rWrapThrough)
{
    SwFrameFormat* pShape = SwTextBoxHelper::getOtherTextBoxFormat(pTextBox, RES_FLYFRMFMT);
    if (pShape)
        rWrapThrough = pShape->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH;
}

SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(const SwFrameFormat* pFormat,
                                                      sal_uInt16 nType, const SdrObject* pObject)
{
    SolarMutexGuard aGuard;
    if (!isTextBox(pFormat, nType, pObject))
        return nullptr;

    if (nType == RES_DRAWFRMFMT)
    {
        if (pObject)
            return pFormat->GetOtherTextBoxFormats()->GetTextBox(pObject);
        if (pFormat->FindRealSdrObject())
            return pFormat->GetOtherTextBoxFormats()->GetTextBox(pFormat->FindRealSdrObject());
        return nullptr;
    }
    if (nType == RES_FLYFRMFMT)
    {
        return pFormat->GetOtherTextBoxFormats()->GetOwnerShape();
    }
    return nullptr;
}

SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XShape> const& xShape)
{
    auto pShape = dynamic_cast<SwXShape*>(xShape.get());
    if (!pShape)
        return nullptr;

    SwFrameFormat* pFormat = pShape->GetFrameFormat();
    return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT,
                                 SdrObject::getSdrObjectFromXShape(xShape));
}

uno::Reference<text::XTextFrame>
SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape)
{
    if (xShape)
    {
        auto pFrameFormat = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
        if (pFrameFormat)
        {
            auto pSdrObj = pFrameFormat->FindSdrObject();
            if (pSdrObj)
            {
                return { pSdrObj->getUnoShape(), uno::UNO_QUERY };
            }
        }
    }
    return {};
}

template <typename T>
static void lcl_queryInterface(const SwFrameFormat* pShape, uno::Any& rAny, SdrObject* pObj)
{
    if (SwFrameFormat* pFormat
        = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
    {
        uno::Reference<T> const xInterface(
            getXWeak(SwXTextFrame::CreateXTextFrame(pFormat->GetDoc(), pFormat).get()),
            uno::UNO_QUERY);
        rAny <<= xInterface;
    }
}

uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const uno::Type&&nbsp;rType,
                                         SdrObject* pObj)
{
    uno::Any aRet;

    if (rType == cppu::UnoType<css::text::XTextAppend>::get())
    {
        lcl_queryInterface<text::XTextAppend>(pShape, aRet, pObj);
    }
    else if (rType == cppu::UnoType<css::text::XText>::get())
    {
        lcl_queryInterface<text::XText>(pShape, aRet, pObj);
    }
    else if (rType == cppu::UnoType<css::text::XTextRange>::get())
    {
        lcl_queryInterface<text::XTextRange>(pShape, aRet, pObj);
    }

    return aRet;
}

tools::Rectangle SwTextBoxHelper::getRelativeTextRectangle(SdrObject* pShape)
{
    tools::Rectangle aRet;
    aRet.SetEmpty();

    assert(pShape);

    auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pShape);
    if (pCustomShape)
    {
        // Need to temporarily release the lock acquired in
        // SdXMLShapeContext::AddShape(), otherwise we get an empty rectangle,
        // see EnhancedCustomShapeEngine::getTextBounds().
        uno::Reference<document::XActionLockable> xLockable(pCustomShape->getUnoShape(),
                                                            uno::UNO_QUERY);
        sal_Int16 nLocks = 0;
        if (xLockable.is())
            nLocks = xLockable->resetActionLocks();
        pCustomShape->GetTextBounds(aRet);
        if (nLocks)
            xLockable->setActionLocks(nLocks);
    }
    else if (pShape)
    {
        // fallback - get *any* bound rect we can possibly get hold of
        aRet = pShape->GetCurrentBoundRect();
    }

    if (pShape)
    {
        // Relative, so count the logic (reference) rectangle, see the EnhancedCustomShape2d ctor.
        Point aPoint(pShape->GetSnapRect().Center());
        Size aSize(pShape->GetLogicRect().GetSize());
        aPoint.AdjustX(-(aSize.Width() / 2));
        aPoint.AdjustY(-(aSize.Height() / 2));
        tools::Rectangle aLogicRect(aPoint, aSize);
        aRet.Move(-1 * aLogicRect.Left(), -1 * aLogicRect.Top());
    }

    return aRet;
}

void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rPropertyName,
                                   const css::uno::Any& rValue, SdrObject* pObj)
{
    // Textframes does not have valid horizontal adjust property, so map it to paragraph adjust property
    if (rPropertyName == UNO_NAME_TEXT_HORZADJUST)
    {
        SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
        if (!pFormat)
            return;

        auto xTextFrame = SwXTextFrame::CreateXTextFrame(pFormat->GetDoc(), pFormat);
        uno::Reference<text::XTextCursor> xCursor = xTextFrame->getText()->createTextCursor();

        // Select all paragraphs in the textframe
        xCursor->gotoStart(false);
        xCursor->gotoEnd(true);
        uno::Reference<beans::XPropertySet> xFrameParaProps(xCursor, uno::UNO_QUERY);

        // And simply map the property
        const auto eValue = rValue.get<drawing::TextHorizontalAdjust>();
        switch (eValue)
        {
            case drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER:
                xFrameParaProps->setPropertyValue(
                    UNO_NAME_PARA_ADJUST,
                    uno::Any(style::ParagraphAdjust::ParagraphAdjust_CENTER)); //3
                break;
            case drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT:
                xFrameParaProps->setPropertyValue(
                    UNO_NAME_PARA_ADJUST,
                    uno::Any(style::ParagraphAdjust::ParagraphAdjust_LEFT)); //0
                break;
            case drawing::TextHorizontalAdjust::TextHorizontalAdjust_RIGHT:
                xFrameParaProps->setPropertyValue(
                    UNO_NAME_PARA_ADJUST,
                    uno::Any(style::ParagraphAdjust::ParagraphAdjust_RIGHT)); //1
                break;
            default:
                SAL_WARN("sw.core",
                         "SwTextBoxHelper::syncProperty: unhandled TextHorizontalAdjust: "
                             << static_cast<sal_Int32>(eValue));
                break;
        }
        return;
    }

    if (rPropertyName == u"CustomShapeGeometry")
    {
        // CustomShapeGeometry changes the textbox position offset and size, so adjust both.
        syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any());

        SdrObject* pObject = pObj ? pObj : pShape->FindRealSdrObject();
        if (pObject)
        {
            tools::Rectangle aRectangle(pObject->GetSnapRect());
            syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
                         uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Left()))));
            syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
                         uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Top()))));
        }

        SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
        if (!pFormat)
            return;

        // Older documents or documents in ODF strict do not have WritingMode, but have used the
        // TextRotateAngle values -90 and -270 to emulate these text directions of frames.
        // ToDo: Is TextPreRotateAngle needed for diagrams or can it be removed?
        comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue);
        auto it = aCustomShapeGeometry.find(u"TextPreRotateAngle"_ustr);
        if (it == aCustomShapeGeometry.end())
        {
            it = aCustomShapeGeometry.find(u"TextRotateAngle"_ustr);
        }

        if (it != aCustomShapeGeometry.end())
        {
            auto nAngle = it->second.has<sal_Int32>() ? it->second.get<sal_Int32>() : 0;
            if (nAngle == 0)
            {
                nAngle = it->second.has<double>() ? it->second.get<double>() : 0;
            }

            sal_Int16 nDirection = 0;
            switch (nAngle)
            {
                case -90:
                    nDirection = text::WritingMode2::TB_RL90;
                    break;
                case -270:
                    nDirection = text::WritingMode2::BT_LR;
                    break;
                default:
                    SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled property value: "
                                        "CustomShapeGeometry:TextPreRotateAngle: "
                                            << nAngle);
                    break;
            }

            if (nDirection)
            {
                syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(nDirection), pObj);
            }
        }
    }
    else if (rPropertyName == UNO_NAME_TEXT_VERT_ADJUST)
        syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, rValue, pObj);
    else if (rPropertyName == UNO_NAME_TEXT_AUTOGROWHEIGHT)
        syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, rValue, pObj);
    else if (rPropertyName == UNO_NAME_TEXT_LEFTDIST)
        syncProperty(pShape, RES_BOX, LEFT_BORDER_DISTANCE, rValue, pObj);
    else if (rPropertyName == UNO_NAME_TEXT_RIGHTDIST)
        syncProperty(pShape, RES_BOX, RIGHT_BORDER_DISTANCE, rValue, pObj);
    else if (rPropertyName == UNO_NAME_TEXT_UPPERDIST)
        syncProperty(pShape, RES_BOX, TOP_BORDER_DISTANCE, rValue, pObj);
    else if (rPropertyName == UNO_NAME_TEXT_LOWERDIST)
        syncProperty(pShape, RES_BOX, BOTTOM_BORDER_DISTANCE, rValue, pObj);
    else if (rPropertyName == UNO_NAME_TEXT_WRITINGMODE)
    {
        text::WritingMode eMode;
        sal_Int16 eMode2;
        if (rValue >>= eMode)
            syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
        else if (rValue >>= eMode2)
            syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
    }
    else if (rPropertyName == u"WritingMode")
    {
        sal_Int16 eMode2;
        if (rValue >>= eMode2)
            syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
    }
    else if (rPropertyName == u"TextWordWrap")
    {
        // tdf#81567 shape word wrap to frame width type on shape update.
        bool bTextWordwrap{};
        if (rValue >>= bTextWordwrap)
        {
            syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
                         uno::Any(bTextWordwrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);
        }
    }
    else
        SAL_INFO("sw.core""SwTextBoxHelper::syncProperty: unhandled property: "
                                << static_cast<OUString>(rPropertyName));
}

void SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
                                  css::uno::Any& rValue)
{
    if (!pShape)
        return;

    nMemberID &= ~CONVERT_TWIPS;

    SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
    if (!pFormat)
        return;

    if (nWID != RES_CHAIN)
        return;

    switch (nMemberID)
    {
        case MID_CHAIN_PREVNAME:
        case MID_CHAIN_NEXTNAME:
        {
            const SwFormatChain& rChain = pFormat->GetChain();
            rChain.QueryValue(rValue, nMemberID);
        }
        break;
        case MID_CHAIN_NAME:
            rValue <<= pFormat->GetName().toString();
            break;
        default:
            SAL_WARN("sw.core""SwTextBoxHelper::getProperty: unhandled member-id: "
                                    << o3tl::narrowing<sal_uInt16>(nMemberID));
            break;
    }
}

css::uno::Any SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, const OUString& rPropName)
{
    if (!pShape)
        return {};

    SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
    if (!pFormat)
        return {};

    rtl::Reference<SwXTextFrame> xPropertySet
        = SwXTextFrame::CreateXTextFrame(pFormat->GetDoc(), pFormat);

    return xPropertySet->getPropertyValue(rPropName);
}

void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
                                   const css::uno::Any& rValue, SdrObject* pObj)
{
    // No shape yet? Then nothing to do, initial properties are set by create().
    if (!pShape)
        return;

    uno::Any aValue(rValue);
    nMemberID &= ~CONVERT_TWIPS;

    SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
    if (!pFormat)
        return;

    OUString aPropertyName;
    bool bAdjustX = false;
    bool bAdjustY = false;
    bool bAdjustSize = false;
    switch (nWID)
    {
        case RES_HORI_ORIENT:
            switch (nMemberID)
            {
                case MID_HORIORIENT_ORIENT:
                    aPropertyName = UNO_NAME_HORI_ORIENT;
                    break;
                case MID_HORIORIENT_RELATION:
                    if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
                        aPropertyName = UNO_NAME_HORI_ORIENT_RELATION;
                    else
                        return;
                    break;
                case MID_HORIORIENT_POSITION:
                    aPropertyName = UNO_NAME_HORI_ORIENT_POSITION;
                    bAdjustX = true;
                    break;
                default:
                    SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled member-id: "
                                            << o3tl::narrowing<sal_uInt16>(nMemberID)
                                            << " (which-id: " << nWID << ")");
                    break;
            }
            break;
        case RES_LR_SPACE:
        {
            switch (nMemberID)
            {
                case MID_L_MARGIN:
                    aPropertyName = UNO_NAME_LEFT_MARGIN;
                    break;
                case MID_R_MARGIN:
                    aPropertyName = UNO_NAME_RIGHT_MARGIN;
                    break;
                default:
                    SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled member-id: "
                                            << o3tl::narrowing<sal_uInt16>(nMemberID)
                                            << " (which-id: " << nWID << ")");
                    break;
            }
            break;
        }
        case RES_VERT_ORIENT:
            switch (nMemberID)
            {
                case MID_VERTORIENT_ORIENT:
                    aPropertyName = UNO_NAME_VERT_ORIENT;
                    break;
                case MID_VERTORIENT_RELATION:
                    if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
                        aPropertyName = UNO_NAME_VERT_ORIENT_RELATION;
                    else
                        return;
                    break;
                case MID_VERTORIENT_POSITION:
                    aPropertyName = UNO_NAME_VERT_ORIENT_POSITION;
                    bAdjustY = true;
                    break;
                default:
                    SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled member-id: "
                                            << o3tl::narrowing<sal_uInt16>(nMemberID)
                                            << " (which-id: " << nWID << ")");
                    break;
            }
            break;
        case RES_FRM_SIZE:
            switch (nMemberID)
            {
                case MID_FRMSIZE_WIDTH_TYPE:
                    aPropertyName = UNO_NAME_WIDTH_TYPE;
                    break;
                case MID_FRMSIZE_IS_AUTO_HEIGHT:
                    aPropertyName = UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT;
                    break;
                case MID_FRMSIZE_REL_HEIGHT_RELATION:
                    aPropertyName = UNO_NAME_RELATIVE_HEIGHT_RELATION;
                    break;
                case MID_FRMSIZE_REL_WIDTH_RELATION:
                    aPropertyName = UNO_NAME_RELATIVE_WIDTH_RELATION;
                    break;
                default:
                    aPropertyName = UNO_NAME_SIZE;
                    bAdjustSize = true;
                    break;
            }
            break;
        case RES_ANCHOR:
            switch (nMemberID)
            {
                case MID_ANCHOR_ANCHORTYPE:
                {
                    changeAnchor(pShape, pObj);
                    return;
                }
                break;
                default:
                    SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled member-id: "
                                            << o3tl::narrowing<sal_uInt16>(nMemberID)
                                            << " (which-id: " << nWID << ")");
                    break;
            }
            break;
        case FN_TEXT_RANGE:
        {
            uno::Reference<text::XTextRange> xRange;
            rValue >>= xRange;
            SwUnoInternalPaM aInternalPaM(pFormat->GetDoc());
            if (sw::XTextRangeToSwPaM(aInternalPaM, xRange))
            {
                SwFormatAnchor aAnchor(pFormat->GetAnchor());
                aAnchor.SetAnchor(aInternalPaM.Start());
                pFormat->SetFormatAttr(aAnchor);
            }
        }
        break;
        case RES_CHAIN:
            switch (nMemberID)
            {
                case MID_CHAIN_PREVNAME:
                    aPropertyName = UNO_NAME_CHAIN_PREV_NAME;
                    break;
                case MID_CHAIN_NEXTNAME:
                    aPropertyName = UNO_NAME_CHAIN_NEXT_NAME;
                    break;
                default:
                    SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled member-id: "
                                            << o3tl::narrowing<sal_uInt16>(nMemberID)
                                            << " (which-id: " << nWID << ")");
                    break;
            }
            break;
        case RES_TEXT_VERT_ADJUST:
            aPropertyName = UNO_NAME_TEXT_VERT_ADJUST;
            break;
        case RES_BOX:
            switch (nMemberID)
            {
                case LEFT_BORDER_DISTANCE:
                    aPropertyName = UNO_NAME_LEFT_BORDER_DISTANCE;
                    break;
                case RIGHT_BORDER_DISTANCE:
                    aPropertyName = UNO_NAME_RIGHT_BORDER_DISTANCE;
                    break;
                case TOP_BORDER_DISTANCE:
                    aPropertyName = UNO_NAME_TOP_BORDER_DISTANCE;
                    break;
                case BOTTOM_BORDER_DISTANCE:
                    aPropertyName = UNO_NAME_BOTTOM_BORDER_DISTANCE;
                    break;
                default:
                    SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled member-id: "
                                            << o3tl::narrowing<sal_uInt16>(nMemberID)
                                            << " (which-id: " << nWID << ")");
                    break;
            }
            break;
        case RES_OPAQUE:
            aPropertyName = UNO_NAME_OPAQUE;
            break;
        case RES_FRAMEDIR:
            aPropertyName = UNO_NAME_WRITING_MODE;
            break;
        case RES_WRAP_INFLUENCE_ON_OBJPOS:
            switch (nMemberID)
            {
                case MID_ALLOW_OVERLAP:
                    aPropertyName = UNO_NAME_ALLOW_OVERLAP;
                    break;
                default:
                    SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled member-id: "
                                            << o3tl::narrowing<sal_uInt16>(nMemberID)
                                            << " (which-id: " << nWID << ")");
                    break;
            }
            break;
        default:
            SAL_WARN("sw.core""SwTextBoxHelper::syncProperty: unhandled which-id: "
                                    << nWID << " (member-id: "
                                    << o3tl::narrowing<sal_uInt16>(nMemberID) << ")");
            break;
    }

    if (aPropertyName.isEmpty())
        return;

    // Position/size should be the text position/size, not the shape one as-is.
    if (bAdjustX || bAdjustY || bAdjustSize)
    {
        changeAnchor(pShape, pObj);
        tools::Rectangle aRect
            = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
        if (!aRect.IsEmpty())
        {
            if (bAdjustX || bAdjustY)
            {
                sal_Int32 nValue;
                if (aValue >>= nValue)
                {
                    nValue += convertTwipToMm100(bAdjustX ? aRect.Left() : aRect.Top());
                    aValue <<= nValue;
                }
            }
            else if (bAdjustSize)
            {
                awt::Size aSize(convertTwipToMm100(aRect.getOpenWidth()),
                                convertTwipToMm100(aRect.getOpenHeight()));
                aValue <<= aSize;
            }
        }
    }
    auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
    rtl::Reference<SwXTextFrame> const xPropertySet
        = SwXTextFrame::CreateXTextFrame(pFormat->GetDoc(), pFormat);
    xPropertySet->setPropertyValue(aPropertyName, aValue);
}

void SwTextBoxHelper::saveLinks(const sw::FrameFormats<sw::SpzFrameFormat*>& rFormats,
                                std::map<const SwFrameFormat*, const SwFrameFormat*>& rLinks)
{
    for (const auto pFormat : rFormats)
    {
        if (SwFrameFormat* pTextBox = getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
            rLinks[pFormat] = pTextBox;
    }
}

void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrameFormat*>& rNew,
                                   SavedLink& rSavedLinks)
{
    std::size_t i = 0;
    for (const auto& rIt : rOld)
    {
        auto aTextBoxIt = rSavedLinks.find(rIt.GetFormat());
        if (aTextBoxIt != rSavedLinks.end())
        {
            std::size_t j = 0;
            for (const auto& rJt : rOld)
            {
                if (rJt.GetFormat() == aTextBoxIt->second)
                    rNew[i]->SetFormatAttr(rNew[j]->GetContent());
                ++j;
            }
        }
        ++i;
    }
}

text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& rAnchorID)
{
    text::TextContentAnchorType aAnchorType;
    switch (rAnchorID)
    {
        case RndStdIds::FLY_AS_CHAR:
            aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER;
            break;
        case RndStdIds::FLY_AT_CHAR:
            aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER;
            break;
        case RndStdIds::FLY_AT_PARA:
            aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
            break;
        case RndStdIds::FLY_AT_PAGE:
            aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PAGE;
            break;
        case RndStdIds::FLY_AT_FLY:
            aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_FRAME;
            break;
        default:
            aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
            SAL_WARN("sw.core""SwTextBoxHelper::mapAnchorType: Unknown AnchorType!");
            break;
    }
    return aAnchorType;
}

void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const&&nbsp;rSet,
                                       SdrObject* pObj)
{
    SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT, pObj);
    if (!pFormat)
        return;

    const bool bInlineAnchored = rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
    const bool bLayoutInCell = rShape.GetFollowTextFlow().GetValue()
                               && rShape.GetAnchor().GetAnchorNode()
                               && rShape.GetAnchor().GetAnchorNode()->FindTableNode();
    SfxItemSet aTextBoxSet(pFormat->GetDoc().GetAttrPool(), aFrameFormatSetRange);

    SfxItemIter aIter(rSet);
    const SfxPoolItem* pItem = aIter.GetCurItem();

    do
    {
        switch (pItem->Which())
        {
            case RES_VERT_ORIENT:
            {
                // The new position can be with anchor changing so sync it!
                const text::TextContentAnchorType aNewAnchorType
                    = mapAnchorType(rShape.GetAnchor().GetAnchorId());
                syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
                             pObj);
                if (bInlineAnchored || bLayoutInCell)
                    return;
                SwFormatVertOrient aOrient(pItem->StaticWhichCast(RES_VERT_ORIENT));

                tools::Rectangle aRect
                    = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
                if (!aRect.IsEmpty())
                    aOrient.SetPos(aOrient.GetPos() + aRect.Top());

                if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
                    && rShape.GetAnchor().GetPageNum() != 0)
                    aOrient.SetRelationOrient(rShape.GetVertOrient().GetRelationOrient());
                aTextBoxSet.Put(aOrient);

                // restore height (shrunk for extending beyond the page bottom - tdf#91260)
                SwFormatFrameSize aSize(pFormat->GetFrameSize());
                if (!aRect.IsEmpty())
                {
                    aSize.SetHeight(aRect.getOpenHeight());
                    aTextBoxSet.Put(aSize);
                }
            }
            break;
            case RES_HORI_ORIENT:
            {
                // The new position can be with anchor changing so sync it!
                const text::TextContentAnchorType aNewAnchorType
                    = mapAnchorType(rShape.GetAnchor().GetAnchorId());
                syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
                             pObj);
                if (bInlineAnchored || bLayoutInCell)
                    return;
                SwFormatHoriOrient aOrient(pItem->StaticWhichCast(RES_HORI_ORIENT));

                tools::Rectangle aRect
                    = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
                if (!aRect.IsEmpty())
                    aOrient.SetPos(aOrient.GetPos() + aRect.Left());

                if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
                    && rShape.GetAnchor().GetPageNum() != 0)
                    aOrient.SetRelationOrient(rShape.GetHoriOrient().GetRelationOrient());
                aTextBoxSet.Put(aOrient);
            }
            break;
            case RES_FRM_SIZE:
            {
                // In case the shape got resized, then we need to adjust both
                // the position and the size of the textbox (e.g. larger
                // rounded edges of a rectangle -> need to push right/down the
                // textbox).
                SwFormatVertOrient aVertOrient(rShape.GetVertOrient());
                SwFormatHoriOrient aHoriOrient(rShape.GetHoriOrient());
                SwFormatFrameSize aSize(pFormat->GetFrameSize());

                tools::Rectangle aRect
                    = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
                if (!aRect.IsEmpty())
                {
                    if (!bInlineAnchored)
                    {
                        aVertOrient.SetPos(
                            (pObj ? pObj->GetRelativePos().getX() : aVertOrient.GetPos())
                            + aRect.Top());
                        aHoriOrient.SetPos(
                            (pObj ? pObj->GetRelativePos().getY() : aHoriOrient.GetPos())
                            + aRect.Left());

                        aTextBoxSet.Put(aVertOrient);
                        aTextBoxSet.Put(aHoriOrient);
                    }

                    aSize.SetWidth(aRect.getOpenWidth());
                    aSize.SetHeight(aRect.getOpenHeight());
                    aTextBoxSet.Put(aSize);
                }
            }
            break;
            case RES_ANCHOR:
            {
                if (pItem->StaticWhichCast(RES_ANCHOR) == rShape.GetAnchor())
                // the anchor have to be synced
                {
                    const text::TextContentAnchorType aNewAnchorType
                        = mapAnchorType(rShape.GetAnchor().GetAnchorId());
                    syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
                                 uno::Any(aNewAnchorType), pObj);
                }
                else
                {
                    SAL_WARN("sw.core""SwTextBoxHelper::syncFlyFrameAttr: The anchor of the "
                                        "shape different from the textframe!");
                }
            }
            break;
            default:
                SAL_WARN("sw.core""SwTextBoxHelper::syncFlyFrameAttr: unhandled which-id: "
                                        << pItem->Which());
                break;
        }

        pItem = aIter.NextItem();
    } while (pItem && (0 != pItem->Which()));

    if (aTextBoxSet.Count())
    {
        auto aGuard = SwTextBoxLockGuard(*rShape.GetOtherTextBoxFormats());
        pFormat->SetFormatAttr(aTextBoxSet);
    }
    DoTextBoxZOrderCorrection(&rShape, pObj);
}

void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj)
{
    if (!pObj)
        return;
    uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
    if (!xShape)
        return;
    uno::Reference<beans::XPropertySet> const xPropertySet(xShape, uno::UNO_QUERY);

    auto pParentFormat = getOtherTextBoxFormat(getOtherTextBoxFormat(xShape), RES_FLYFRMFMT);
    if (!pParentFormat)
        return;

    // Sync the padding
    syncProperty(pParentFormat, UNO_NAME_TEXT_LEFTDIST,
                 xPropertySet->getPropertyValue(UNO_NAME_TEXT_LEFTDIST), pObj);
    syncProperty(pParentFormat, UNO_NAME_TEXT_RIGHTDIST,
                 xPropertySet->getPropertyValue(UNO_NAME_TEXT_RIGHTDIST), pObj);
    syncProperty(pParentFormat, UNO_NAME_TEXT_UPPERDIST,
                 xPropertySet->getPropertyValue(UNO_NAME_TEXT_UPPERDIST), pObj);
    syncProperty(pParentFormat, UNO_NAME_TEXT_LOWERDIST,
                 xPropertySet->getPropertyValue(UNO_NAME_TEXT_LOWERDIST), pObj);

    // Sync the text aligning
    syncProperty(pParentFormat, UNO_NAME_TEXT_VERTADJUST,
                 xPropertySet->getPropertyValue(UNO_NAME_TEXT_VERTADJUST), pObj);
    syncProperty(pParentFormat, UNO_NAME_TEXT_HORZADJUST,
                 xPropertySet->getPropertyValue(UNO_NAME_TEXT_HORZADJUST), pObj);

    // tdf137803: Sync autogrow:
    const bool bIsAutoGrow
        = xPropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT).get<bool>();
    const bool bIsAutoWrap = xPropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();

    syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, uno::Any(bIsAutoGrow),
                 pObj);

    syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
                 uno::Any(bIsAutoWrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);

    changeAnchor(pParentFormat, pObj);
    DoTextBoxZOrderCorrection(pParentFormat, pObj);
}

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();

        const auto pOldCnt = rOldAnch.GetContentAnchor();
        const auto pNewCnt = rNewAnch.GetContentAnchor();

        const uno::Any aShapeHorRelOrient(pShape->GetHoriOrient().GetRelationOrient());

        try
        {
            auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
            ::sw::UndoGuard const UndoGuard(pShape->GetDoc().GetIDocumentUndoRedo());
            rtl::Reference<SwXTextFrame> const xPropertySet
                = SwXTextFrame::CreateXTextFrame(pFormat->GetDoc(), pFormat);
            if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
                && rNewAnch.GetPageNum())
            {
                uno::Any aValue(text::TextContentAnchorType_AT_PAGE);
                xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient);
                xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
                xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
                                               uno::Any(rNewAnch.GetPageNum()));
            }
            else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt)
            {
                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,
                                                   uno::Any(text::RelOrientation::CHAR));
                    xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
                                                   uno::Any(text::RelOrientation::PRINT_AREA));
                    SwFormatAnchor aPos(pFormat->GetAnchor());
                    aPos.SetAnchor(pNewCnt);
                    pFormat->SetFormatAttr(aPos);
                }
                else
                {
                    uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
                    xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
                                                   aShapeHorRelOrient);
                    xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
                    pFormat->SetFormatAttr(rNewAnch);
                }
            }
            else
            {
                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,
                                                   uno::Any(text::RelOrientation::CHAR));
                    xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
                                                   uno::Any(text::RelOrientation::PRINT_AREA));
                    SwFormatAnchor aPos(pFormat->GetAnchor());
                    aPos.SetAnchor(pNewCnt);
                    pFormat->SetFormatAttr(aPos);
                }
                else
                {
                    xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
                                                   aShapeHorRelOrient);
                    if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
                        && rNewAnch.GetPageNum() == 0)
                    {
                        pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
                    }
                    else
                        pFormat->SetFormatAttr(pShape->GetAnchor());
                }
            }
        }
        catch (uno::Exception& e)
        {
            SAL_WARN("sw.core""SwTextBoxHelper::changeAnchor(): " << e.Message);
        }

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

    return false;
}

bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj)
{
    // Set the position of the textboxes according to the position of its shape-pair
    const bool bIsGroupObj = (pObj != pShape->FindRealSdrObject()) && pObj;
    if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
    {
        // 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)
        {
            // Get the text area of the shape
            tools::Rectangle aRect
                = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());

            // Set the textbox position at the X-axis:
            SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
            if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
                aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);

            // tdf#152142: For RTL, positioning is relative to the right
            if (pShape->GetLayoutDir() == SwFrameFormat::HORI_R2L)
            {
                auto nRightSpace = pShape->GetLRSpace().ResolveRight({});

                const bool bMSOLayout = pFormat->getIDocumentSettingAccess().get(
                    DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS);
                if (bMSOLayout)
                {
                    aNewHOri.SetPos(-aRect.Right() + nRightSpace
                                    + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
                }
                else
                {
                    aNewHOri.SetPos(aRect.Right() + nRightSpace
                                    + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
                }
            }
            else
            {
                auto nLeftSpace = pShape->GetLRSpace().ResolveLeft({});
                aNewHOri.SetPos(aRect.Left() + nLeftSpace
                                + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
            }

            SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());

            // Special handling of group textboxes
            if (bIsGroupObj)
            {
                // There are the following cases:
                // case 1: The textbox should be in that position where the shape is.
                // case 2: The shape has negative offset so that have to be subtracted
                // case 3: The shape and its parent shape also has negative offset, so subtract
                aNewVOri.SetPos(
                    ((pObj->GetRelativePos().getY()) > 0
                         ? (pShape->GetVertOrient().GetPos() > 0
                                ? pObj->GetRelativePos().getY()
                                : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos())
                         : (pShape->GetVertOrient().GetPos() > 0
                                ? 0 // Is this can be a variation?
                                : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos()))
                    + aRect.Top());
            }
            else
            {
                // Simple textboxes: vertical position equals to the vertical offset of the shape
                aNewVOri.SetPos(
                    ((pShape->GetVertOrient().GetPos()) > 0 ? pShape->GetVertOrient().GetPos() : 0)
                    + aRect.Top());
            }

            // Special cases when the shape is aligned to the line
            if (pShape->GetVertOrient().GetVertOrient() != text::VertOrientation::NONE)
            {
                aNewVOri.SetVertOrient(text::VertOrientation::NONE);
                switch (pShape->GetVertOrient().GetVertOrient())
                {
                    // Top aligned shape
                    case text::VertOrientation::TOP:
                    case text::VertOrientation::CHAR_TOP:
                    case text::VertOrientation::LINE_TOP:
                    {
                        aNewVOri.SetPos(aNewVOri.GetPos() - pShape->GetFrameSize().GetHeight());
                        break;
                    }
                    // Bottom aligned shape
                    case text::VertOrientation::BOTTOM:
                    case text::VertOrientation::CHAR_BOTTOM:
                    case text::VertOrientation::LINE_BOTTOM:
                    {
                        aNewVOri.SetPos(aNewVOri.GetPos() + pShape->GetFrameSize().GetHeight());
                        break;
                    }
                    // Center aligned shape
                    case text::VertOrientation::CENTER:
                    case text::VertOrientation::CHAR_CENTER:
                    case text::VertOrientation::LINE_CENTER:
                    {
                        aNewVOri.SetPos(aNewVOri.GetPos()
                                        + std::lroundf(pShape->GetFrameSize().GetHeight() / 2));
                        break;
                    }
                    default:
                        break;
                }
            }

            pFormat->SetFormatAttr(aNewHOri);
            pFormat->SetFormatAttr(aNewVOri);
        }
        // Other cases when the shape has different anchor from AS_CHAR
        else
        {
            // Text area of the shape
            tools::Rectangle aRect
                = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());

            // Set the same position as the (child) shape has
            SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
            if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
                aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);

            aNewHOri.SetPos(
                (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
                + aRect.Left());
            SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
            aNewVOri.SetPos(
                (bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos())
                + aRect.Top());

            // Get the distance of the child shape inside its parent
            const auto nInshapePos
                = pObj ? pObj->GetRelativePos() - pShape->FindRealSdrObject()->GetRelativePos()
                       : Point();

            // Special case: the shape has relative position from the page
            if (pShape->GetHoriOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
                && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
            {
                aNewHOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
                aNewHOri.SetPos(pShape->GetHoriOrient().GetPos() + nInshapePos.getX()
                                + aRect.Left());
            }

            if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
                && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
            {
                aNewVOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
                aNewVOri.SetPos(pShape->GetVertOrient().GetPos() + nInshapePos.getY()
                                + aRect.Top());
            }

            // Other special case: shape is inside a table or floating table following the text flow
            if (pShape->GetFollowTextFlow().GetValue() && pShape->GetAnchor().GetAnchorNode()
                && pShape->GetAnchor().GetAnchorNode()->FindTableNode())
            {
                // WARNING: It is highly likely that everything here is simplistic and incomplete.

                // Microsoft allows WrapThrough shapes to be placed outside of the cell
                // despite having specified layoutInCell.
                // (Re-using existing, appropriately-named, compat flag to identify MSO formats.)
                const bool bMSOLayout = pFormat->getIDocumentSettingAccess().get(
                    DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);

                // Table position
                Point nTableOffset;
                // Floating table
                if (auto pFly
                    = pShape->GetAnchor().GetAnchorNode()->FindTableNode()->FindFlyStartNode())
                {
                    if (auto pFlyFormat = pFly->GetFlyFormat())
                    {
                        nTableOffset.setX(pFlyFormat->GetHoriOrient().GetPos());
                        nTableOffset.setY(pFlyFormat->GetVertOrient().GetPos());
                    }
                }
                else
                // Normal table
                {
                    auto pTableNode = pShape->GetAnchor().GetAnchorNode()->FindTableNode();
                    if (auto pTableFormat = pTableNode->GetTable().GetFrameFormat())
                    {
                        nTableOffset.setX(pTableFormat->GetHoriOrient().GetPos());
                        nTableOffset.setY(pTableFormat->GetVertOrient().GetPos());
                    }
                }

                // stay within the cell limits (since following text flow)
                // unless this is based on a Microsoft layout which has a through-wrap exception.
                bool bWrapThrough = false;
                getShapeWrapThrough(pShape, bWrapThrough);
                sal_Int32 nPos = aNewHOri.GetPos();
                if (nPos < 0 && (!bMSOLayout || !bWrapThrough))
                    nPos = 0;
                // Add the table positions to the textbox
                aNewHOri.SetPos(nPos + nTableOffset.getX());

                if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
                    || pShape->GetVertOrient().GetRelationOrient()
                           == text::RelOrientation::PAGE_PRINT_AREA)
                {
                    nPos = aNewVOri.GetPos();
                    if (nPos < 0 && (!bMSOLayout || !bWrapThrough))
                        nPos = 0;
                    aNewVOri.SetPos(nPos + nTableOffset.getY());
                }
            }

            pFormat->SetFormatAttr(aNewHOri);
            pFormat->SetFormatAttr(aNewVOri);
        }
        return true;
    }

    return false;
}

bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)
{
    if (!pShape || !pObj)
        return false;

    if (auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
    {
        auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
        const auto aSize = getRelativeTextRectangle(pObj).GetSize();
        if (!aSize.IsEmpty())
        {
            SwFormatFrameSize aFrameSize(pTextBox->GetFrameSize());
            aFrameSize.SetSize(aSize);
            return pTextBox->SetFormatAttr(aFrameSize);
        }
    }

    return false;
}

bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const SdrObject* pObj)
{
    // TODO: do this with group shape textboxes.
    SdrObject* pShpObj = nullptr;

    pShpObj = pShape->FindRealSdrObject();

    if (!pShpObj)
    {
        SAL_WARN("sw.core""SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
                            "No Valid SdrObject for the shape!");
        return false;
    }

    auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
    if (!pTextBox)
        return false;
    SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
    if (!pFrmObj)
    {
        if (SwFlyFrameFormat* pFlyFrameFormat = dynamic_cast<SwFlyFrameFormat*>(pTextBox))
        {
            // During loading there is no ready SdrObj for z-ordering, so create and cache it here
            pFrmObj = SwXTextFrame::GetOrCreateSdrObject(*pFlyFrameFormat);
        }
    }
    if (!pFrmObj)
    {
        SAL_WARN("sw.core""SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
                            "No Valid SdrObject for the frame!");
        return false;
    }
    // Get the draw model from the doc
    SwDrawModel* pDrawModel = pShape->GetDoc().getIDocumentDrawModelAccess().GetDrawModel();
    if (!pDrawModel)
    {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=97 G=93

¤ Dauer der Verarbeitung: 0.19 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.