sd/qa/unit/data/pdf/ClippedImages.pdf |binary sd/qa/unit/export-tests.cxx | 30 +++++++++++++++++ svx/source/inc/svdpdf.hxx | 4 ++ svx/source/svdraw/svdpdf.cxx | 57 ++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+)
New commits: commit 68c5c606bcb7fba0f6cf7ea4c30900ccec4366a2 Author: Caolán McNamara <[email protected]> AuthorDate: Tue Oct 28 17:38:07 2025 +0000 Commit: Caolán McNamara <[email protected]> CommitDate: Wed Oct 29 16:13:16 2025 +0100 take into account clipping for pdf graphics at least for graphics that are entirely clipped out. Change-Id: I2e8b15209a8a0e2e6777601b8d853ad323e5828e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193116 Tested-by: Jenkins Reviewed-by: Caolán McNamara <[email protected]> diff --git a/sd/qa/unit/data/pdf/ClippedImages.pdf b/sd/qa/unit/data/pdf/ClippedImages.pdf new file mode 100644 index 000000000000..58d75f9e95b0 Binary files /dev/null and b/sd/qa/unit/data/pdf/ClippedImages.pdf differ diff --git a/sd/qa/unit/export-tests.cxx b/sd/qa/unit/export-tests.cxx index 1fc61f3b9f29..baaef5f55173 100644 --- a/sd/qa/unit/export-tests.cxx +++ b/sd/qa/unit/export-tests.cxx @@ -1185,6 +1185,36 @@ CPPUNIT_TEST_FIXTURE(SdExportTest, testExplodedPdfGrayscaleImageUnderInvisibleTe CPPUNIT_ASSERT_MESSAGE("Shape should be Invisible", !bVisible); } +CPPUNIT_TEST_FIXTURE(SdExportTest, testExplodedPdfClippedImages) +{ + auto pPdfium = vcl::pdf::PDFiumLibrary::get(); + if (!pPdfium) + return; + UsePdfium aGuard; + + loadFromFile(u"pdf/ClippedImages.pdf"); + + setFilterOptions("{\"DecomposePDF\":{\"type\":\"boolean\",\"value\":\"true\"}}"); + setImportFilterName(u"OpenDocument Drawing Flat XML"_ustr); + saveAndReload(u"OpenDocument Drawing Flat XML"_ustr); + + uno::Reference<drawing::XShapes> xGroupShape(getShapeFromPage(0, 0), uno::UNO_QUERY); + CPPUNIT_ASSERT(xGroupShape.is()); + + uno::Reference<beans::XPropertySet> xGraphicShape1(xGroupShape->getByIndex(0), uno::UNO_QUERY); + CPPUNIT_ASSERT(xGraphicShape1.is()); + bool bVisible(true); + xGraphicShape1->getPropertyValue(u"Visible"_ustr) >>= bVisible; + CPPUNIT_ASSERT_MESSAGE("1st Graphic should be Visible", bVisible); + + // before the fix the clip for this graphic wasn't taken into account so it was visiblem + // not it is detected as entirely clipped out and toggled to invisible + uno::Reference<beans::XPropertySet> xGraphicShape2(xGroupShape->getByIndex(1), uno::UNO_QUERY); + CPPUNIT_ASSERT(xGraphicShape2.is()); + xGraphicShape2->getPropertyValue(u"Visible"_ustr) >>= bVisible; + CPPUNIT_ASSERT_MESSAGE("2nd Graphic should be Invisible", !bVisible); +} + CPPUNIT_TEST_FIXTURE(SdExportTest, testExplodedPdfMissingFontVersion) { auto pPdfium = vcl::pdf::PDFiumLibrary::get(); diff --git a/svx/source/inc/svdpdf.hxx b/svx/source/inc/svdpdf.hxx index f3eee410d920..38a977ea33b5 100644 --- a/svx/source/inc/svdpdf.hxx +++ b/svx/source/inc/svdpdf.hxx @@ -162,6 +162,10 @@ class ImpSdrPdfImport final const std::vector<std::unique_ptr<vcl::pdf::PDFiumPathSegment>>& rPathSegments, const basegfx::B2DHomMatrix& rPathMatrix); + basegfx::B2DPolyPolygon GetClip(const std::unique_ptr<vcl::pdf::PDFiumClipPath>& rClipPath, + const basegfx::B2DHomMatrix& rPathMatrix, + const basegfx::B2DHomMatrix& rTransform); + // #i73407# reformulation to use new B2DPolygon classes bool CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon& rPolyPolygon); diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx index cb358c636e6c..505e011bda1e 100644 --- a/svx/source/svdraw/svdpdf.cxx +++ b/svx/source/svdraw/svdpdf.cxx @@ -20,6 +20,7 @@ #include <svdpdf.hxx> #include <tools/UnitConversion.hxx> +#include <vcl/canvastools.hxx> #include <vcl/embeddedfontsmanager.hxx> #include <vcl/graph.hxx> #include <vcl/vectorgraphicdata.hxx> @@ -55,6 +56,7 @@ #include <svx/svdetc.hxx> #include <svl/itemset.hxx> #include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> #include <tools/helpers.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> @@ -1950,6 +1952,29 @@ void ImpSdrPdfImport::MapScaling() mnMapScalingOfs = nCount; } +basegfx::B2DPolyPolygon +ImpSdrPdfImport::GetClip(const std::unique_ptr<vcl::pdf::PDFiumClipPath>& pClipPath, + const basegfx::B2DHomMatrix& rPathMatrix, + const basegfx::B2DHomMatrix& rTransform) +{ + basegfx::B2DPolyPolygon aClipPolyPoly; + + int nClipPathCount = pClipPath->getPathCount(); + for (int i = 0; i < nClipPathCount; ++i) + { + std::vector<std::unique_ptr<vcl::pdf::PDFiumPathSegment>> aPathSegments; + const int nSegments = pClipPath->getPathSegmentCount(i); + for (int nSegmentIndex = 0; nSegmentIndex < nSegments; ++nSegmentIndex) + aPathSegments.push_back(pClipPath->getPathSegment(i, nSegmentIndex)); + + appendSegmentsToPolyPoly(aClipPolyPoly, aPathSegments, rPathMatrix); + } + + aClipPolyPoly.transform(rTransform); + + return aClipPolyPoly; +} + void ImpSdrPdfImport::ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject, int /*nPageObjectIndex*/) { @@ -1974,6 +1999,34 @@ void ImpSdrPdfImport::ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> co float right = aBounds.getMaxX(); // Upside down. float top = aBounds.getMaxY(); + + bool bEntirelyClippedOut = false; + + auto aPathMatrix = pPageObject->getMatrix(); + + aPathMatrix *= maCurrentMatrix; + + const basegfx::B2DHomMatrix aTransform( + basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y())); + + basegfx::B2DPolyPolygon aClipPolyPoly + = GetClip(pPageObject->getClipPath(), aPathMatrix, aTransform); + + if (aClipPolyPoly.count()) + { + // Clip against ClipRegion, use same conversions and transformation on + // the graphic bounds as were applied to the clipping polypolygon + const tools::Rectangle aRect = PointsToLogic(aBounds.getMinX(), aBounds.getMaxX(), + aBounds.getMinY(), aBounds.getMaxY()); + basegfx::B2DPolyPolygon aGraphicBounds( + basegfx::utils::createPolygonFromRect(vcl::unotools::b2DRectangleFromRectangle(aRect))); + aGraphicBounds.transform(aTransform); + const basegfx::B2DPolyPolygon aClippedBounds(basegfx::utils::clipPolyPolygonOnPolyPolygon( + aGraphicBounds, aClipPolyPoly, true, false)); + // completely clipped out + bEntirelyClippedOut = aClippedBounds.getB2DRange().isEmpty(); + } + tools::Rectangle aRect = PointsToLogic(left, right, top, bottom); aRect.AdjustRight(1); aRect.AdjustBottom(1); @@ -1984,6 +2037,10 @@ void ImpSdrPdfImport::ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> co // This action is not creating line and fill, set directly, do not use SetAttributes(..) pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE)); pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE)); + + if (bEntirelyClippedOut) + pGraf->SetVisible(false); + InsertObj(pGraf.get()); }
