From 33e69fb9ef450169e7d85ae3215fd9a4fc8082c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCl=C5=9Fah=20K=C3=B6se?= <gulsah.kose@collabora.com>
Date: Mon, 1 Feb 2021 17:03:33 +0300
Subject: [PATCH] tdf#134210 Import crop position of bitmap filled shape.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Change-Id: I6a62d68cd0f57e53934851a2f53dae05bf7d3730
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110262
Tested-by: Jenkins
Reviewed-by: Gülşah Köse <gulsah.kose@collabora.com>
Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110973
---
 oox/source/drawingml/fillproperties.cxx |  84 ++++++++++++++++++------
 oox/source/export/drawingml.cxx         |  39 +++++++----
 sd/qa/unit/data/pptx/crop-position.pptx | Bin 0 -> 175724 bytes
 sd/qa/unit/export-tests-ooxml1.cxx      |  29 ++++++--
 sd/qa/unit/import-tests.cxx             |  16 +++++
 5 files changed, 129 insertions(+), 39 deletions(-)
 create mode 100644 sd/qa/unit/data/pptx/crop-position.pptx

diff --git a/oox/source/drawingml/fillproperties.cxx b/oox/source/drawingml/fillproperties.cxx
index 78ebff7e2122..3439d28a4f22 100644
--- a/oox/source/drawingml/fillproperties.cxx
+++ b/oox/source/drawingml/fillproperties.cxx
@@ -87,6 +87,30 @@ Reference< XGraphic > lclRotateGraphic(uno::Reference<graphic::XGraphic> const &
     return aReturnGraphic.GetXGraphic();
 }
 
