From 0dcf1f40652edd701d032227a742f6a63e6e3fae Mon Sep 17 00:00:00 2001 From: Henrique Nakashima Date: Thu, 21 Jun 2018 18:51:15 +0000 Subject: Do not save content stream if all page objects were removed from it. Bug: pdfium:1051 Change-Id: Ia990a47eeceb47fd2b15fe4ea7226861507484db Reviewed-on: https://pdfium-review.googlesource.com/35115 Reviewed-by: dsinclair Commit-Queue: Henrique Nakashima --- fpdfsdk/fpdf_edit_embeddertest.cpp | 344 ++++++++++++++++++++++++++++++++++++- 1 file changed, 342 insertions(+), 2 deletions(-) (limited to 'fpdfsdk/fpdf_edit_embeddertest.cpp') diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp index c78700ea67..f2690499d1 100644 --- a/fpdfsdk/fpdf_edit_embeddertest.cpp +++ b/fpdfsdk/fpdf_edit_embeddertest.cpp @@ -748,8 +748,6 @@ TEST_F(FPDFEditEmbeddertest, RemoveExistingPageObjectSplitStreamsLonely) { CloseSavedDocument(); } -// TODO(pdfium:1051): Extend this test to remove some elements and verify -// saving works. TEST_F(FPDFEditEmbeddertest, GetContentStream) { // Load document with some text split across streams. EXPECT_TRUE(OpenDocument("split_streams.pdf")); @@ -776,6 +774,348 @@ TEST_F(FPDFEditEmbeddertest, GetContentStream) { UnloadPage(page); } +TEST_F(FPDFEditEmbeddertest, RemoveAllFromStream) { + // Load document with some text split across streams. + EXPECT_TRUE(OpenDocument("split_streams.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Content stream 0: page objects 0-14. + // Content stream 1: page objects 15-17. + // Content stream 2: page object 18. + ASSERT_EQ(19, FPDFPage_CountObjects(page)); + + // Loop backwards because objects will being removed, which shifts the indexes + // after the removed position. + for (int i = 18; i >= 0; i--) { + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i); + ASSERT_TRUE(page_object); + CPDF_PageObject* cpdf_page_object = + CPDFPageObjectFromFPDFPageObject(page_object); + + // Empty content stream 1. + if (cpdf_page_object->GetContentStream() == 1) { + EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object)); + FPDFPageObj_Destroy(page_object); + } + } + + // Content stream 0: page objects 0-14. + // Content stream 2: page object 15. + ASSERT_EQ(16, FPDFPage_CountObjects(page)); + for (int i = 0; i < 16; i++) { + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i); + ASSERT_TRUE(page_object); + CPDF_PageObject* cpdf_page_object = + CPDFPageObjectFromFPDFPageObject(page_object); + if (i < 15) + EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i; + else + EXPECT_EQ(2, cpdf_page_object->GetContentStream()) << i; + } + + // Generate contents should remove the empty stream and update the page + // objects' contents stream indexes. + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + + // Content stream 0: page objects 0-14. + // Content stream 1: page object 15. + ASSERT_EQ(16, FPDFPage_CountObjects(page)); + for (int i = 0; i < 16; i++) { + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i); + ASSERT_TRUE(page_object); + CPDF_PageObject* cpdf_page_object = + CPDFPageObjectFromFPDFPageObject(page_object); + if (i < 15) + EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i; + else + EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i; + } + +#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ + const char kStream1RemovedMD5[] = "d2e21fbd5a6de563f619feeeb6163331"; +#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ + const char kStream1RemovedMD5[] = "b4140f203523e38793283a5943d8075b"; +#else + const char kStream1RemovedMD5[] = "e86a3efc160ede6cfcb1f59bcacf1105"; +#endif + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5); + } + + // Save the file + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + + // Re-open the file and check the page object count is still 16, and that + // content stream 1 was removed. + OpenSavedDocument(); + FPDF_PAGE saved_page = LoadSavedPage(0); + + // Content stream 0: page objects 0-14. + // Content stream 1: page object 15. + EXPECT_EQ(16, FPDFPage_CountObjects(saved_page)); + for (int i = 0; i < 16; i++) { + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, i); + ASSERT_TRUE(page_object); + CPDF_PageObject* cpdf_page_object = + CPDFPageObjectFromFPDFPageObject(page_object); + if (i < 15) + EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i; + else + EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i; + } + + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(saved_page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kStream1RemovedMD5); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); +} + +TEST_F(FPDFEditEmbeddertest, RemoveAllFromSingleStream) { + // Load document with a single stream. + EXPECT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Content stream 0: page objects 0-1. + ASSERT_EQ(2, FPDFPage_CountObjects(page)); + + // Loop backwards because objects will being removed, which shifts the indexes + // after the removed position. + for (int i = 1; i >= 0; i--) { + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i); + ASSERT_TRUE(page_object); + CPDF_PageObject* cpdf_page_object = + CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object)); + FPDFPageObj_Destroy(page_object); + } + + // No more objects in the stream + ASSERT_EQ(0, FPDFPage_CountObjects(page)); + + // Generate contents should remove the empty stream and update the page + // objects' contents stream indexes. + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + + ASSERT_EQ(0, FPDFPage_CountObjects(page)); + + const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c"; + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5); + } + + // Save the file + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + + // Re-open the file and check the page object count is still 0. + OpenSavedDocument(); + FPDF_PAGE saved_page = LoadSavedPage(0); + + EXPECT_EQ(0, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(saved_page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); +} + +TEST_F(FPDFEditEmbeddertest, RemoveFirstFromSingleStream) { + // Load document with a single stream. + EXPECT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Content stream 0: page objects 0-1. + ASSERT_EQ(2, FPDFPage_CountObjects(page)); + + // Remove first object. + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 0); + ASSERT_TRUE(page_object); + CPDF_PageObject* cpdf_page_object = + CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object)); + FPDFPageObj_Destroy(page_object); + + // One object left in the stream. + ASSERT_EQ(1, FPDFPage_CountObjects(page)); + page_object = FPDFPage_GetObject(page, 0); + ASSERT_TRUE(page_object); + cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + + // Still one object left in the stream. + ASSERT_EQ(1, FPDFPage_CountObjects(page)); + page_object = FPDFPage_GetObject(page, 0); + ASSERT_TRUE(page_object); + cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + +#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ + const char kFirstRemovedMD5[] = "af760c4702467cb1492a57fb8215efaa"; +#elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_ + const char kFirstRemovedMD5[] = "72be917349bf7004a5c39661fe1fc433"; +#else + const char kFirstRemovedMD5[] = "b76df015fe88009c3c342395df96abf1"; +#endif + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5); + } + + // Save the file + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + + // Re-open the file and check the page object count is still 0. + OpenSavedDocument(); + FPDF_PAGE saved_page = LoadSavedPage(0); + + ASSERT_EQ(1, FPDFPage_CountObjects(saved_page)); + page_object = FPDFPage_GetObject(saved_page, 0); + ASSERT_TRUE(page_object); + cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(saved_page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kFirstRemovedMD5); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); +} + +TEST_F(FPDFEditEmbeddertest, RemoveLastFromSingleStream) { + // Load document with a single stream. + EXPECT_TRUE(OpenDocument("hello_world.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Content stream 0: page objects 0-1. + ASSERT_EQ(2, FPDFPage_CountObjects(page)); + + // Remove last object + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, 1); + ASSERT_TRUE(page_object); + CPDF_PageObject* cpdf_page_object = + CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object)); + FPDFPageObj_Destroy(page_object); + + // One object left in the stream. + ASSERT_EQ(1, FPDFPage_CountObjects(page)); + page_object = FPDFPage_GetObject(page, 0); + ASSERT_TRUE(page_object); + cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + + // Still one object left in the stream. + ASSERT_EQ(1, FPDFPage_CountObjects(page)); + page_object = FPDFPage_GetObject(page, 0); + ASSERT_TRUE(page_object); + cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + +#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ + const char kLastRemovedMD5[] = "f8fbd14a048b9e2ea8e5f059f22a910e"; +#else + const char kLastRemovedMD5[] = "93dcc09055f87a2792c8e3065af99a1b"; +#endif + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5); + } + + // Save the file + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + + // Re-open the file and check the page object count is still 0. + OpenSavedDocument(); + FPDF_PAGE saved_page = LoadSavedPage(0); + + ASSERT_EQ(1, FPDFPage_CountObjects(saved_page)); + page_object = FPDFPage_GetObject(saved_page, 0); + ASSERT_TRUE(page_object); + cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object); + ASSERT_EQ(0, cpdf_page_object->GetContentStream()); + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(saved_page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kLastRemovedMD5); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); +} + +TEST_F(FPDFEditEmbeddertest, RemoveAllFromMultipleStreams) { + // Load document with some text. + EXPECT_TRUE(OpenDocument("hello_world_split_streams.pdf")); + FPDF_PAGE page = LoadPage(0); + ASSERT_TRUE(page); + + // Content stream 0: page objects 0-1. + // Content stream 1: page object 2. + ASSERT_EQ(3, FPDFPage_CountObjects(page)); + + // Loop backwards because objects will being removed, which shifts the indexes + // after the removed position. + for (int i = 2; i >= 0; i--) { + FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i); + ASSERT_TRUE(page_object); + ASSERT_TRUE(FPDFPage_RemoveObject(page, page_object)); + FPDFPageObj_Destroy(page_object); + } + + // No more objects in the page. + ASSERT_EQ(0, FPDFPage_CountObjects(page)); + + // Generate contents should remove the empty streams and update the page + // objects' contents stream indexes. + EXPECT_TRUE(FPDFPage_GenerateContent(page)); + + ASSERT_EQ(0, FPDFPage_CountObjects(page)); + + const char kAllRemovedMD5[] = "eee4600ac08b458ac7ac2320e225674c"; + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5); + } + + // Save the file + EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0)); + UnloadPage(page); + + // Re-open the file and check the page object count is still 0. + OpenSavedDocument(); + FPDF_PAGE saved_page = LoadSavedPage(0); + + EXPECT_EQ(0, FPDFPage_CountObjects(saved_page)); + { + ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(saved_page, nullptr, 0); + CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedMD5); + } + + CloseSavedPage(saved_page); + CloseSavedDocument(); +} + TEST_F(FPDFEditEmbeddertest, InsertPageObjectAndSave) { // Load document with some text. EXPECT_TRUE(OpenDocument("hello_world.pdf")); -- cgit v1.2.3