Create API to remove a content mark from a page object.

- FPDFPageObj_RemoveMark()

Bug: pdfium:1037
Change-Id: I7ff320261d64e3ead45375ccc72301e7c64dd6e3
Reviewed-on: https://pdfium-review.googlesource.com/37710
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Henrique Nakashima <hnakashima@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_contentmark.cpp b/core/fpdfapi/page/cpdf_contentmark.cpp
index 4932ee6..615d31f 100644
--- a/core/fpdfapi/page/cpdf_contentmark.cpp
+++ b/core/fpdfapi/page/cpdf_contentmark.cpp
@@ -73,6 +73,10 @@
   m_pMarkData->AddMarkWithPropertiesDict(std::move(name), pDict, property_name);
 }
 
+bool CPDF_ContentMark::RemoveMark(CPDF_ContentMarkItem* pMarkItem) {
+  return m_pMarkData && m_pMarkData->RemoveMark(pMarkItem);
+}
+
 void CPDF_ContentMark::EnsureMarkDataExists() {
   if (!m_pMarkData)
     m_pMarkData = pdfium::MakeRetain<MarkData>();
@@ -160,6 +164,16 @@
   m_Marks.push_back(pItem);
 }
 
+bool CPDF_ContentMark::MarkData::RemoveMark(CPDF_ContentMarkItem* pMarkItem) {
+  for (auto it = m_Marks.begin(); it != m_Marks.end(); ++it) {
+    if (it->Get() == pMarkItem) {
+      m_Marks.erase(it);
+      return true;
+    }
+  }
+  return false;
+}
+
 void CPDF_ContentMark::MarkData::DeleteLastMark() {
   if (!m_Marks.empty())
     m_Marks.pop_back();
diff --git a/core/fpdfapi/page/cpdf_contentmark.h b/core/fpdfapi/page/cpdf_contentmark.h
index 8dc9837..4f678af 100644
--- a/core/fpdfapi/page/cpdf_contentmark.h
+++ b/core/fpdfapi/page/cpdf_contentmark.h
@@ -35,6 +35,7 @@
   void AddMarkWithPropertiesDict(ByteString name,
                                  CPDF_Dictionary* pDict,
                                  const ByteString& property_name);
+  bool RemoveMark(CPDF_ContentMarkItem* pMarkItem);
   void DeleteLastMark();
   size_t FindFirstDifference(const CPDF_ContentMark* other) const;
 
@@ -56,6 +57,7 @@
     void AddMarkWithPropertiesDict(ByteString name,
                                    CPDF_Dictionary* pDict,
                                    const ByteString& property_name);
+    bool RemoveMark(CPDF_ContentMarkItem* pMarkItem);
     void DeleteLastMark();
 
    private:
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index ba790de..9a03a4d 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -692,6 +692,55 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFEditEmbeddertest, RemoveMarks) {
+  // Load document with some text.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  constexpr int kExpectedObjectCount = 19;
+  CheckMarkCounts(page, 1, kExpectedObjectCount, 8, 4, 9, 1);
+
+  // Remove all "Prime" content marks.
+  for (int i = 0; i < kExpectedObjectCount; ++i) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+
+    int mark_count = FPDFPageObj_CountMarks(page_object);
+    for (int j = mark_count - 1; j >= 0; --j) {
+      FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
+
+      char buffer[256];
+      ASSERT_GT(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer)), 0u);
+      std::wstring name =
+          GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+      if (name == L"Prime") {
+        // Remove mark.
+        EXPECT_TRUE(FPDFPageObj_RemoveMark(page_object, mark));
+
+        // Verify there is now one fewer mark in the page object.
+        EXPECT_EQ(mark_count - 1, FPDFPageObj_CountMarks(page_object));
+      }
+    }
+  }
+
+  // Verify there are 0 "Prime" content marks now.
+  CheckMarkCounts(page, 1, kExpectedObjectCount, 0, 4, 9, 1);
+
+  // Save the file.
+  EXPECT_TRUE(FPDFPage_GenerateContent(page));
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  UnloadPage(page);
+
+  // Re-open the file and check the prime marks are not there anymore.
+  OpenSavedDocument();
+  FPDF_PAGE saved_page = LoadSavedPage(0);
+
+  CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 0, 4, 9, 1);
+
+  CloseSavedPage(saved_page);
+  CloseSavedDocument();
+}
+
 TEST_F(FPDFEditEmbeddertest, MaintainMarkedObjects) {
   // Load document with some text.
   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index 3abdcdf..808e330 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -326,6 +326,21 @@
   return FPDFPageObjectMarkFromCPDFContentMarkItem(mark->GetItem(index));
 }
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_RemoveMark(FPDF_PAGEOBJECT page_object, FPDF_PAGEOBJECTMARK mark) {
+  CPDF_PageObject* pPageObj = CPDFPageObjectFromFPDFPageObject(page_object);
+  CPDF_ContentMarkItem* pMarkItem =
+      CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+  if (!pPageObj || !pMarkItem)
+    return false;
+
+  bool result = pPageObj->m_ContentMark.RemoveMark(pMarkItem);
+  if (result)
+    pPageObj->SetDirty(true);
+
+  return result;
+}
+
 FPDF_EXPORT unsigned long FPDF_CALLCONV
 FPDFPageObjMark_GetName(FPDF_PAGEOBJECTMARK mark,
                         void* buffer,
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index a89bc2e..96c9bfe 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -162,6 +162,7 @@
     CHK(FPDFPageObj_HasTransparency);
     CHK(FPDFPageObj_NewImageObj);
     CHK(FPDFPageObj_NewTextObj);
+    CHK(FPDFPageObj_RemoveMark);
     CHK(FPDFPageObj_SetBlendMode);
     CHK(FPDFPageObj_SetFillColor);
     CHK(FPDFPageObj_SetLineCap);
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index e71b4ed..e18f1cd 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -330,6 +330,17 @@
 FPDFPageObj_AddMark(FPDF_PAGEOBJECT page_object, FPDF_BYTESTRING name);
 
 // Experimental API.
+// Removes a content |mark| from a |page_object|.
+// The mark handle will be invalid after the removal.
+//
+//   page_object - handle to a page object.
+//   mark        - handle to a content mark in that object to remove.
+//
+// Returns TRUE if the operation succeeded, FALSE if it failed.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObj_RemoveMark(FPDF_PAGEOBJECT page_object, FPDF_PAGEOBJECTMARK mark);
+
+// Experimental API.
 // Get name of a content mark. |buffer| is only modified if |buflen| is longer
 // than the length of the name.
 //