+void lclCalculateCropPercentage(uno::Reference<graphic::XGraphic> const & xGraphic, geometry::IntegerRectangle2D &aFillRect)
+{
+    ::Graphic aGraphic(xGraphic);
+    assert (aGraphic.GetType() == GraphicType::Bitmap);
+
+    BitmapEx aBitmapEx(aGraphic.GetBitmapEx());
+
+    sal_Int32 nScaledWidth = aBitmapEx.GetSizePixel().Width();
+    sal_Int32 nScaledHeight = aBitmapEx.GetSizePixel().Height();
+
+    sal_Int32 nOrigWidth = (nScaledWidth * (100000 - aFillRect.X1 - aFillRect.X2)) / 100000;
+    sal_Int32 nOrigHeight = (nScaledHeight * (100000 - aFillRect.Y1 - aFillRect.Y2)) / 100000;
+
+    sal_Int32 nLeftPercentage = nScaledWidth * aFillRect.X1 / nOrigWidth;
+    sal_Int32 nRightPercentage = nScaledWidth * aFillRect.X2 / nOrigWidth;
+    sal_Int32 nTopPercentage = nScaledHeight * aFillRect.Y1 / nOrigHeight;
+    sal_Int32 nBottomPercentage = nScaledHeight * aFillRect.Y2 / nOrigHeight;
+
+    aFillRect.X1 = -nLeftPercentage;
+    aFillRect.X2 = -nRightPercentage;
+    aFillRect.Y1 = -nTopPercentage;
+    aFillRect.Y2 = -nBottomPercentage;
+}
+
 Reference< XGraphic > lclCropGraphic(uno::Reference<graphic::XGraphic> const & xGraphic, geometry::IntegerRectangle2D aFillRect)
 {
     ::Graphic aGraphic(xGraphic);
@@ -103,8 +127,15 @@ Reference< XGraphic > lclCropGraphic(uno::Reference<graphic::XGraphic> const & x
     sal_Int32 nBottomCorr = nOrigHeight * -1 * static_cast<double>(aFillRect.Y2) / 100000;
     nHeight += nBottomCorr;
 
-    aBitmapEx.Scale(Size(aBitmapEx.GetSizePixel().Width(), nHeight));
-    aBitmapEx.Crop(tools::Rectangle(Point(0, nTopCorr), Size(aBitmapEx.GetSizePixel().Width(), nOrigHeight)));
+    sal_Int32 nOrigWidth = aBitmapEx.GetSizePixel().Width();
+    sal_Int32 nWidth = nOrigWidth;
+    sal_Int32 nLeftCorr  = nOrigWidth * -1 * static_cast<double>(aFillRect.X1) / 100000;
+    nWidth += nLeftCorr;
+    sal_Int32 nRightCorr = nOrigWidth * -1 * static_cast<double>(aFillRect.X2) / 100000;
+    nWidth += nRightCorr;
+
+    aBitmapEx.Scale(Size(nWidth, nHeight));
+    aBitmapEx.Crop(tools::Rectangle(Point(nLeftCorr, nTopCorr), Size(nOrigWidth, nOrigHeight)));
 
     aReturnGraphic = ::Graphic(aBitmapEx);
     aReturnGraphic.setOriginURL(aGraphic.getOriginURL());
@@ -813,6 +844,36 @@ void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelpe
             nContrast = 0;
         }
 
+        // cropping
+        if ( maBlipProps.moClipRect.has() )
+        {
+            geometry::IntegerRectangle2D oClipRect( maBlipProps.moClipRect.get() );
+            awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
+            if ( aOriginalSize.Width && aOriginalSize.Height )
+            {
+                text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
+                if ( oClipRect.X1 )
+                    aGraphCrop.Left = rtl::math::round( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X1 ) / 100000 );
+                if ( oClipRect.Y1 )
+                    aGraphCrop.Top = rtl::math::round( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y1 ) / 100000 );
+                if ( oClipRect.X2 )
+                    aGraphCrop.Right = rtl::math::round( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X2 ) / 100000 );
+                if ( oClipRect.Y2 )
+                    aGraphCrop.Bottom = rtl::math::round( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y2 ) / 100000 );
+                rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
+            }
+
+            if(mbIsCustomShape)
+            {
+                geometry::IntegerRectangle2D aCropRect = oClipRect;
+                lclCalculateCropPercentage(xGraphic, aCropRect);
+                xGraphic = lclCropGraphic(xGraphic, aCropRect);
+
+                rPropMap.setProperty(PROP_FillBitmap, xGraphic);
+            }
+
+        }
+
         if(mbIsCustomShape)
         {
             // it is a cropped graphic.
@@ -837,25 +898,6 @@ void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelpe
         else
             rPropMap.setProperty(PROP_Graphic, xGraphic);
 
-        // cropping
-        if ( maBlipProps.moClipRect.has() )
-        {
-            geometry::IntegerRectangle2D oClipRect( maBlipProps.moClipRect.get() );
-            awt::Size aOriginalSize( rGraphicHelper.getOriginalSize( xGraphic ) );
-            if ( aOriginalSize.Width && aOriginalSize.Height )
-            {
-                text::GraphicCrop aGraphCrop( 0, 0, 0, 0 );
-                if ( oClipRect.X1 )
-                    aGraphCrop.Left = rtl::math::round( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X1 ) / 100000 );
-                if ( oClipRect.Y1 )
-                    aGraphCrop.Top = rtl::math::round( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y1 ) / 100000 );
-                if ( oClipRect.X2 )
-                    aGraphCrop.Right = rtl::math::round( ( static_cast< double >( aOriginalSize.Width ) * oClipRect.X2 ) / 100000 );
-                if ( oClipRect.Y2 )
-                    aGraphCrop.Bottom = rtl::math::round( ( static_cast< double >( aOriginalSize.Height ) * oClipRect.Y2 ) / 100000 );
-                rPropMap.setProperty(PROP_GraphicCrop, aGraphCrop);
-            }
-        }
 
         if ( maBlipProps.moAlphaModFix.has() )
         {
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 21394acc80da..94b67cb379f2 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -1598,27 +1598,40 @@ void DrawingML::WritePattFill(const Reference<XPropertySet>& rXPropSet, const cs
         mpFS->endElementNS( XML_a , XML_pattFill );
 }
 
-void DrawingML::WriteGraphicCropProperties(uno::Reference<beans::XPropertySet> const & rXPropSet, Size const & rOriginalSize, MapMode const & rMapMode)
+void DrawingML::WriteGraphicCropProperties(uno::Reference<beans::XPropertySet> const & rXPropSet,
+                                           Size const & rOriginalSize,
+                                           MapMode const & rMapMode)
 {
     if (!GetProperty(rXPropSet, "GraphicCrop"))
         return;
 
-    Size aOriginalSize(rOriginalSize);
-
-    // GraphicCrop is in mm100, so in case the original size is in pixels, convert it over.
-    if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
-        aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, MapMode(MapUnit::Map100thMM));
-
     css::text::GraphicCrop aGraphicCropStruct;
     mAny >>= aGraphicCropStruct;
 
-    if ( (0 != aGraphicCropStruct.Left) || (0 != aGraphicCropStruct.Top) || (0 != aGraphicCropStruct.Right) || (0 != aGraphicCropStruct.Bottom) )
+    if(GetProperty(rXPropSet, "CustomShapeGeometry"))
+    {
+    // tdf#134210 GraphicCrop property is handled in import filter because of LibreOffice has not core
+    // feature. We croped the bitmap physically and MSO shouldn't crop bitmap one more time. When we
+    // have core feature for graphic cropping in custom shapes, we should uncomment the code anymore.
+
+        mpFS->singleElementNS( XML_a, XML_srcRect);
+    }
+    else
     {
-        mpFS->singleElementNS( XML_a, XML_srcRect,
-            XML_l, OString::number(rtl::math::round(aGraphicCropStruct.Left * 100000.0 / aOriginalSize.Width())),
-            XML_t, OString::number(rtl::math::round(aGraphicCropStruct.Top * 100000.0 / aOriginalSize.Height())),
-            XML_r, OString::number(rtl::math::round(aGraphicCropStruct.Right * 100000.0 / aOriginalSize.Width())),
-            XML_b, OString::number(rtl::math::round(aGraphicCropStruct.Bottom * 100000.0 / aOriginalSize.Height())) );
+        Size aOriginalSize(rOriginalSize);
+
+        // GraphicCrop is in mm100, so in case the original size is in pixels, convert it over.
+        if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
+            aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, MapMode(MapUnit::Map100thMM));
+
+        if ( (0 != aGraphicCropStruct.Left) || (0 != aGraphicCropStruct.Top) || (0 != aGraphicCropStruct.Right) || (0 != aGraphicCropStruct.Bottom) )
+        {
+            mpFS->singleElementNS( XML_a, XML_srcRect,
+                XML_l, OString::number(rtl::math::round(aGraphicCropStruct.Left * 100000.0 / aOriginalSize.Width())),
+                XML_t, OString::number(rtl::math::round(aGraphicCropStruct.Top * 100000.0 / aOriginalSize.Height())),
+                XML_r, OString::number(rtl::math::round(aGraphicCropStruct.Right * 100000.0 / aOriginalSize.Width())),
+                XML_b, OString::number(rtl::math::round(aGraphicCropStruct.Bottom * 100000.0 / aOriginalSize.Height())) );
+        }
     }
 }
 
