Related: tdf#94238 PPTX import: handle subset of radial gradient fill

Handle the case when the horizontal center is at 50%. Other cases are
still unhandled, those are more complex.

After fixing the style, center and border, the gradient fill looks
similar to how PowerPoint renders it.

Change-Id: I419da70482de37031aa2c7fc735692019d7665f5
Reviewed-on: https://gerrit.libreoffice.org/67168
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
diff --git a/include/oox/drawingml/color.hxx b/include/oox/drawingml/color.hxx
index 94d9050..2d33eb6 100644
--- a/include/oox/drawingml/color.hxx
+++ b/include/oox/drawingml/color.hxx
@@ -103,6 +103,9 @@
    /** Translates between color transformation token names and the corresponding token */
    static sal_Int32    getColorTransformationToken( const OUString& sName );

    /// Compares this color with rOther.
    bool equals(const Color& rOther, const GraphicHelper& rGraphicHelper, ::Color nPhClr) const;

private:
    /** Internal helper for getColor(). */
    void                setResolvedRgb( ::Color nRgb ) const;
diff --git a/oox/source/drawingml/color.cxx b/oox/source/drawingml/color.cxx
index 53b77c6..b258e7c 100644
--- a/oox/source/drawingml/color.cxx
+++ b/oox/source/drawingml/color.cxx
@@ -431,6 +431,14 @@
    return XML_TOKEN_INVALID;
}

bool Color::equals(const Color& rOther, const GraphicHelper& rGraphicHelper, ::Color nPhClr) const
{
    if (getColor(rGraphicHelper, nPhClr) != rOther.getColor(rGraphicHelper, nPhClr))
        return false;

    return getTransparency() == rOther.getTransparency();
}

void Color::clearTransparence()
{
    mnAlpha = MAX_PERCENT;
diff --git a/oox/source/drawingml/fillproperties.cxx b/oox/source/drawingml/fillproperties.cxx
index b255116..24e5e70 100644
--- a/oox/source/drawingml/fillproperties.cxx
+++ b/oox/source/drawingml/fillproperties.cxx
@@ -154,6 +154,30 @@
    return aSizeHmm;
}

/**
 * Looks for a last gradient transition and possibly sets a gradient border
 * based on that.
 */
void extractGradientBorderFromStops(const GradientFillProperties& rGradientProps,
                                    const GraphicHelper& rGraphicHelper, ::Color nPhClr,
                                    awt::Gradient& rGradient)
{
    if (rGradientProps.maGradientStops.size() <= 1)
        return;

    auto it = rGradientProps.maGradientStops.rbegin();
    double fLastPos = it->first;
    Color aLastColor = it->second;
    ++it;
    double fLastButOnePos = it->first;
    Color aLastButOneColor = it->second;
    if (!aLastColor.equals(aLastButOneColor, rGraphicHelper, nPhClr))
        return;

    // Last transition has the same color, we can map that to a border.
    rGradient.Border = rtl::math::round((fLastPos - fLastButOnePos) * 100);
}

} // namespace

