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

Reply via email to