diff --git a/sd/qa/unit/export-tests-ooxml1.cxx b/sd/qa/unit/export-tests-ooxml1.cxx
index 0e938e05e9c7..a1e752834f1a 100644
--- a/sd/qa/unit/export-tests-ooxml1.cxx
+++ b/sd/qa/unit/export-tests-ooxml1.cxx
@@ -1145,17 +1145,36 @@ void SdOOXMLExportTest1::testCustomshapeBitmapfillSrcrect()
     xDocShRef->DoClose();
 
     xmlDocUniquePtr pXmlDoc = parseExport(tempFile, "ppt/slides/slide1.xml");
-    const OString sXmlPath = "//a:blipFill/a:srcRect";
+
+    // tdf#132680
+    // We are preventing the side effect of DOCX improvement to PPTX case.
     // Without the accompanying fix in place, this test would have failed with:
     // - Expected: 1
     // - Actual  : 0
     // - XPath '//a:blipFill/a:srcRect' number of nodes is incorrect
     // i.e. <a:srcRect> was exported as <a:fillRect> in <a:stretch>, which made part of the image
     // invisible.
-    double fLeftPercent = std::round(getXPath(pXmlDoc, sXmlPath, "l").toDouble() / 1000);
-    CPPUNIT_ASSERT_EQUAL(4.0, fLeftPercent);
-    double fRightPercent = std::round(getXPath(pXmlDoc, sXmlPath, "r").toDouble() / 1000);
-    CPPUNIT_ASSERT_EQUAL(4.0, fRightPercent);
+
+    // tdf#134210
+    // Original values of attribute of l and r in xml files: <a:srcRect l="4393" r="4393"/>
+    // Because of we have not core feature for cropping bitmap in custom shapes, we added cropping
+    // support to import filter. We modified the bitmap during import. As result the original
+    // image is cropped anymore (if we had the core feature for that, the original image would
+    // remain same, just we would see like cropped in the shape) To see the image in correct
+    // position in Microsoft Office too, we have to remove left right top bottom percentages
+    // anymore. In the future if we add core feature to LibreOffice, this test will failed with
+    // When we add the core feature, we should change the control value with 4393 as following.
+
+    //const OString sXmlPath = "//a:blipFill/a:srcRect";
+    //sal_Int32 nLeftPercent = getXPath(pXmlDoc, sXmlPath, "l").toInt32();
+    //CPPUNIT_ASSERT_EQUAL(sal_Int32(4393), nLeftPercent);
+    //sal_Int32 nRightPercent = getXPath(pXmlDoc, sXmlPath, "r").toInt32();
+    //CPPUNIT_ASSERT_EQUAL(sal_Int32(4393), nRightPercent);
+
+    assertXPathNoAttribute(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:blipFill/a:srcRect", "l");
+    assertXPathNoAttribute(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:blipFill/a:srcRect", "r");
+    assertXPathNoAttribute(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:blipFill/a:srcRect", "t");
+    assertXPathNoAttribute(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:blipFill/a:srcRect", "b");
 }
 
 void SdOOXMLExportTest1::testTdf100348FontworkBitmapFill()
diff --git a/sd/qa/unit/import-tests.cxx b/sd/qa/unit/import-tests.cxx
index eff937566383..9aefdd4ecc7b 100644
--- a/sd/qa/unit/import-tests.cxx
+++ b/sd/qa/unit/import-tests.cxx
@@ -217,6 +217,7 @@ class SdImportTest : public SdModelTestBase
     void testShapeGlowEffectPPTXImpoer();
     void testShapeBlurPPTXImport();
     void testMirroredGraphic();
+    void testCropPositionGraphic();
 
     bool checkPattern(sd::DrawDocShellRef const & rDocRef, int nShapeNumber, std::vector<sal_uInt8>& rExpected);
     void testPatternImport();
@@ -346,6 +347,7 @@ class SdImportTest : public SdModelTestBase
     CPPUNIT_TEST(testShapeGlowEffectPPTXImpoer);
     CPPUNIT_TEST(testShapeBlurPPTXImport);
     CPPUNIT_TEST(testMirroredGraphic);
+    CPPUNIT_TEST(testCropPositionGraphic);
 
     CPPUNIT_TEST_SUITE_END();
 };
@@ -3324,6 +3326,20 @@ void SdImportTest::testMirroredGraphic()
     xDocShRef->DoClose();
 }
 
+void SdImportTest::testCropPositionGraphic()
+{
+    sd::DrawDocShellRef xDocShRef = loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/pptx/crop-position.pptx"), PPTX);
+    uno::Reference<beans::XPropertySet> xShape(getShapeFromPage(0, 0, xDocShRef), uno::UNO_SET_THROW);
+    CPPUNIT_ASSERT(xShape.is());
+    uno::Reference<graphic::XGraphic> xGraphic;
+    xShape->getPropertyValue("FillBitmap") >>= xGraphic;
+    CPPUNIT_ASSERT(xGraphic.is());
+    Graphic aGraphic(xGraphic);
+    BitmapEx aBitmap(aGraphic.GetBitmapEx());
+    CPPUNIT_ASSERT_EQUAL( Color(8682893), aBitmap.GetPixelColor( 0, 0 ));
+    xDocShRef->DoClose();
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