void GradientFillProperties::assignUsed( const GradientFillProperties& rSourceProps )
@@ -354,15 +378,28 @@

                    if( maGradientProps.moGradientPath.has() )
                    {
                        aGradient.Style = (maGradientProps.moGradientPath.get() == XML_circle) ? awt::GradientStyle_ELLIPTICAL : awt::GradientStyle_RECT;
                        // position of gradient center (limited to [30%;70%], otherwise gradient is too hidden)
                        // position of gradient center (limited to [30%;100%], otherwise gradient is too hidden)
                        IntegerRectangle2D aFillToRect = maGradientProps.moFillToRect.get( IntegerRectangle2D( 0, 0, MAX_PERCENT, MAX_PERCENT ) );
                        sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - aFillToRect.X2) / 2;
                        aGradient.XOffset = getLimitedValue< sal_Int16, sal_Int32 >( nCenterX / PER_PERCENT, 30, 70 );
                        aGradient.XOffset = getLimitedValue<sal_Int16, sal_Int32>(
                            nCenterX / PER_PERCENT, 30, 100);

                        // Style should be radial at least when the horizontal center is at 50%.
                        awt::GradientStyle eCircle = aGradient.XOffset == 50
                                                         ? awt::GradientStyle_RADIAL
                                                         : awt::GradientStyle_ELLIPTICAL;
                        aGradient.Style = (maGradientProps.moGradientPath.get() == XML_circle)
                                              ? eCircle
                                              : awt::GradientStyle_RECT;

                        sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - aFillToRect.Y2) / 2;
                        aGradient.YOffset = getLimitedValue< sal_Int16, sal_Int32 >( nCenterY / PER_PERCENT, 30, 70 );
                        aGradient.YOffset = getLimitedValue<sal_Int16, sal_Int32>(
                            nCenterY / PER_PERCENT, 30, 100);
                        ::std::swap( aGradient.StartColor, aGradient.EndColor );
                        ::std::swap( nStartTrans, nEndTrans );

                        extractGradientBorderFromStops(maGradientProps, rGraphicHelper, nPhClr,
                                                       aGradient);
                    }
                    else if (!maGradientProps.maGradientStops.empty())
                    {
@@ -394,8 +431,7 @@
                            GradientFillProperties::GradientStopMap::const_iterator aItZ(std::prev(aGradientStops.end()));
                            while( bSymmetric && aItA->first < aItZ->first )
                            {
                                if( aItA->second.getColor( rGraphicHelper, nPhClr ) != aItZ->second.getColor( rGraphicHelper, nPhClr ) ||
                                    aItA->second.getTransparency() != aItZ->second.getTransparency() )
                                if (!aItA->second.equals(aItZ->second, rGraphicHelper, nPhClr))
                                    bSymmetric = false;
                                else
                                {
diff --git a/sd/qa/unit/data/pptx/tdf94238.pptx b/sd/qa/unit/data/pptx/tdf94238.pptx
new file mode 100644
index 0000000..cf35ece
--- /dev/null
+++ b/sd/qa/unit/data/pptx/tdf94238.pptx
Binary files differ
diff --git a/sd/qa/unit/import-tests.cxx b/sd/qa/unit/import-tests.cxx
index 0234733..a068fad 100644
--- a/sd/qa/unit/import-tests.cxx
+++ b/sd/qa/unit/import-tests.cxx
@@ -190,6 +190,7 @@
    void testTdf119015();
    void testTdf120028();
    void testTdf120028b();
    void testTdf94238();

    CPPUNIT_TEST_SUITE(SdImportTest);

@@ -273,6 +274,7 @@
    CPPUNIT_TEST(testTdf119015);
    CPPUNIT_TEST(testTdf120028);
    CPPUNIT_TEST(testTdf120028b);
    CPPUNIT_TEST(testTdf94238);

    CPPUNIT_TEST_SUITE_END();
};
@@ -2592,6 +2594,37 @@
    xDocShRef->DoClose();
}

void SdImportTest::testTdf94238()
{
    // Assert how the gradient fill of the only shape in the document is
    // imported.
    ::sd::DrawDocShellRef xDocShRef
        = loadURL(m_directories.getURLFromSrc("/sd/qa/unit/data/pptx/tdf94238.pptx"), PPTX);
    uno::Reference<drawing::XDrawPagesSupplier> xDoc(xDocShRef->GetDoc()->getUnoModel(),
                                                     uno::UNO_QUERY);
    CPPUNIT_ASSERT(xDoc.is());

    uno::Reference<drawing::XDrawPage> xPage(xDoc->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xPage.is());

    uno::Reference<beans::XPropertySet> xShape(getShape(0, xPage));
    CPPUNIT_ASSERT(xShape.is());

    awt::Gradient aGradient;
    CPPUNIT_ASSERT(xShape->getPropertyValue("FillGradient") >>= aGradient);

    // Without the accompanying fix in place, this test would have failed with
    // the following details:
    // - aGradient.Style was awt::GradientStyle_ELLIPTICAL
    // - aGradient.YOffset was 70
    // - aGradient.Border was 0
    CPPUNIT_ASSERT_EQUAL(awt::GradientStyle_RADIAL, aGradient.Style);
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(100), aGradient.YOffset);
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(39), aGradient.Border);

    xDocShRef->DoClose();
}

CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTest);

CPPUNIT_PLUGIN_IMPLEMENT();