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());
 }
 

Reply via email to