Add FPDFPageObj_GetClipPath() API
This adds an API to expose the data inside CPDF_ClipPath.
Next to the initial getter, this also adds:
- FPDFClipPath_CountPaths() to get number of paths inside the clip path
- FPDFClipPath_CountPathSegments() to get the number of segments inside
one specific path
- FPDFClipPath_GetPathSegment() to get a FPDF_PATHSEGMENT, which can be
already analyzed using the existing FPDFPathSegment_*() APIs.
Bug: pdfium:1332
Change-Id: I6a8d99fd80d1df2c3e1c1406757f20d9f9873f0d
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/58310
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index 3276066..f3af839 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -431,6 +431,102 @@
VerifySavedDocument(612, 792, kLastMD5);
}
+TEST_F(FPDFEditEmbedderTest, ClipPath) {
+ // Load document with a clipped rectangle.
+ EXPECT_TRUE(OpenDocument("clip_path.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+
+ ASSERT_EQ(1, FPDFPage_CountObjects(page));
+
+ FPDF_PAGEOBJECT triangle = FPDFPage_GetObject(page, 0);
+ ASSERT_TRUE(triangle);
+
+ // Test that we got the expected triangle.
+ ASSERT_EQ(4, FPDFPath_CountSegments(triangle));
+
+ FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(triangle, 0);
+ float x;
+ float y;
+ EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+ EXPECT_EQ(10, x);
+ EXPECT_EQ(10, y);
+ EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+ EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+ segment = FPDFPath_GetPathSegment(triangle, 1);
+ EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+ EXPECT_EQ(25, x);
+ EXPECT_EQ(40, y);
+ EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+ EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+ segment = FPDFPath_GetPathSegment(triangle, 2);
+ EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+ EXPECT_EQ(40, x);
+ EXPECT_EQ(10, y);
+ EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+ EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+ segment = FPDFPath_GetPathSegment(triangle, 3);
+ EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+ EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
+
+ // Test FPDFPageObj_GetClipPath().
+ ASSERT_EQ(nullptr, FPDFPageObj_GetClipPath(nullptr));
+
+ FPDF_CLIPPATH clip_path = FPDFPageObj_GetClipPath(triangle);
+ ASSERT_TRUE(clip_path);
+
+ // Test FPDFClipPath_CountPaths().
+ ASSERT_EQ(-1, FPDFClipPath_CountPaths(nullptr));
+ ASSERT_EQ(1, FPDFClipPath_CountPaths(clip_path));
+
+ // Test FPDFClipPath_CountPathSegments().
+ ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(nullptr, 0));
+ ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, -1));
+ ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, 1));
+ ASSERT_EQ(4, FPDFClipPath_CountPathSegments(clip_path, 0));
+
+ // FPDFClipPath_GetPathSegment() negative testing.
+ ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(nullptr, 0, 0));
+ ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, -1, 0));
+ ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 1, 0));
+ ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 0, -1));
+ ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 0, 4));
+
+ // FPDFClipPath_GetPathSegment() positive testing.
+ segment = FPDFClipPath_GetPathSegment(clip_path, 0, 0);
+ EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+ EXPECT_EQ(10, x);
+ EXPECT_EQ(15, y);
+ EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
+ EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+ segment = FPDFClipPath_GetPathSegment(clip_path, 0, 1);
+ EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+ EXPECT_EQ(40, x);
+ EXPECT_EQ(15, y);
+ EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+ EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+ segment = FPDFClipPath_GetPathSegment(clip_path, 0, 2);
+ EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+ EXPECT_EQ(40, x);
+ EXPECT_EQ(35, y);
+ EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+ EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+ segment = FPDFClipPath_GetPathSegment(clip_path, 0, 3);
+ EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
+ EXPECT_EQ(10, x);
+ EXPECT_EQ(35, y);
+ EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
+ EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
+
+ UnloadPage(page);
+}
+
TEST_F(FPDFEditEmbedderTest, SetText) {
// Load document with some text.
EXPECT_TRUE(OpenDocument("hello_world.pdf"));
diff --git a/fpdfsdk/fpdf_transformpage.cpp b/fpdfsdk/fpdf_transformpage.cpp
index 9ad5982..59c5fb5 100644
--- a/fpdfsdk/fpdf_transformpage.cpp
+++ b/fpdfsdk/fpdf_transformpage.cpp
@@ -272,6 +272,59 @@
pPageObj->TransformGeneralState(matrix);
}
+FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV
+FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object) {
+ CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+ if (!pPageObj)
+ return nullptr;
+
+ return FPDFClipPathFromCPDFClipPath(&pPageObj->m_ClipPath);
+}
+
+FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path) {
+ CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
+ if (!pClipPath)
+ return -1;
+
+ return pClipPath->GetPathCount();
+}
+
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index) {
+ CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
+ if (!pClipPath)
+ return -1;
+
+ if (path_index < 0 ||
+ static_cast<size_t>(path_index) >= pClipPath->GetPathCount()) {
+ return -1;
+ }
+
+ return pdfium::CollectionSize<int>(
+ pClipPath->GetPath(path_index).GetPoints());
+}
+
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,
+ int path_index,
+ int segment_index) {
+ CPDF_ClipPath* pClipPath = CPDFClipPathFromFPDFClipPath(clip_path);
+ if (!pClipPath)
+ return nullptr;
+
+ if (path_index < 0 ||
+ static_cast<size_t>(path_index) >= pClipPath->GetPathCount()) {
+ return nullptr;
+ }
+
+ const std::vector<FX_PATHPOINT>& points =
+ pClipPath->GetPath(path_index).GetPoints();
+ if (!pdfium::IndexInBounds(points, segment_index))
+ return nullptr;
+
+ return FPDFPathSegmentFromFXPathPoint(&points[segment_index]);
+}
+
FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV FPDF_CreateClipPath(float left,
float bottom,
float right,
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 9f58b8b..cf1c14f 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -343,6 +343,10 @@
CHK(FPDFPage_GetThumbnailAsBitmap);
// fpdf_transformpage.h
+ CHK(FPDFClipPath_CountPathSegments);
+ CHK(FPDFClipPath_CountPaths);
+ CHK(FPDFClipPath_GetPathSegment);
+ CHK(FPDFPageObj_GetClipPath);
CHK(FPDFPageObj_TransformClipPath);
CHK(FPDFPage_GetArtBox);
CHK(FPDFPage_GetBleedBox);
diff --git a/public/fpdf_transformpage.h b/public/fpdf_transformpage.h
index a2eb6b0..38ef113 100644
--- a/public/fpdf_transformpage.h
+++ b/public/fpdf_transformpage.h
@@ -220,6 +220,51 @@
double e,
double f);
+// Experimental API.
+// Get the clip path of the page object.
+//
+// page object - Handle to a page object. Returned by e.g.
+// FPDFPage_GetObject().
+//
+// Caller does not take ownership of the returned FPDF_CLIPPATH. Instead, it
+// remains valid until FPDF_ClosePage() is called for the page containing
+// page_object.
+FPDF_EXPORT FPDF_CLIPPATH FPDF_CALLCONV
+FPDFPageObj_GetClipPath(FPDF_PAGEOBJECT page_object);
+
+// Experimental API.
+// Get number of paths inside |clip_path|.
+//
+// clip_path - handle to a clip_path.
+//
+// Returns the number of objects in |clip_path| or -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV FPDFClipPath_CountPaths(FPDF_CLIPPATH clip_path);
+
+// Experimental API.
+// Get number of segments inside one path of |clip_path|.
+//
+// clip_path - handle to a clip_path.
+// path_index - index into the array of paths of the clip path.
+//
+// Returns the number of segments or -1 on failure.
+FPDF_EXPORT int FPDF_CALLCONV
+FPDFClipPath_CountPathSegments(FPDF_CLIPPATH clip_path, int path_index);
+
+// Experimental API.
+// Get segment in one specific path of |clip_path| at index.
+//
+// clip_path - handle to a clip_path.
+// path_index - the index of a path.
+// segment_index - the index of a segment.
+//
+// Returns the handle to the segment, or NULL on failure. The caller does not
+// take ownership of the returned FPDF_CLIPPATH. Instead, it remains valid until
+// FPDF_ClosePage() is called for the page containing page_object.
+FPDF_EXPORT FPDF_PATHSEGMENT FPDF_CALLCONV
+FPDFClipPath_GetPathSegment(FPDF_CLIPPATH clip_path,
+ int path_index,
+ int segment_index);
+
/**
* Create a new clip path, with a rectangle inserted.
*
diff --git a/testing/resources/clip_path.in b/testing/resources/clip_path.in
new file mode 100644
index 0000000..fd13f8e
--- /dev/null
+++ b/testing/resources/clip_path.in
@@ -0,0 +1,39 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Kids [3 0 R]
+ /MediaBox [0 0 100 50]
+ /Count 1
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Contents 4 0 R
+ /Parent 2 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+10 15 m
+40 15 l
+40 35 l
+10 35 l
+W n
+0 0 1 RG
+10 10 m
+25 40 l
+40 10 l
+s
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/clip_path.pdf b/testing/resources/clip_path.pdf
new file mode 100644
index 0000000..8d27bcb
--- /dev/null
+++ b/testing/resources/clip_path.pdf
@@ -0,0 +1,50 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Kids [3 0 R]
+ /MediaBox [0 0 100 50]
+ /Count 1
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Contents 4 0 R
+ /Parent 2 0 R
+>>
+endobj
+4 0 obj <<
+ /Length 71
+>>
+stream
+10 15 m
+40 15 l
+40 35 l
+10 35 l
+W n
+0 0 1 RG
+10 10 m
+25 40 l
+40 10 l
+s
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000156 00000 n
+0000000225 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+347
+%%EOF