tdf#140158 tdf#138598 tdf#140598 sw: fix sync of AS_CHAR textboxes

Textboxes anchored "As Character" fell apart, when
typing before some characters or inserting a page break.

By fixing that, the tdf#138598 bug also have fixed which
was a regression from commit b6850bbe95418ecfde404be1696548f18d200c9b
(tdf#106153 sw compatibility: fix textboxes exceeding the page).

In addition, tdf140598 is also fixed, which was
a regression from commit c96c386c5db45dc4d5e358915caad7474e373068
(tdf#136516 add positioning to SwTextBoxHelper::syncProperty()).

Change-Id: Ifeadd8b2055ce52a019d651369ca41185de7bbe3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111338
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/extras/uiwriter/data3/AsCharTxBxTest.docx b/sw/qa/extras/uiwriter/data3/AsCharTxBxTest.docx
new file mode 100755
index 0000000..7603b80
--- /dev/null
+++ b/sw/qa/extras/uiwriter/data3/AsCharTxBxTest.docx
Binary files differ
diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx
index 7bb002b..6ee8609 100644
--- a/sw/qa/extras/uiwriter/uiwriter3.cxx
+++ b/sw/qa/extras/uiwriter/uiwriter3.cxx
@@ -960,6 +960,58 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf134253)
    CPPUNIT_ASSERT_EQUAL(6, getPages());
}

CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, TestAsCharTextBox)
{
    // Releated tickets:
    // tdf#138598 Replace vertical alignment of As_char textboxes in footer
    // tdf#140158 Remove horizontal positioning of As_char textboxes, because
    // the anchor moving does the same for it.

    load(DATA_DIRECTORY, "AsCharTxBxTest.docx");
    SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
    CPPUNIT_ASSERT(pTextDoc);

    // Add 3x tab to the doc
    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
    pTextDoc->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_TAB);
    Scheduler::ProcessEventsToIdle();

    auto pExportDump = parseLayoutDump();
    CPPUNIT_ASSERT(pExportDump);

    // Check if the texbox fallen apart due to the tabs
    const double nLeftSideOfShape1
        = getXPath(pExportDump, "/root/page/body/txt/anchored/SwAnchoredDrawObject/bounds", "left")
              .toDouble();
    const double nLeftSideOfTxBx1
        = getXPath(pExportDump, "/root/page/body/txt/anchored/fly/infos/bounds", "left").toDouble();

    CPPUNIT_ASSERT(nLeftSideOfShape1 < nLeftSideOfTxBx1);

    // Another test is for the tdf#138598: Check footer textbox
    const double nLeftSideOfShape2
        = getXPath(pExportDump, "/root/page[2]/footer/txt/anchored/SwAnchoredDrawObject/bounds",
                   "left")
              .toDouble();
    const double nLeftSideOfTxBx2
        = getXPath(pExportDump, "/root/page[2]/footer/txt/anchored/fly/infos/bounds", "left")
              .toDouble();

    CPPUNIT_ASSERT(nLeftSideOfShape2 < nLeftSideOfTxBx2);

    const double nTopSideOfShape2
        = getXPath(pExportDump, "/root/page[2]/footer/txt/anchored/SwAnchoredDrawObject/bounds",
                   "top")
              .toDouble();
    const double nTopSideOfTxBx2
        = getXPath(pExportDump, "/root/page[2]/footer/txt/anchored/fly/infos/bounds", "top")
              .toDouble();

    CPPUNIT_ASSERT(nTopSideOfShape2 < nTopSideOfTxBx2);
    // Without the fix in place the two texboxes has been fallen apart, and  asserts will broken.
}

CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf76636)
{
    load(DATA_DIRECTORY, "tdf76636.doc");
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
index 37b6120..abe8f38 100644
--- a/sw/source/core/doc/textboxhelper.cxx
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -711,10 +711,39 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_u
                    if (aValue.get<text::TextContentAnchorType>()
                        == text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER)
                    {
                        xPropertySet->setPropertyValue(
                            UNO_NAME_ANCHOR_TYPE,
                            uno::makeAny(
                                text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH));
                        if (const auto aPos = pShape->GetAnchor().GetContentAnchor())
                        {
                            xPropertySet->setPropertyValue(
                                UNO_NAME_ANCHOR_TYPE,
                                uno::makeAny(text::TextContentAnchorType::
                                                 TextContentAnchorType_AT_CHARACTER));
                            xPropertySet->setPropertyValue(
                                UNO_NAME_HORI_ORIENT_RELATION,
                                uno::makeAny(text::RelOrientation::CHAR));

                            auto pAnch = pFormat->GetAnchor();
                            pAnch.SetAnchor(pShape->GetAnchor().GetContentAnchor());
                            tools::Rectangle aRect(getTextRectangle(pShape, false));

                            SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
                            aNewHOri.SetPos(aRect.getX());

                            SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
                            aNewVOri.SetPos(aRect.getY());

                            pFormat->SetFormatAttr(pAnch);
                            // tdf#140598: Do not apply wrong rectangle position.
                            if (aRect.TopLeft() != Point(0, 0))
                            {
                                pFormat->SetFormatAttr(aNewHOri);
                                pFormat->SetFormatAttr(aNewVOri);
                            }
                            else
                                SAL_WARN("sw.core",
                                         "SwTextBoxHelper::syncProperty: Repositioning failed!");
                        }

                        return;
                    }
                    else // Otherwise copy the anchor type of the shape
                    {
@@ -725,9 +754,15 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_u
                    if (aValue.get<text::TextContentAnchorType>()
                        == text::TextContentAnchorType::TextContentAnchorType_AT_PAGE)
                    {
                        xPropertySet->setPropertyValue(
                            UNO_NAME_ANCHOR_PAGE_NO,
                            uno::makeAny(pShape->GetAnchor().GetPageNum()));
                        if (pShape->GetAnchor().GetPageNum())
                            xPropertySet->setPropertyValue(
                                UNO_NAME_ANCHOR_PAGE_NO,
                                uno::makeAny(pShape->GetAnchor().GetPageNum()));
                        else
                        {
                            SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: Invalid Page Num!");
                            return;
                        }
                    }
                    // At-Content Anchors have to be synced:
                    if (aValue.get<text::TextContentAnchorType>()
@@ -748,15 +783,25 @@ void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_u
                                     "SwTextBoxHelper::syncProperty: Anchor without content!");
                    }
                    // And the repositioning:
                    tools::Rectangle aRect(getTextRectangle(pShape, false));
                    if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
                    {
                        tools::Rectangle aRect(getTextRectangle(pShape, false));

                    SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
                    aNewHOri.SetPos(aNewHOri.GetPos() + aRect.getX());
                    SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
                    aNewVOri.SetPos(aNewVOri.GetPos() + aRect.getY());
                        // tdf#140598: Do not apply wrong rectangle position.
                        if (aRect.TopLeft() != Point(0, 0))
                        {
                            SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
                            aNewHOri.SetPos(aNewHOri.GetPos() + aRect.getX());
                            SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
                            aNewVOri.SetPos(aNewVOri.GetPos() + aRect.getY());

                    pFormat->SetFormatAttr(aNewHOri);
                    pFormat->SetFormatAttr(aNewVOri);
                            pFormat->SetFormatAttr(aNewHOri);
                            pFormat->SetFormatAttr(aNewVOri);
                        }
                        else
                            SAL_WARN("sw.core",
                                     "SwTextBoxHelper::syncProperty: Repositioning failed!");
                    }
                    return;
                }
                break;
@@ -947,6 +992,7 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& 
    if (!pFormat)
        return;

    const bool bInlineAnchored = rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
    SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);

    SfxItemIter aIter(rSet);
