Add FPDFFormObj_RemoveObject API to remove objects from forms
This CL adds a new API function to allow removing page objects from form
objects. Previously, it was not possible to remove specific page objects
(like images) that were part of a Form XObject. Now,
FPDFFormObj_RemoveObject can be used to remove objects from form objects
similar to how FPDFPage_RemoveObject works for objects directly on a
page.
Bug: 42271087
Change-Id: I08e647b91cefb786d71ee846e3f00c8883fd25db
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/131232
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_formobject.h b/core/fpdfapi/page/cpdf_formobject.h
index a2c8006..49c76c2 100644
--- a/core/fpdfapi/page/cpdf_formobject.h
+++ b/core/fpdfapi/page/cpdf_formobject.h
@@ -30,6 +30,7 @@
void CalcBoundingBox();
const CPDF_Form* form() const { return form_.get(); }
+ CPDF_Form* form() { return form_.get(); }
const CFX_Matrix& form_matrix() const { return form_matrix_; }
void SetFormMatrix(const CFX_Matrix& matrix);
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index b4adf6c..30f8a54 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -5472,3 +5472,40 @@
EXPECT_EQ(widths_array->GetIntegerAt(0), 1);
EXPECT_EQ(widths_array->GetIntegerAt(2), 5);
}
+
+TEST_F(FPDFEditEmbedderTest, FormModifyObject) {
+ ASSERT_TRUE(OpenDocument("form_object_with_image.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+
+ // Since we know the document structure, assert the exact object count
+ constexpr int kExpectedPageObjectCount = 1;
+ ASSERT_EQ(kExpectedPageObjectCount, FPDFPage_CountObjects(page));
+
+ FPDF_PAGEOBJECT form_obj = FPDFPage_GetObject(page, 0);
+ ASSERT_TRUE(form_obj);
+ ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form_obj));
+
+ // Get the count of objects in the form
+ constexpr int kExpectedFormObjectCount = 1;
+ ASSERT_EQ(kExpectedFormObjectCount, FPDFFormObj_CountObjects(form_obj));
+
+ constexpr int kImageObjectIndex = 0;
+ FPDF_PAGEOBJECT image_obj =
+ FPDFFormObj_GetObject(form_obj, kImageObjectIndex);
+ ASSERT_TRUE(image_obj);
+ ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(image_obj));
+
+ ASSERT_FALSE(FPDFFormObj_RemoveObject(nullptr, image_obj));
+ ASSERT_FALSE(FPDFFormObj_RemoveObject(form_obj, nullptr));
+ ASSERT_FALSE(FPDFFormObj_RemoveObject(nullptr, nullptr));
+ ASSERT_TRUE(FPDFFormObj_RemoveObject(form_obj, image_obj));
+
+ // After removing the image, the form should have no objects left
+ ASSERT_EQ(0, FPDFFormObj_CountObjects(form_obj));
+
+ FPDFPageObj_Destroy(image_obj);
+ ASSERT_TRUE(FPDFPage_GenerateContent(page));
+
+ UnloadPage(page);
+}
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index 026b790..ea74abf 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -155,6 +155,13 @@
return pFormObject ? pFormObject->form() : nullptr;
}
+// Non-const version needed for FPDFFormObj_RemoveObject
+CPDF_PageObjectHolder* MutableCPDFPageObjHolderFromFPDFFormObject(
+ FPDF_PAGEOBJECT page_object) {
+ CPDF_FormObject* form_object = CPDFFormObjectFromFPDFPageObject(page_object);
+ return form_object ? form_object->form() : nullptr;
+}
+
} // namespace
FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV FPDF_CreateNewDocument() {
@@ -1157,3 +1164,20 @@
return FPDFPageObjectFromCPDFPageObject(
pObjectList->GetPageObjectByIndex(index));
}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFFormObj_RemoveObject(FPDF_PAGEOBJECT form_object,
+ FPDF_PAGEOBJECT page_object) {
+ auto* object_holder = MutableCPDFPageObjHolderFromFPDFFormObject(form_object);
+ if (!object_holder) {
+ return false;
+ }
+
+ CPDF_PageObject* cpage_object = CPDFPageObjectFromFPDFPageObject(page_object);
+ if (!cpage_object) {
+ return false;
+ }
+
+ // Caller takes ownership of the removed page object
+ return !!object_holder->RemovePageObject(cpage_object).release();
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index a2f3633..6048a94 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -178,6 +178,7 @@
CHK(FPDFFont_GetWeight);
CHK(FPDFFormObj_CountObjects);
CHK(FPDFFormObj_GetObject);
+ CHK(FPDFFormObj_RemoveObject);
CHK(FPDFGlyphPath_CountGlyphSegments);
CHK(FPDFGlyphPath_GetGlyphPathSegment);
CHK(FPDFImageObj_GetBitmap);
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 5dc11c4..f826075 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -1617,6 +1617,21 @@
FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV
FPDFFormObj_GetObject(FPDF_PAGEOBJECT form_object, unsigned long index);
+// Experimental API.
+//
+// Remove |page_object| from |form_object|.
+//
+// form_object - handle to a form object.
+// page_object - handle to a page object to be removed from the form.
+//
+// Returns TRUE on success.
+//
+// Ownership of the removed |page_object| is transferred to the caller.
+// Call FPDFPageObj_Destroy() on the removed page_object to free it.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFFormObj_RemoveObject(FPDF_PAGEOBJECT form_object,
+ FPDF_PAGEOBJECT page_object);
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus