sd/qa/unit/data/pdf/ClippedImages.pdf |binary sd/qa/unit/export-tests.cxx | 30 ++++++++++ svx/source/inc/svdpdf.hxx | 9 +++ svx/source/svdraw/svdpdf.cxx | 99 +++++++++++++++++++++++++++++----- 4 files changed, 124 insertions(+), 14 deletions(-)
New commits: commit fd7464c146fa2b4478df7b8f148f12a6a42873f8 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 15:56:36 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/+/193113 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Mike Kaganski <[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 12a31a9b08ac..7817c962e9a0 100644 --- a/sd/qa/unit/export-tests.cxx +++ b/sd/qa/unit/export-tests.cxx @@ -1177,6 +1177,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 2f226e3d0587..ab354ee28c5c 100644 --- a/svx/source/inc/svdpdf.hxx +++ b/svx/source/inc/svdpdf.hxx @@ -164,6 +164,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 6db5745806cb..6e6fbf4dea38 100644 --- a/svx/source/svdraw/svdpdf.cxx +++ b/svx/source/svdraw/svdpdf.cxx @@ -21,6 +21,7 @@ #include <config_features.h> #include <tools/UnitConversion.hxx> +#include <vcl/canvastools.hxx> #include <vcl/embeddedfontsmanager.hxx> #include <vcl/graph.hxx> #include <vcl/vectorgraphicdata.hxx> @@ -56,6 +57,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> @@ -1938,6 +1940,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*/) { @@ -1962,6 +1987,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); @@ -1972,6 +2025,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()); } commit 9858a04a0d991d91158f565efe68795b8b282d85 Author: Caolán McNamara <[email protected]> AuthorDate: Tue Oct 28 15:02:25 2025 +0000 Commit: Caolán McNamara <[email protected]> CommitDate: Wed Oct 29 15:56:30 2025 +0100 split out pdfium segments to polypolygon conversion Change-Id: Ib3af196943324a8ae543a1306c008060e53aa5a6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193101 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Mike Kaganski <[email protected]> diff --git a/svx/source/inc/svdpdf.hxx b/svx/source/inc/svdpdf.hxx index 56f2142b265a..2f226e3d0587 100644 --- a/svx/source/inc/svdpdf.hxx +++ b/svx/source/inc/svdpdf.hxx @@ -159,6 +159,11 @@ class ImpSdrPdfImport final void InsertObj(SdrObject* pObj, bool bScale = true); void MapScaling(); + void appendSegmentsToPolyPoly( + basegfx::B2DPolyPolygon& rPolyPoly, + const std::vector<std::unique_ptr<vcl::pdf::PDFiumPathSegment>>& rPathSegments, + const basegfx::B2DHomMatrix& rPathMatrix); + // #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 d95eed7a3c1f..6db5745806cb 100644 --- a/svx/source/svdraw/svdpdf.cxx +++ b/svx/source/svdraw/svdpdf.cxx @@ -1975,26 +1975,20 @@ void ImpSdrPdfImport::ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> co InsertObj(pGraf.get()); } -void ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject, - std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage, - int /*nPageObjectIndex*/) +void ImpSdrPdfImport::appendSegmentsToPolyPoly( + basegfx::B2DPolyPolygon& rPolyPoly, + const std::vector<std::unique_ptr<vcl::pdf::PDFiumPathSegment>>& rPathSegments, + const basegfx::B2DHomMatrix& rPathMatrix) { - auto aPathMatrix = pPageObject->getMatrix(); - - aPathMatrix *= maCurrentMatrix; - - basegfx::B2DPolyPolygon aPolyPoly; basegfx::B2DPolygon aPoly; std::vector<basegfx::B2DPoint> aBezier; - const int nSegments = pPageObject->getPathSegmentCount(); - for (int nSegmentIndex = 0; nSegmentIndex < nSegments; ++nSegmentIndex) + for (const auto& pPathSegment : rPathSegments) { - auto pPathSegment = pPageObject->getPathSegment(nSegmentIndex); if (pPathSegment != nullptr) { basegfx::B2DPoint aB2DPoint = pPathSegment->getPoint(); - aB2DPoint *= aPathMatrix; + aB2DPoint *= rPathMatrix; const bool bClose = pPathSegment->isClosed(); if (bClose) @@ -2024,7 +2018,7 @@ void ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> con // New Poly. if (aPoly.count() > 0) { - aPolyPoly.append(aPoly, 1); + rPolyPoly.append(aPoly, 1); aPoly.clear(); } @@ -2048,9 +2042,29 @@ void ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> con if (aPoly.count() > 0) { - aPolyPoly.append(aPoly, 1); + rPolyPoly.append(aPoly, 1); aPoly.clear(); } +} + +void ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject, + std::unique_ptr<vcl::pdf::PDFiumPage> const& pPage, + int /*nPageObjectIndex*/) +{ + auto aPathMatrix = pPageObject->getMatrix(); + + aPathMatrix *= maCurrentMatrix; + + basegfx::B2DPolyPolygon aPolyPoly; + + std::vector<std::unique_ptr<vcl::pdf::PDFiumPathSegment>> aPathSegments; + const int nSegments = pPageObject->getPathSegmentCount(); + for (int nSegmentIndex = 0; nSegmentIndex < nSegments; ++nSegmentIndex) + { + aPathSegments.push_back(pPageObject->getPathSegment(nSegmentIndex)); + } + + appendSegmentsToPolyPoly(aPolyPoly, aPathSegments, aPathMatrix); const basegfx::B2DHomMatrix aTransform( basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
