tdf#153105 PPTX export: fix "Custom position/size" background image

Map size and the 9 preset positions of the ODF background image
style "Custom position/size" to the OOXML a:stretch/a:fillRect
with the appropriate left/top/right/bottom arguments.

Note: it seems, applying a:stretch or a:tile was not mandatory,
but missing a:stretch resulted non-editable document in Office 365.

Note: the import of the PPTX mapping hasn't been implemented, yet.

Follow-up to commit e8335bac5690b6beccb5ca9b36281c89fb2f28f5
"tdf#153107 OOXML export: fix scale of tile of shape background".

Change-Id: Ie940ebc2610c5b75126da05678a04ed1552cacb3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147337
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx
index 3613eb4..21914fb 100644
--- a/include/oox/export/drawingml.hxx
+++ b/include/oox/export/drawingml.hxx
@@ -281,6 +281,10 @@ public:
                           css::uno::Reference<css::graphic::XGraphic> const& rxGraphic,
                           css::awt::Size const& rSize);

    void WriteXGraphicCustomPosition(css::uno::Reference<css::beans::XPropertySet> const& rXPropSet,
                                     css::uno::Reference<css::graphic::XGraphic> const& rxGraphic,
                                     css::awt::Size const& rSize);

    void WriteLinespacing(const css::style::LineSpacing& rLineSpacing, float fFirstCharHeight);

    OUString WriteXGraphicBlip(css::uno::Reference<css::beans::XPropertySet> const & rXPropSet,
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 24463a2a..d3fb8a3 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -1584,6 +1584,9 @@ void DrawingML::WriteXGraphicBlipMode(uno::Reference<beans::XPropertySet> const 
    case BitmapMode_STRETCH:
        WriteXGraphicStretch(rXPropSet, rxGraphic);
        break;
    case BitmapMode_NO_REPEAT:
        WriteXGraphicCustomPosition(rXPropSet, rxGraphic, rSize);
        break;
    default:
        break;
    }
@@ -1923,6 +1926,81 @@ void DrawingML::WriteXGraphicTile(uno::Reference<beans::XPropertySet> const& rXP
                          OUString::number(nSizeY), XML_algn, sRectanglePoint);
}

void DrawingML::WriteXGraphicCustomPosition(uno::Reference<beans::XPropertySet> const& rXPropSet,
                                            uno::Reference<graphic::XGraphic> const& rxGraphic,
                                            css::awt::Size const& rSize)
{
    Graphic aGraphic(rxGraphic);
    Size aOriginalSize(aGraphic.GetPrefSize());
    const MapMode& rMapMode = aGraphic.GetPrefMapMode();
    // if the original size is in pixel, convert it to mm100
    if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
        aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize,
                                                                      MapMode(MapUnit::Map100thMM));
    double nSizeX = 0;
    if (GetProperty(rXPropSet, "FillBitmapSizeX"))
    {
        mAny >>= nSizeX;
        if (nSizeX <= 0)
        {
            if (nSizeX == 0)
                nSizeX = aOriginalSize.Width();
            else
                nSizeX /= 100; // percentage
        }
    }

    double nSizeY = 0;
    if (GetProperty(rXPropSet, "FillBitmapSizeY"))
    {
        mAny >>= nSizeY;
        if (nSizeY <= 0)
        {
            if (nSizeY == 0)
                nSizeY = aOriginalSize.Height();
            else
                nSizeY /= 100; // percentage
        }
    }

    if (nSizeX < 0 && nSizeY < 0 && rSize.Width != 0 && rSize.Height != 0)
    {
        nSizeX = rSize.Width * std::abs(nSizeX);
        nSizeY = rSize.Height * std::abs(nSizeY);
    }

    sal_Int32 nL = 0, nT = 0, nR = 0, nB = 0;
    if (GetProperty(rXPropSet, "FillBitmapRectanglePoint"))
    {
        sal_Int32 nWidth = (1 - (nSizeX / rSize.Width)) * 100000;
        sal_Int32 nHeight = (1 - (nSizeY / rSize.Height)) * 100000;

        switch (*o3tl::doAccess<RectanglePoint>(mAny))
        {
            case RectanglePoint_LEFT_TOP:      nR = nWidth;          nB = nHeight;          break;
            case RectanglePoint_RIGHT_TOP:     nL = nWidth;          nB = nHeight;          break;
            case RectanglePoint_LEFT_BOTTOM:   nR = nWidth;          nT = nHeight;          break;
            case RectanglePoint_RIGHT_BOTTOM:  nL = nWidth;          nT = nHeight;          break;
            case RectanglePoint_LEFT_MIDDLE:   nR = nWidth;          nT = nB = nHeight / 2; break;
            case RectanglePoint_RIGHT_MIDDLE:  nL = nWidth;          nT = nB = nHeight / 2; break;
            case RectanglePoint_MIDDLE_TOP:    nB = nHeight;         nL = nR = nWidth / 2;  break;
            case RectanglePoint_MIDDLE_BOTTOM: nT = nHeight;         nL = nR = nWidth / 2;  break;
            case RectanglePoint_MIDDLE_MIDDLE: nL = nR = nWidth / 2; nT = nB = nHeight / 2; break;
            default: break;
        }
    }

    mpFS->startElementNS(XML_a, XML_stretch);

    mpFS->singleElementNS(XML_a, XML_fillRect, XML_l,
                          sax_fastparser::UseIf(OString::number(nL), nL != 0), XML_t,
                          sax_fastparser::UseIf(OString::number(nT), nT != 0), XML_r,
                          sax_fastparser::UseIf(OString::number(nR), nR != 0), XML_b,
                          sax_fastparser::UseIf(OString::number(nB), nB != 0));

    mpFS->endElementNS(XML_a, XML_stretch);
}

namespace
{
bool IsTopGroupObj(const uno::Reference<drawing::XShape>& xShape)
diff --git a/sd/qa/unit/data/odp/tdf153105.odp b/sd/qa/unit/data/odp/tdf153105.odp
new file mode 100644
index 0000000..a5f765f
--- /dev/null
+++ b/sd/qa/unit/data/odp/tdf153105.odp
Binary files differ
diff --git a/sd/qa/unit/export-tests-ooxml3.cxx b/sd/qa/unit/export-tests-ooxml3.cxx
index 9616720..405170ff 100644
--- a/sd/qa/unit/export-tests-ooxml3.cxx
+++ b/sd/qa/unit/export-tests-ooxml3.cxx
@@ -49,6 +49,22 @@ public:
    int testTdf115005_FallBack_Images(bool bAddReplacementImages);
};

CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest3, testTdf153105)
{
    createSdImpressDoc("odp/tdf153105.odp");
    save("Impress Office Open XML");

    xmlDocUniquePtr pXmlDoc1 = parseExport("ppt/slides/slide1.xml");
    assertXPath(pXmlDoc1, "/p:sld/p:cSld/p:bg/p:bgPr/a:blipFill/a:stretch/a:fillRect", "l",
                "20000");
    assertXPath(pXmlDoc1, "/p:sld/p:cSld/p:bg/p:bgPr/a:blipFill/a:stretch/a:fillRect", "t",
                "30000");
    assertXPath(pXmlDoc1, "/p:sld/p:cSld/p:bg/p:bgPr/a:blipFill/a:stretch/a:fillRect", "r",
                "20000");
    assertXPath(pXmlDoc1, "/p:sld/p:cSld/p:bg/p:bgPr/a:blipFill/a:stretch/a:fillRect", "b",
                "30000");
}

CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest3, testTdf92222)
{
    createSdImpressDoc("pptx/tdf92222.pptx");