tdf#133070 DOCX import: fix shape height relative to bottom page margin

using UNO API RelativeHeightRelation and the associated
lo-ext attribute for OpenDocument export.

See commit 43d7f4e3640c5e370fd1204739c2b0c7eb5f40e4
(offapi: document the 4 new properties which are no longer read-only).

Co-authored-by: Szabolcs Tóth

Change-Id: I31db99fac5ad24971fa427671198b47906e9c057
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95451
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/extras/ooxmlexport/data/tdf133070_testRelativeAnchorHeightFromBottomMarginHasFooter.docx b/sw/qa/extras/ooxmlexport/data/tdf133070_testRelativeAnchorHeightFromBottomMarginHasFooter.docx
new file mode 100644
index 0000000..2625a9a
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf133070_testRelativeAnchorHeightFromBottomMarginHasFooter.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf133070_testRelativeAnchorHeightFromBottomMarginNoFooter.docx b/sw/qa/extras/ooxmlexport/data/tdf133070_testRelativeAnchorHeightFromBottomMarginNoFooter.docx
new file mode 100644
index 0000000..155e7485
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf133070_testRelativeAnchorHeightFromBottomMarginNoFooter.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx
index 92c5423..240447b 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport15.cxx
@@ -52,6 +52,38 @@ DECLARE_OOXMLEXPORT_TEST(testTdf134063, "tdf134063.docx")
    CPPUNIT_ASSERT_EQUAL(sal_Int32(720), getXPath(pDump, "//page[1]/body/txt[1]/Text[3]", "nWidth").toInt32());
}

DECLARE_OOXMLEXPORT_TEST(testRelativeAnchorHeightFromBottomMarginHasFooter,
                         "tdf133070_testRelativeAnchorHeightFromBottomMarginHasFooter.docx")
{
    // TODO: fix export too
    if (mbExported)
        return;
    // tdf#133070 The height was set relative to page print area bottom,
    // but this was handled relative to page height.
    // Note: page print area bottom = margin + footer height.
    // In this case the footer exists.
    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
    const sal_Int32 nAnchoredHeight
        = getXPath(pXmlDoc, "//SwAnchoredDrawObject/bounds", "height").toInt32();
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1147), nAnchoredHeight);
}

DECLARE_OOXMLEXPORT_TEST(testRelativeAnchorHeightFromBottomMarginNoFooter,
                         "tdf133070_testRelativeAnchorHeightFromBottomMarginNoFooter.docx")
{
    // TODO: fix export too
    if (mbExported)
        return;
    // tdf#133070 The height was set relative to page print area bottom,
    // but this was handled relative to page height.
    // Note: page print area bottom = margin + footer height.
    // In this case the footer does not exist, so OpenDocument and OOXML margins are the same.
    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
    const sal_Int32 nAnchoredHeight
        = getXPath(pXmlDoc, "//SwAnchoredDrawObject/bounds", "height").toInt32();
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1147), nAnchoredHeight);
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/pagefrm.hxx b/sw/source/core/inc/pagefrm.hxx
index 81949f5..3c7f29f 100644
--- a/sw/source/core/inc/pagefrm.hxx
+++ b/sw/source/core/inc/pagefrm.hxx
@@ -21,6 +21,7 @@

#include <viewsh.hxx>
#include "ftnboss.hxx"
#include "hffrm.hxx"

#include <SidebarWindowsTypes.hxx>

@@ -328,6 +329,8 @@ public:
    /// Is bottom-of-page-frame - bottom-of-text-frame difference valid in case whitespace is hidden?
    /// If false is returned, then the caller should handle negative difference as (at least) zero difference instead.
    bool CheckPageHeightValidForHideWhitespace(SwTwips nDiff);

    const SwFooterFrame* GetFooterFrame() const;
};

inline SwContentFrame *SwPageFrame::FindFirstBodyContent()
diff --git a/sw/source/core/layout/anchoreddrawobject.cxx b/sw/source/core/layout/anchoreddrawobject.cxx
index ae7ecf1..9b4bce8 100644
--- a/sw/source/core/layout/anchoreddrawobject.cxx
+++ b/sw/source/core/layout/anchoreddrawobject.cxx
@@ -652,13 +652,22 @@ SwRect SwAnchoredDrawObject::GetObjBoundRect() const
        long nTargetHeight = aCurrObjRect.GetHeight( );
        if ( GetDrawObj( )->GetRelativeHeight( ) )
        {
            tools::Rectangle aPageRect;
            long nHeight = 0;
            if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::FRAME)
                // Exclude margins.
                aPageRect = GetPageFrame()->getFramePrintArea().SVRect();
                nHeight = GetPageFrame()->getFramePrintArea().SVRect().GetHeight();
            else if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
            {
                // count required height: print area bottom = bottom margin + footer
                SwRect aFooterRect;
                auto pFooterFrame = GetPageFrame()->GetFooterFrame();
                if (pFooterFrame)
                    aFooterRect = pFooterFrame->GetPaintArea();
                nHeight = GetPageFrame()->GetBottomMargin() + aFooterRect.Height();
            }
            else
                aPageRect = GetPageFrame( )->GetBoundRect( GetPageFrame()->getRootFrame()->GetCurrShell()->GetOut() ).SVRect();
            nTargetHeight = aPageRect.GetHeight( ) * (*GetDrawObj( )->GetRelativeHeight());
                nHeight = GetPageFrame( )->GetBoundRect( GetPageFrame()->getRootFrame()->GetCurrShell()->GetOut() ).SVRect().GetHeight();
            nTargetHeight = nHeight * (*GetDrawObj()->GetRelativeHeight());
        }

        if ( nTargetWidth != aCurrObjRect.GetWidth( ) || nTargetHeight != aCurrObjRect.GetHeight( ) )
diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx
index 4a29018..a3188eb 100644
--- a/sw/source/core/layout/pagechg.cxx
+++ b/sw/source/core/layout/pagechg.cxx
@@ -2512,6 +2512,18 @@ bool SwPageFrame::CheckPageHeightValidForHideWhitespace(SwTwips nDiff)
    return true;
}

const SwFooterFrame* SwPageFrame::GetFooterFrame() const
{
    const SwFrame* pLowerFrame = Lower();
    while (pLowerFrame)
    {
        if (pLowerFrame->IsFooterFrame())
            return dynamic_cast<const SwFooterFrame*>(pLowerFrame);
        pLowerFrame = pLowerFrame->GetNext();
    }
    return nullptr;
}

SwTextGridItem const* GetGridItem(SwPageFrame const*const pPage)
{
    if (pPage && pPage->HasGrid())
diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx
index 7a273ed..7d4b016 100644
--- a/writerfilter/source/dmapper/GraphicImport.cxx
+++ b/writerfilter/source/dmapper/GraphicImport.cxx
@@ -994,6 +994,13 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue)
                        xPropertySet->setPropertyValue("RelativeHeightRelation", uno::makeAny(text::RelOrientation::PAGE_FRAME));
                    }
                    break;
                case NS_ooxml::LN_ST_SizeRelFromV_bottomMargin:
                    if (m_xShape.is())
                    {
                        uno::Reference<beans::XPropertySet> xPropertySet(m_xShape, uno::UNO_QUERY);
                        xPropertySet->setPropertyValue("RelativeHeightRelation", uno::makeAny(text::RelOrientation::PAGE_PRINT_AREA_BOTTOM));
                    }
                    break;
                default:
                    SAL_WARN("writerfilter", "GraphicImport::lcl_attribute: unhandled NS_ooxml::LN_CT_SizeRelV_relativeFrom value: " << nIntValue);
                    break;