tdf#116256 sw: fix textbox position in floating table

in case of it has to follow the text flow.

Change-Id: Ic4f195c2efcc465276faa9a95362933dafa65bee
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130077
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/extras/layout/data/tdf116256.docx b/sw/qa/extras/layout/data/tdf116256.docx
new file mode 100644
index 0000000..f067e04
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf116256.docx
Binary files differ
diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx
index 3845146..7c8914b 100644
--- a/sw/qa/extras/layout/layout2.cxx
+++ b/sw/qa/extras/layout/layout2.cxx
@@ -11,6 +11,7 @@

#include <com/sun/star/text/XTextFrame.hpp>
#include <com/sun/star/linguistic2/XHyphenator.hpp>
#include <com/sun/star/table/XTable.hpp>

#include <comphelper/scopeguard.hxx>
#include <comphelper/propertysequence.hxx>
@@ -988,6 +989,49 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf69648)
    }
}

CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf116256)
{
    // Open bugdoc
    createSwDoc(DATA_DIRECTORY, "tdf116256.docx");
    CPPUNIT_ASSERT(mxComponent);

    // Get the textbox
    uno::Reference<beans::XPropertySet> xTextBox(getShape(2), uno::UNO_QUERY_THROW);

    // Ensure that is a real textbox, and follows the text flow
    CPPUNIT_ASSERT(xTextBox->getPropertyValue("TextBox").get<bool>());
    CPPUNIT_ASSERT(xTextBox->getPropertyValue("IsFollowingTextFlow").get<bool>());

    // Parse the layout
    auto pLayout = parseLayoutDump();
    // Get the position of the shape
    const auto nTextBoxShapeLeft = getXPath(pLayout,
                                            "/root/page/body/txt/anchored/fly/tab/row[1]/cell/txt/"
                                            "anchored/SwAnchoredDrawObject/bounds",
                                            "left")
                                       .toInt64();
    const auto nTextBoxShapeTop = getXPath(pLayout,
                                           "/root/page/body/txt/anchored/fly/tab/row[1]/cell/txt/"
                                           "anchored/SwAnchoredDrawObject/bounds",
                                           "top")
                                      .toInt64();
    // Get the position of the textframe too.
    const auto nTextBoxFrameLeft
        = getXPath(pLayout,
                   "/root/page/body/txt/anchored/fly/tab/row[1]/cell/txt/anchored/fly/infos/bounds",
                   "left")
              .toInt64();
    const auto nTextBoxFrameTop
        = getXPath(pLayout,
                   "/root/page/body/txt/anchored/fly/tab/row[1]/cell/txt/anchored/fly/infos/bounds",
                   "top")
              .toInt64();

    // Without the fix in place these were less than they supposed to.
    CPPUNIT_ASSERT_GREATEREQUAL(nTextBoxShapeLeft, nTextBoxFrameLeft);
    CPPUNIT_ASSERT_GREATEREQUAL(nTextBoxShapeTop, nTextBoxFrameTop);
}

CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf138194)
{
    SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "xaxis-labelbreak.docx");
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
index 6657a52..21da16f5 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -26,9 +26,11 @@
#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>
@@ -1069,6 +1071,9 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& 
        return;

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

    SfxItemIter aIter(rSet);
@@ -1085,7 +1090,7 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& 
                    = mapAnchorType(rShape.GetAnchor().GetAnchorId());
                syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
                             pObj);
                if (bInlineAnchored)
                if (bInlineAnchored || bLayoutInCell)
                    return;
                SwFormatVertOrient aOrient(pItem->StaticWhichCast(RES_VERT_ORIENT));

@@ -1115,7 +1120,7 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& 
                    = mapAnchorType(rShape.GetAnchor().GetAnchorId());
                syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
                             pObj);
                if (bInlineAnchored)
                if (bInlineAnchored || bLayoutInCell)
                    return;
                SwFormatHoriOrient aOrient(pItem->StaticWhichCast(RES_HORI_ORIENT));

@@ -1324,24 +1329,37 @@ bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)

bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj)
{
    bool bSuccess = false;
    // 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());

        // Special treatment for AS_CHAR textboxes:
        if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
        {
            // Get the text area of the shape
            tools::Rectangle aRect(
                getTextRectangle(pObj ? pObj : pShape->FindRealSdrObject(), false));

            // Get the left spacing of the text area of the shape
            auto nLeftSpace = pShape->GetLRSpace().GetLeft();

            // Set the textbox position at the X-axis:
            SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
            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
@@ -1354,16 +1372,19 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
            }
            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:
@@ -1371,6 +1392,7 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
                        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:
@@ -1378,6 +1400,7 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
                        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:
@@ -1391,14 +1414,20 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
                }
            }

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

            // X Offset of the shape spacing
            auto nLeftSpace = pShape->GetLRSpace().GetLeft();

            // Set the same position as the (child) shape has
            SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
            aNewHOri.SetPos(
                (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
@@ -1408,10 +1437,12 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
                (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)
            {
@@ -1428,10 +1459,49 @@ bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pOb
                                + aRect.Top());
            }

            pFormat->SetFormatAttr(aNewHOri);
            pFormat->SetFormatAttr(aNewVOri);
            // Other special case: shape is inside a table or floating table following the text flow
            if (pShape->GetFollowTextFlow().GetValue() && pShape->GetAnchor().GetContentAnchor()
                && pShape->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode())
            {
                // Table position
                Point nTableOffset;
                // Floating table
                if (auto pFly = pShape->GetAnchor()
                                    .GetContentAnchor()
                                    ->nNode.GetNode()
                                    .FindTableNode()
                                    ->FindFlyStartNode())
                {
                    if (auto pFlyFormat = pFly->GetFlyFormat())
                    {
                        nTableOffset.setX(pFlyFormat->GetHoriOrient().GetPos());
                        nTableOffset.setY(pFlyFormat->GetVertOrient().GetPos());
                    }
                }
                else
                // Normal table
                {
                    auto pTableNode
                        = pShape->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode();
                    if (auto pTableFormat = pTableNode->GetTable().GetFrameFormat())
                    {
                        nTableOffset.setX(pTableFormat->GetHoriOrient().GetPos());
                        nTableOffset.setY(pTableFormat->GetVertOrient().GetPos());
                    }
                }

                // Add the table positions to the textbox.
                aNewHOri.SetPos(aNewHOri.GetPos() + nTableOffset.getX() + nLeftSpace);
                if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
                    || pShape->GetVertOrient().GetRelationOrient()
                           == text::RelOrientation::PAGE_PRINT_AREA)
                    aNewVOri.SetPos(aNewVOri.GetPos() + nTableOffset.getY());
            }

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

    return false;
@@ -1469,7 +1539,7 @@ bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const Sdr
        SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
        if (!pFrmObj)
        {
            // During doc-loading there is no ready SdrObj for z-ordering, so create one here and cache it.
            // During loading there is no ready SdrObj for z-ordering, so create and cache it here
            pFrmObj
                = SwXTextFrame::GetOrCreateSdrObject(*dynamic_cast<SwFlyFrameFormat*>(pTextBox));
        }
@@ -1480,9 +1550,9 @@ bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const Sdr
                = pShape->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
            if (pDrawModel)
            {
                // Not really sure this will work all page, but it seems it will.
                // Not really sure this will work on all pages, but it seems it will.
                auto pPage = pDrawModel->GetPage(0);
                // Recalc all Zorders
                // Recalc all Z-orders
                pPage->RecalcObjOrdNums();
                // Here is a counter avoiding running to in infinity:
                sal_uInt16 nIterator = 0;
diff --git a/xmloff/qa/unit/draw.cxx b/xmloff/qa/unit/draw.cxx
index 9185bfb..ac0801d 100644
--- a/xmloff/qa/unit/draw.cxx
+++ b/xmloff/qa/unit/draw.cxx
@@ -109,7 +109,7 @@ CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTextBoxLoss)
    // Make sure that the shape is still a textbox.
    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
    uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(1), uno::UNO_QUERY);
    uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    bool bTextBox = false;
    xShape->getPropertyValue("TextBox") >>= bTextBox;