diff --git a/oox/source/drawingml/fillproperties.cxx b/oox/source/drawingml/fillproperties.cxx index 93fea51194ef..9270201920b3 100644 --- a/oox/source/drawingml/fillproperties.cxx +++ b/oox/source/drawingml/fillproperties.cxx @@ -88,67 +88,55 @@ Reference< XGraphic > lclRotateGraphic(uno::Reference const & return aReturnGraphic.GetXGraphic(); } -void lclCalculateCropPercentage(uno::Reference const & xGraphic, geometry::IntegerRectangle2D &aFillRect) +using Quotients = std::tuple; +Quotients getQuotients(geometry::IntegerRectangle2D aRelRect, double hDiv, double vDiv) { - ::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; - if (nOrigWidth == 0) - { - nOrigWidth = 1; - } - sal_Int32 nOrigHeight = (nScaledHeight * (100000 - aFillRect.Y1 - aFillRect.Y2)) / 100000; - if (nOrigHeight == 0) - { - nOrigHeight = 1; - } - - 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; + return { aRelRect.X1 / hDiv, aRelRect.Y1 / vDiv, aRelRect.X2 / hDiv, aRelRect.Y2 / vDiv }; } -// Crops a piece of the bitmap. Takes negative aFillRect values. Negative values means "crop", -// positive values means "grow" bitmap with empty spaces. lclCropGraphic doesn't handle growing. -Reference< XGraphic > lclCropGraphic(uno::Reference const & xGraphic, geometry::IntegerRectangle2D aFillRect) +// ECMA-376 Part 1 20.1.8.55 srcRect (Source Rectangle) +std::optional CropQuotientsFromSrcRect(geometry::IntegerRectangle2D aSrcRect) +{ + // Currently the following precondition is guaranteed in GraphicProperties::pushToPropMap + assert(aSrcRect.X1 >= 0 && aSrcRect.X2 >= 0 && aSrcRect.Y1 >= 0 && aSrcRect.Y2 >= 0); + if (aSrcRect.X1 + aSrcRect.X2 >= 100'000 || aSrcRect.Y1 + aSrcRect.Y2 >= 100'000) + return {}; // Cropped everything + return getQuotients(aSrcRect, 100'000.0, 100'000.0); +} + +// ECMA-376 Part 1 20.1.8.30 fillRect (Fill Rectangle) +std::optional CropQuotientsFromFillRect(geometry::IntegerRectangle2D aFillRect) +{ + // Currently the following precondition is guaranteed in FillProperties::pushToPropMap + assert(aFillRect.X1 <= 0 && aFillRect.X2 <= 0 && aFillRect.Y1 <= 0 && aFillRect.Y2 <= 0); + // Negative divisor and negative relative offset give positive value wanted in lclCropGraphic + return getQuotients(aFillRect, -100'000.0 + aFillRect.X1 + aFillRect.X2, + -100'000.0 + aFillRect.Y1 + aFillRect.Y2); +} + +// Crops a piece of the bitmap. lclCropGraphic doesn't handle growing. +Reference lclCropGraphic(uno::Reference const& xGraphic, + std::optional quotients) { ::Graphic aGraphic(xGraphic); - ::Graphic aReturnGraphic; - assert (aGraphic.GetType() == GraphicType::Bitmap); - BitmapEx aBitmapEx(aGraphic.GetBitmapEx()); + BitmapEx aBitmapEx; + if (quotients) + { + aBitmapEx = aGraphic.GetBitmapEx(); - sal_Int32 nOrigHeight = aBitmapEx.GetSizePixel().Height(); - sal_Int32 nHeight = nOrigHeight; - sal_Int32 nTopCorr = nOrigHeight * -1 * static_cast(aFillRect.Y1) / 100000; - nHeight += nTopCorr; - sal_Int32 nBottomCorr = nOrigHeight * -1 * static_cast(aFillRect.Y2) / 100000; - nHeight += nBottomCorr; + const Size bmpSize = aBitmapEx.GetSizePixel(); + const auto& [qx1, qy1, qx2, qy2] = *quotients; + const tools::Long l = std::round(bmpSize.Width() * qx1); + const tools::Long t = std::round(bmpSize.Height() * qy1); + const tools::Long r = std::round(bmpSize.Width() * qx2); + const tools::Long b = std::round(bmpSize.Height() * qy2); - sal_Int32 nOrigWidth = aBitmapEx.GetSizePixel().Width(); - sal_Int32 nWidth = nOrigWidth; - sal_Int32 nLeftCorr = nOrigWidth * -1 * static_cast(aFillRect.X1) / 100000; - nWidth += nLeftCorr; - sal_Int32 nRightCorr = nOrigWidth * -1 * static_cast(aFillRect.X2) / 100000; - nWidth += nRightCorr; + aBitmapEx.Crop({ l, t, bmpSize.Width() - r - 1, bmpSize.Height() - b - 1 }); + } - aBitmapEx.Scale(Size(nWidth, nHeight)); - aBitmapEx.Crop(tools::Rectangle(Point(nLeftCorr, nTopCorr), Size(nOrigWidth, nOrigHeight))); - - aReturnGraphic = ::Graphic(aBitmapEx); + ::Graphic aReturnGraphic(aBitmapEx); aReturnGraphic.setOriginURL(aGraphic.getOriginURL()); return aReturnGraphic.GetXGraphic(); @@ -818,7 +806,7 @@ void FillProperties::pushToPropMap( ShapePropertyMap& rPropMap, if(bIsCustomShape && bHasCropValues && bNeedCrop) { - xGraphic = lclCropGraphic(xGraphic, aFillRect); + xGraphic = lclCropGraphic(xGraphic, CropQuotientsFromFillRect(aFillRect)); rPropMap.setProperty(ShapeProperty::FillBitmap, xGraphic); } } @@ -932,9 +920,7 @@ void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelpe if(mbIsCustomShape && bHasCropValues && bNeedCrop) { - geometry::IntegerRectangle2D aCropRect = oClipRect; - lclCalculateCropPercentage(xGraphic, aCropRect); - xGraphic = lclCropGraphic(xGraphic, aCropRect); + xGraphic = lclCropGraphic(xGraphic, CropQuotientsFromSrcRect(oClipRect)); } } } diff --git a/sd/qa/unit/data/pptx/croppedTo0.pptx b/sd/qa/unit/data/pptx/croppedTo0.pptx new file mode 100644 index 000000000000..fecf53559b1f Binary files /dev/null and b/sd/qa/unit/data/pptx/croppedTo0.pptx differ diff --git a/sd/qa/unit/import-tests2.cxx b/sd/qa/unit/import-tests2.cxx index 3bddc580c07d..8ada9e682f39 100644 --- a/sd/qa/unit/import-tests2.cxx +++ b/sd/qa/unit/import-tests2.cxx @@ -135,6 +135,7 @@ public: void testTdf112209(); void testTdf128596(); void testDefaultTabStop(); + void testCropToZero(); CPPUNIT_TEST_SUITE(SdImportTest2); @@ -204,6 +205,7 @@ public: CPPUNIT_TEST(testTdf112209); CPPUNIT_TEST(testTdf128596); CPPUNIT_TEST(testDefaultTabStop); + CPPUNIT_TEST(testCropToZero); CPPUNIT_TEST_SUITE_END(); }; @@ -1293,7 +1295,7 @@ void SdImportTest2::testTdf134210() Graphic aGraphic(xGraphic); BitmapEx aBitmap(aGraphic.GetBitmapEx()); - CPPUNIT_ASSERT_EQUAL(Color(0x60563e), aBitmap.GetPixelColor(0, 0)); + CPPUNIT_ASSERT_EQUAL(Color(0x605741), aBitmap.GetPixelColor(0, 0)); xDocShRef->DoClose(); } @@ -2035,6 +2037,12 @@ void SdImportTest2::testDefaultTabStop() xDocShRef->DoClose(); } +void SdImportTest2::testCropToZero() +{ + // Must not crash because of division by zero + loadURL(m_directories.getURLFromSrc(u"/sd/qa/unit/data/pptx/croppedTo0.pptx"), PPTX); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTest2); CPPUNIT_PLUGIN_IMPLEMENT();