@@ -962,6 +1008,8 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& 
                const text::TextContentAnchorType aNewAnchorType
                    = mapAnchorType(rShape.GetAnchor().GetAnchorId());
                syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType));
                if (bInlineAnchored)
                    return;
                auto& rOrient = static_cast<const SwFormatVertOrient&>(*pItem);
                SwFormatVertOrient aOrient(rOrient);

@@ -990,6 +1038,8 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& 
                    = mapAnchorType(rShape.GetAnchor().GetAnchorId());
                syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType));
                auto& rOrient = static_cast<const SwFormatHoriOrient&>(*pItem);
                if (bInlineAnchored)
                    return;
                SwFormatHoriOrient aOrient(rOrient);

                tools::Rectangle aRect = getTextRectangle(&rShape, /*bAbsolute=*/false);
@@ -1015,11 +1065,14 @@ void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& 
                tools::Rectangle aRect = getTextRectangle(&rShape, /*bAbsolute=*/false);
                if (!aRect.IsEmpty())
                {
                    aVertOrient.SetPos(aVertOrient.GetPos() + aRect.getY());
                    aTextBoxSet.Put(aVertOrient);
                    if (!bInlineAnchored)
                    {
                        aVertOrient.SetPos(aVertOrient.GetPos() + aRect.getY());
                        aHoriOrient.SetPos(aHoriOrient.GetPos() + aRect.getX());

                    aHoriOrient.SetPos(aHoriOrient.GetPos() + aRect.getX());
                    aTextBoxSet.Put(aHoriOrient);
                        aTextBoxSet.Put(aVertOrient);
                        aTextBoxSet.Put(aHoriOrient);
                    }

                    aSize.SetWidth(aRect.getWidth());
                    aSize.SetHeight(aRect.getHeight());
diff --git a/sw/source/core/text/porfly.cxx b/sw/source/core/text/porfly.cxx
index 8fcbb7f..e41afd8 100644
--- a/sw/source/core/text/porfly.cxx
+++ b/sw/source/core/text/porfly.cxx
@@ -352,20 +352,30 @@ void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase,
            // is relative to the print area of the anchor text frame.
            tools::Rectangle aTextRectangle = SwTextBoxHelper::getTextRectangle(pShape);

            SwFormatHoriOrient aHori(pTextBox->GetHoriOrient());
            aHori.SetHoriOrient(css::text::HoriOrientation::NONE);
            sal_Int32 nLeft = aTextRectangle.getX() - rFrame.getFrameArea().Left()
                              - rFrame.getFramePrintArea().Left();
            aHori.SetPos(nLeft);

            const auto aPos(pShape->GetAnchor().GetContentAnchor());
            SwFormatVertOrient aVert(pTextBox->GetVertOrient());
            aVert.SetVertOrient(css::text::VertOrientation::NONE);
            sal_Int32 const nTop = aTextRectangle.getY() - rFrame.getFrameArea().Top()
                                   - rFrame.getFramePrintArea().Top();
            aVert.SetPos(nTop);

            // tdf#138598 Replace vertical alignment of As_char textboxes in footer
            // tdf#140158 Remove horizontal positioning of As_char textboxes, because
            // the anchor moving does the same for it.
            if (!aPos->nNode.GetNode().FindFooterStartNode())
            {
                aVert.SetVertOrient(css::text::VertOrientation::NONE);
                sal_Int32 const nTop = aTextRectangle.getY() - rFrame.getFrameArea().Top()
                                       - rFrame.getFramePrintArea().Top();
                aVert.SetPos(nTop);
            }
            else
            {
                aVert.SetVertOrient(css::text::VertOrientation::NONE);
                aVert.SetPos(SwTextBoxHelper::getTextRectangle(pShape, false).getY());
            }

            SwFormatAnchor aNewTxBxAnchor(pTextBox->GetAnchor());
            aNewTxBxAnchor.SetAnchor(aPos);

            pTextBox->LockModify();
            pTextBox->SetFormatAttr(aHori);
            pTextBox->SetFormatAttr(aNewTxBxAnchor);
            pTextBox->SetFormatAttr(aVert);
            pTextBox->UnlockModify();
        }