From 35841fa4e3dbf8f9146f78def048c4a287894a8a Mon Sep 17 00:00:00 2001 From: Henrique Nakashima Date: Thu, 15 Mar 2018 15:25:16 +0000 Subject: Create FPDFPage_RemoveObject(). This call removes a page object from a page. We currently offer an API to insert these objects, but not to remove. Bug: pdfium:1037 Change-Id: I35ff596f9e7c87a39051f0cb1de40a5bec40fee5 Reviewed-on: https://pdfium-review.googlesource.com/28492 Reviewed-by: dsinclair Commit-Queue: Henrique Nakashima --- core/fpdfapi/page/cpdf_page.cpp | 14 ++++++ core/fpdfapi/page/cpdf_page.h | 1 + fpdfsdk/fpdfannot_embeddertest.cpp | 6 +++ fpdfsdk/fpdfedit_embeddertest.cpp | 90 ++++++++++++++++++++++++++++++++++++++ fpdfsdk/fpdfeditpage.cpp | 19 ++++++++ fpdfsdk/fpdfview_c_api_test.c | 1 + public/fpdf_edit.h | 13 ++++++ 7 files changed, 144 insertions(+) diff --git a/core/fpdfapi/page/cpdf_page.cpp b/core/fpdfapi/page/cpdf_page.cpp index ba93f4a7d3..0c8d63de71 100644 --- a/core/fpdfapi/page/cpdf_page.cpp +++ b/core/fpdfapi/page/cpdf_page.cpp @@ -180,6 +180,20 @@ int CPDF_Page::GetPageRotation() const { return (rotate < 0) ? (rotate + 4) : rotate; } +bool CPDF_Page::RemoveObject(CPDF_PageObject* pPageObj) { + pdfium::FakeUniquePtr p(pPageObj); + + auto* pPageObjectList = GetPageObjectList(); + auto it = + std::find(std::begin(*pPageObjectList), std::end(*pPageObjectList), p); + if (it == std::end(*pPageObjectList)) + return false; + + it->release(); + pPageObjectList->erase(it); + return true; +} + bool GraphicsData::operator<(const GraphicsData& other) const { if (fillAlpha != other.fillAlpha) return fillAlpha < other.fillAlpha; diff --git a/core/fpdfapi/page/cpdf_page.h b/core/fpdfapi/page/cpdf_page.h index c7aa12e474..995d99cf4e 100644 --- a/core/fpdfapi/page/cpdf_page.h +++ b/core/fpdfapi/page/cpdf_page.h @@ -54,6 +54,7 @@ class CPDF_Page : public CPDF_PageObjectHolder { View* GetView() const { return m_pView; } void SetView(View* pView) { m_pView = pView; } + bool RemoveObject(CPDF_PageObject* pPageObj); private: void StartParse(); diff --git a/fpdfsdk/fpdfannot_embeddertest.cpp b/fpdfsdk/fpdfannot_embeddertest.cpp index 70e184dd41..4e32b19ff9 100644 --- a/fpdfsdk/fpdfannot_embeddertest.cpp +++ b/fpdfsdk/fpdfannot_embeddertest.cpp @@ -575,6 +575,7 @@ TEST_F(FPDFAnnotEmbeddertest, AddAndModifyPath) { // Check that this annotation has one path object and retrieve it. EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get())); + ASSERT_EQ(32, FPDFPage_CountObjects(page)); FPDF_PAGEOBJECT path = FPDFAnnot_GetObject(annot.get(), 1); EXPECT_FALSE(path); path = FPDFAnnot_GetObject(annot.get(), 0); @@ -601,6 +602,10 @@ TEST_F(FPDFAnnotEmbeddertest, AddAndModifyPath) { EXPECT_TRUE(FPDFAnnot_AppendObject(annot.get(), dot)); EXPECT_EQ(2, FPDFAnnot_GetObjectCount(annot.get())); + // The object is in the annontation, not in the page, so the page object + // array should not change. + ASSERT_EQ(32, FPDFPage_CountObjects(page)); + // Check that the page with an annotation with two paths renders correctly. { std::unique_ptr bitmap = @@ -611,6 +616,7 @@ TEST_F(FPDFAnnotEmbeddertest, AddAndModifyPath) { // Delete the newly added path object. EXPECT_TRUE(FPDFAnnot_RemoveObject(annot.get(), 1)); EXPECT_EQ(1, FPDFAnnot_GetObjectCount(annot.get())); + ASSERT_EQ(32, FPDFPage_CountObjects(page)); } // Check that the page renders the same as before. diff --git a/fpdfsdk/fpdfedit_embeddertest.cpp b/fpdfsdk/fpdfedit_embeddertest.cpp index 3fc6cae8b4..ec403c74b3 100644 --- a/fpdfsdk/fpdfedit_embeddertest.cpp +++ b/fpdfsdk/fpdfedit_embeddertest.cpp @@ -381,6 +381,96 @@ TEST_F(FPDFEditEmbeddertest, AddPaths) { VerifySavedDocument(612, 792, kLastMD5); } +TEST_F(FPDFEditEmbeddertest, RemovePageObject) { + // Load document with some text. + EXPECT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + +// Show how the original file looks like. +#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ + const char kOriginalMD5[] = "b90475ca64d1348c3bf5e2b77ad9187a"; +#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ + const char kOriginalMD5[] = "e5a6fa28298db07484cd922f3e210c88"; +#else + const char kOriginalMD5[] = "2baa4c0e1758deba1b9c908e1fbd04ed"; +#endif + { + std::unique_ptr page_bitmap = + RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5); + } + + // Get the "Hello, world!" text object and remove it. + ASSERT_EQ(2, FPDFPage_CountObjects(page)); + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0); + ASSERT_TRUE(page_object); + EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object)); + +// Verify the "Hello, world!" text is gone. +#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ + const char kRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa"; +#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ + const char kRemovedMD5[] = "72be917349bf7004a5c39661fe1fc433"; +#else + const char kRemovedMD5[] = "b76df015fe88009c3c342395df96abf1"; +#endif + { + std::unique_ptr page_bitmap = + RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kRemovedMD5); + } + ASSERT_EQ(1, FPDFPage_CountObjects(page)); + + UnloadPage(page); + FPDFPageObj_Destroy(page_object); +} + +TEST_F(FPDFEditEmbeddertest, AddAndRemovePaths) { + // Start with a blank page. + FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792); + ASSERT_TRUE(page); + + // Render the blank page and verify it's a blank bitmap. + const char kBlankMD5[] = "1940568c9ba33bac5d0b1ee9558c76b3"; + { + std::unique_ptr page_bitmap = + RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5); + } + ASSERT_EQ(0, FPDFPage_CountObjects(page)); + + // Add a red rectangle. + FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20); + ASSERT_TRUE(red_rect); + EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255)); + EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0)); + FPDFPage_InsertObject(page, red_rect); + const char kRedRectangleMD5[] = "66d02eaa6181e2c069ce2ea99beda497"; + { + std::unique_ptr page_bitmap = + RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleMD5); + } + EXPECT_EQ(1, FPDFPage_CountObjects(page)); + + // Remove rectangle and verify it does not render anymore and the bitmap is + // back to a blank one. + EXPECT_TRUE(FPDFPage_RemoveObject(page, red_rect)); + { + std::unique_ptr page_bitmap = + RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 612, 792, kBlankMD5); + } + EXPECT_EQ(0, FPDFPage_CountObjects(page)); + + // Trying to remove an object not in the page should return false. + EXPECT_FALSE(FPDFPage_RemoveObject(page, red_rect)); + + FPDF_ClosePage(page); + FPDFPageObj_Destroy(red_rect); +} + TEST_F(FPDFEditEmbeddertest, PathsPoints) { CreateNewDocument(); FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document_); diff --git a/fpdfsdk/fpdfeditpage.cpp b/fpdfsdk/fpdfeditpage.cpp index ca2cf3fb6e..800613348b 100644 --- a/fpdfsdk/fpdfeditpage.cpp +++ b/fpdfsdk/fpdfeditpage.cpp @@ -174,10 +174,25 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertObject(FPDF_PAGE page, if (!IsPageObject(pPage)) return; pPageObj->SetDirty(true); + + // TODO(hnakashima): Move into CPDF_Page. pPage->GetPageObjectList()->push_back(std::move(pPageObjHolder)); CalcBoundingBox(pPageObj); } +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +FPDFPage_RemoveObject(FPDF_PAGE page, FPDF_PAGEOBJECT page_obj) { + CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_obj); + if (!pPageObj) + return false; + + CPDF_Page* pPage = CPDFPageFromFPDFPage(page); + if (!IsPageObject(pPage)) + return false; + + return pPage->RemoveObject(pPageObj); +} + FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObject(FPDF_PAGE page) { return FPDFPage_CountObjects(page); } @@ -186,6 +201,8 @@ FPDF_EXPORT int FPDF_CALLCONV FPDFPage_CountObjects(FPDF_PAGE page) { CPDF_Page* pPage = CPDFPageFromFPDFPage(page); if (!IsPageObject(pPage)) return -1; + + // TODO(hnakashima): Move into CPDF_Page. return pdfium::CollectionSize(*pPage->GetPageObjectList()); } @@ -194,6 +211,8 @@ FPDF_EXPORT FPDF_PAGEOBJECT FPDF_CALLCONV FPDFPage_GetObject(FPDF_PAGE page, CPDF_Page* pPage = CPDFPageFromFPDFPage(page); if (!IsPageObject(pPage)) return nullptr; + + // TODO(hnakashima): Move into CPDF_Page. return pPage->GetPageObjectList()->GetPageObjectByIndex(index); } diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c index 193050e84a..2a0b230ca5 100644 --- a/fpdfsdk/fpdfview_c_api_test.c +++ b/fpdfsdk/fpdfview_c_api_test.c @@ -129,6 +129,7 @@ int CheckPDFiumCApi() { CHK(FPDFPage_GetRotation); CHK(FPDFPage_SetRotation); CHK(FPDFPage_InsertObject); + CHK(FPDFPage_RemoveObject); CHK(FPDFPage_CountObject); CHK(FPDFPage_CountObjects); CHK(FPDFPage_GetObject); diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h index 54735a3bde..1e84e8f7ff 100644 --- a/public/fpdf_edit.h +++ b/public/fpdf_edit.h @@ -148,6 +148,19 @@ FPDF_EXPORT void FPDF_CALLCONV FPDFPage_SetRotation(FPDF_PAGE page, int rotate); FPDF_EXPORT void FPDF_CALLCONV FPDFPage_InsertObject(FPDF_PAGE page, FPDF_PAGEOBJECT page_obj); +// Experimental API. +// Remove |page_obj| from |page|. +// +// page - handle to a page +// page_obj - handle to a page object to be removed. +// +// Returns TRUE on success. +// +// Ownership is transferred to the caller. Call FPDFPageObj_Destroy() to free +// it. +FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV +FPDFPage_RemoveObject(FPDF_PAGE page, FPDF_PAGEOBJECT page_obj); + // Get number of page objects inside |page|. // // page - handle to a page. -- cgit v1.2.3