Create API for adding content marks and setting their params.

This CL creates the following new functions in the public API:
- FPDFPageObj_AddMark
- FPDFPageObjMark_SetIntParam
- FPDFPageObjMark_SetStringParam

Bug: pdfium:1037
Change-Id: Icabf3fdd8e8153b9156bab807a3708d38a9365d8
Reviewed-on: https://pdfium-review.googlesource.com/37330
Commit-Queue: Henrique Nakashima <hnakashima@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_contentmark.cpp b/core/fpdfapi/page/cpdf_contentmark.cpp
index 6021dc8..912cc0e 100644
--- a/core/fpdfapi/page/cpdf_contentmark.cpp
+++ b/core/fpdfapi/page/cpdf_contentmark.cpp
@@ -29,7 +29,12 @@
   return m_pMarkData->CountItems();
 }
 
-const CPDF_ContentMarkItem& CPDF_ContentMark::GetItem(size_t i) const {
+CPDF_ContentMarkItem* CPDF_ContentMark::GetItem(size_t i) {
+  ASSERT(i < CountItems());
+  return m_pMarkData->GetItem(i);
+}
+
+const CPDF_ContentMarkItem* CPDF_ContentMark::GetItem(size_t i) const {
   ASSERT(i < CountItems());
   return m_pMarkData->GetItem(i);
 }
@@ -42,7 +47,7 @@
 }
 
 void CPDF_ContentMark::AddMark(ByteString name,
-                               const CPDF_Dictionary* pDict,
+                               CPDF_Dictionary* pDict,
                                bool bDirect) {
   if (!m_pMarkData)
     m_pMarkData.Reset(new MarkData());
@@ -70,9 +75,13 @@
   return m_Marks.size();
 }
 
-const CPDF_ContentMarkItem& CPDF_ContentMark::MarkData::GetItem(
+CPDF_ContentMarkItem* CPDF_ContentMark::MarkData::GetItem(size_t index) {
+  return m_Marks[index].Get();
+}
+
+const CPDF_ContentMarkItem* CPDF_ContentMark::MarkData::GetItem(
     size_t index) const {
-  return *m_Marks[index];
+  return m_Marks[index].Get();
 }
 
 int CPDF_ContentMark::MarkData::GetMarkedContentID() const {
@@ -85,7 +94,7 @@
 }
 
 void CPDF_ContentMark::MarkData::AddMark(ByteString name,
-                                         const CPDF_Dictionary* pDict,
+                                         CPDF_Dictionary* pDict,
                                          bool bDirect) {
   auto pItem = pdfium::MakeRetain<CPDF_ContentMarkItem>();
   pItem->SetName(std::move(name));
diff --git a/core/fpdfapi/page/cpdf_contentmark.h b/core/fpdfapi/page/cpdf_contentmark.h
index ac21151..e0ae1bf 100644
--- a/core/fpdfapi/page/cpdf_contentmark.h
+++ b/core/fpdfapi/page/cpdf_contentmark.h
@@ -24,9 +24,12 @@
   std::unique_ptr<CPDF_ContentMark> Clone();
   int GetMarkedContentID() const;
   size_t CountItems() const;
-  const CPDF_ContentMarkItem& GetItem(size_t i) const;
 
-  void AddMark(ByteString name, const CPDF_Dictionary* pDict, bool bDirect);
+  // The returned pointer is never null.
+  CPDF_ContentMarkItem* GetItem(size_t i);
+  const CPDF_ContentMarkItem* GetItem(size_t i) const;
+
+  void AddMark(ByteString name, CPDF_Dictionary* pDict, bool bDirect);
   void DeleteLastMark();
 
  private:
@@ -37,12 +40,11 @@
     ~MarkData() override;
 
     size_t CountItems() const;
-    const CPDF_ContentMarkItem& GetItem(size_t index) const;
+    CPDF_ContentMarkItem* GetItem(size_t index);
+    const CPDF_ContentMarkItem* GetItem(size_t index) const;
 
     int GetMarkedContentID() const;
-    void AddMark(ByteString name,
-                 const CPDF_Dictionary* pDict,
-                 bool bDictNeedClone);
+    void AddMark(ByteString name, CPDF_Dictionary* pDict, bool bDictNeedClone);
     void DeleteLastMark();
 
    private:
diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.cpp b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
index 4f56eea..dd9f7e8 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.cpp
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
@@ -26,6 +26,18 @@
   }
 }
 
+CPDF_Dictionary* CPDF_ContentMarkItem::GetParam() {
+  switch (m_ParamType) {
+    case PropertiesDict:
+      return m_pPropertiesDict.Get();
+    case DirectDict:
+      return m_pDirectDict.get();
+    case None:
+    default:
+      return nullptr;
+  }
+}
+
 bool CPDF_ContentMarkItem::HasMCID() const {
   const CPDF_Dictionary* pDict = GetParam();
   return pDict && pDict->KeyExist("MCID");
@@ -37,7 +49,7 @@
   m_pDirectDict = std::move(pDict);
 }
 
-void CPDF_ContentMarkItem::SetPropertiesDict(const CPDF_Dictionary* pDict) {
+void CPDF_ContentMarkItem::SetPropertiesDict(CPDF_Dictionary* pDict) {
   m_ParamType = PropertiesDict;
   m_pPropertiesDict = pDict;
 }
diff --git a/core/fpdfapi/page/cpdf_contentmarkitem.h b/core/fpdfapi/page/cpdf_contentmarkitem.h
index 72548ce..343db9d 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.h
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.h
@@ -27,17 +27,18 @@
   ByteString GetName() const { return m_MarkName; }
   ParamType GetParamType() const { return m_ParamType; }
   const CPDF_Dictionary* GetParam() const;
+  CPDF_Dictionary* GetParam();
   bool HasMCID() const;
 
   void SetName(const ByteString& name) { m_MarkName = name; }
   void SetDirectDict(std::unique_ptr<CPDF_Dictionary> pDict);
-  void SetPropertiesDict(const CPDF_Dictionary* pDict);
+  void SetPropertiesDict(CPDF_Dictionary* pDict);
 
  private:
   ByteString m_MarkName;
   ParamType m_ParamType = None;
-  UnownedPtr<const CPDF_Dictionary> m_pPropertiesDict;
-  std::unique_ptr<const CPDF_Dictionary> m_pDirectDict;
+  UnownedPtr<CPDF_Dictionary> m_pPropertiesDict;
+  std::unique_ptr<CPDF_Dictionary> m_pDirectDict;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_CONTENTMARKITEM_H_
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index ba7388c..01fb347 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -603,7 +603,7 @@
 
 void CPDF_StreamContentParser::Handle_BeginMarkedContent_Dictionary() {
   ByteString tag = GetString(1);
-  const CPDF_Object* pProperty = GetObject(0);
+  CPDF_Object* pProperty = GetObject(0);
   if (!pProperty)
     return;
 
@@ -614,7 +614,7 @@
       return;
     bDirect = false;
   }
-  if (const CPDF_Dictionary* pDict = pProperty->AsDictionary()) {
+  if (CPDF_Dictionary* pDict = pProperty->AsDictionary()) {
     std::unique_ptr<CPDF_ContentMark> new_marks =
         m_ContentMarksStack.top()->Clone();
     new_marks->AddMark(std::move(tag), pDict, bDirect);
diff --git a/core/fpdfdoc/cpdf_occontext.cpp b/core/fpdfdoc/cpdf_occontext.cpp
index 2aac213..727696b 100644
--- a/core/fpdfdoc/cpdf_occontext.cpp
+++ b/core/fpdfdoc/cpdf_occontext.cpp
@@ -182,10 +182,10 @@
 
 bool CPDF_OCContext::CheckObjectVisible(const CPDF_PageObject* pObj) {
   for (size_t i = 0; i < pObj->m_ContentMark.CountItems(); ++i) {
-    const CPDF_ContentMarkItem& item = pObj->m_ContentMark.GetItem(i);
-    if (item.GetName() == "OC" &&
-        item.GetParamType() == CPDF_ContentMarkItem::PropertiesDict &&
-        !CheckOCGVisible(item.GetParam())) {
+    const CPDF_ContentMarkItem* item = pObj->m_ContentMark.GetItem(i);
+    if (item->GetName() == "OC" &&
+        item->GetParamType() == CPDF_ContentMarkItem::PropertiesDict &&
+        !CheckOCGVisible(item->GetParam())) {
       return false;
     }
   }
diff --git a/core/fpdftext/cpdf_textpage.cpp b/core/fpdftext/cpdf_textpage.cpp
index 4c31964..2894160 100644
--- a/core/fpdftext/cpdf_textpage.cpp
+++ b/core/fpdftext/cpdf_textpage.cpp
@@ -846,8 +846,8 @@
   bool bExist = false;
   const CPDF_Dictionary* pDict = nullptr;
   for (size_t i = 0; i < nContentMark; ++i) {
-    const CPDF_ContentMarkItem& item = pTextObj->m_ContentMark.GetItem(i);
-    pDict = item.GetParam();
+    const CPDF_ContentMarkItem* item = pTextObj->m_ContentMark.GetItem(i);
+    pDict = item->GetParam();
     if (!pDict)
       continue;
     const CPDF_String* temp = ToString(pDict->GetObjectFor("ActualText"));
@@ -862,7 +862,7 @@
   if (m_pPreTextObj) {
     const CPDF_ContentMark& mark = m_pPreTextObj->m_ContentMark;
     if (mark.CountItems() == nContentMark &&
-        mark.GetItem(nContentMark - 1).GetParam() == pDict) {
+        mark.GetItem(nContentMark - 1)->GetParam() == pDict) {
       return FPDFText_MarkedContent::Done;
     }
   }
@@ -904,8 +904,8 @@
 
   WideString actText;
   for (size_t n = 0; n < nContentMark; n++) {
-    const CPDF_ContentMarkItem& item = pTextObj->m_ContentMark.GetItem(n);
-    const CPDF_Dictionary* pDict = item.GetParam();
+    const CPDF_ContentMarkItem* item = pTextObj->m_ContentMark.GetItem(n);
+    const CPDF_Dictionary* pDict = item->GetParam();
     if (pDict)
       actText = pDict->GetUnicodeTextFor("ActualText");
   }
diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h
index 88e0530..4fd85cf 100644
--- a/fpdfsdk/cpdfsdk_helpers.h
+++ b/fpdfsdk/cpdfsdk_helpers.h
@@ -139,12 +139,12 @@
 }
 
 inline FPDF_PAGEOBJECTMARK FPDFPageObjectMarkFromCPDFContentMarkItem(
-    const CPDF_ContentMarkItem* mark) {
+    CPDF_ContentMarkItem* mark) {
   return reinterpret_cast<FPDF_PAGEOBJECTMARK>(mark);
 }
-inline const CPDF_ContentMarkItem* CPDFContentMarkItemFromFPDFPageObjectMark(
+inline CPDF_ContentMarkItem* CPDFContentMarkItemFromFPDFPageObjectMark(
     FPDF_PAGEOBJECTMARK mark) {
-  return reinterpret_cast<const CPDF_ContentMarkItem*>(mark);
+  return reinterpret_cast<CPDF_ContentMarkItem*>(mark);
 }
 
 inline FPDF_PAGERANGE FPDFPageRangeFromCPDFArray(const CPDF_Array* range) {
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index a490162..a9eca92 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -2021,6 +2021,83 @@
   VerifySavedDocument(612, 792, md5);
 }
 
+TEST_F(FPDFEditEmbeddertest, AddMarkedText) {
+  // Start with a blank page.
+  FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
+
+  const CPDF_Font* stock_font = CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
+  const uint8_t* data = stock_font->GetFont()->GetFontData();
+  const uint32_t size = stock_font->GetFont()->GetSize();
+  ScopedFPDFFont font(
+      FPDFText_LoadFont(document(), data, size, FPDF_FONT_TRUETYPE, 0));
+  ASSERT_TRUE(font.get());
+
+  // Add some text to the page.
+  FPDF_PAGEOBJECT text_object =
+      FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
+
+  EXPECT_TRUE(text_object);
+  std::unique_ptr<unsigned short, pdfium::FreeDeleter> text1 =
+      GetFPDFWideString(L"I am testing my loaded font, WEE.");
+  EXPECT_TRUE(FPDFText_SetText(text_object, text1.get()));
+  FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
+  FPDFPage_InsertObject(page, text_object);
+
+  // Add a mark with the tag "TestMarkName" to that text.
+  EXPECT_EQ(0, FPDFPageObj_CountMarks(text_object));
+  FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(text_object, "TestMarkName");
+  EXPECT_TRUE(mark);
+  EXPECT_EQ(1, FPDFPageObj_CountMarks(text_object));
+  EXPECT_EQ(mark, FPDFPageObj_GetMark(text_object, 0));
+  char buffer[256];
+  EXPECT_GT(FPDFPageObjMark_GetName(mark, buffer, 256), 0u);
+  std::wstring name =
+      GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+  EXPECT_EQ(L"TestMarkName", name);
+
+  // Add parameters:
+  // - int "IntKey" : 42
+  // - string "StringKey": "StringValue"
+  EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark));
+  EXPECT_TRUE(FPDFPageObjMark_SetIntParam(document(), mark, "IntKey", 42));
+  EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), mark, "StringKey",
+                                             "StringValue"));
+  EXPECT_EQ(2, FPDFPageObjMark_CountParams(mark));
+
+  // Check the two parameters can be retrieved.
+  EXPECT_EQ(FPDF_OBJECT_NUMBER,
+            FPDFPageObjMark_GetParamValueTypeByKey(mark, "IntKey"));
+  int int_value;
+  EXPECT_TRUE(
+      FPDFPageObjMark_GetParamIntValueByKey(mark, "IntKey", &int_value));
+  EXPECT_EQ(42, int_value);
+
+  EXPECT_EQ(FPDF_OBJECT_STRING,
+            FPDFPageObjMark_GetParamValueTypeByKey(mark, "StringKey"));
+  unsigned long out_buffer_len;
+  EXPECT_TRUE(FPDFPageObjMark_GetParamStringValueByKey(
+      mark, "StringKey", buffer, 256, &out_buffer_len));
+  EXPECT_GT(out_buffer_len, 0u);
+  name = GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+  EXPECT_EQ(L"StringValue", name);
+
+// Render and check the bitmap is the expected one.
+#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
+  const char md5[] = "17d2b6cd574cf66170b09c8927529a94";
+#else
+  const char md5[] = "70592859010ffbf532a2237b8118bcc4";
+#endif
+  {
+    ScopedFPDFBitmap page_bitmap = RenderPageWithFlags(page, nullptr, 0);
+    CompareBitmap(page_bitmap.get(), 612, 792, md5);
+  }
+
+  FPDF_ClosePage(page);
+
+  // TODO(pdfium:1118): Save, then re-open the file and check the changes were
+  // kept in the saved .pdf.
+}
+
 TEST_F(FPDFEditEmbeddertest, ExtractImageBitmap) {
   ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
   FPDF_PAGE page = LoadPage(0);
diff --git a/fpdfsdk/fpdf_editpage.cpp b/fpdfsdk/fpdf_editpage.cpp
index d275559..8a12008 100644
--- a/fpdfsdk/fpdf_editpage.cpp
+++ b/fpdfsdk/fpdf_editpage.cpp
@@ -125,6 +125,30 @@
   return nullptr;
 }
 
+CPDF_Dictionary* GetOrCreateMarkParamsDict(FPDF_DOCUMENT document,
+                                           FPDF_PAGEOBJECTMARK mark) {
+  CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
+  if (!pDoc)
+    return nullptr;
+
+  CPDF_ContentMarkItem* pMarkItem =
+      CPDFContentMarkItemFromFPDFPageObjectMark(mark);
+  if (!pMarkItem)
+    return nullptr;
+
+  CPDF_Dictionary* pParams = pMarkItem->GetParam();
+
+  // If the Params dict does not exist, create a new one.
+  if (!pParams) {
+    auto new_dict =
+        pdfium::MakeUnique<CPDF_Dictionary>(pDoc->GetByteStringPool());
+    pParams = new_dict.get();
+    pMarkItem->SetDirectDict(std::move(new_dict));
+  }
+
+  return pParams;
+}
+
 unsigned int GetUnsignedAlpha(float alpha) {
   return static_cast<unsigned int>(alpha * 255.f + 0.5f);
 }
@@ -293,7 +317,19 @@
   if (index >= mark->CountItems())
     return nullptr;
 
-  return FPDFPageObjectMarkFromCPDFContentMarkItem(&mark->GetItem(index));
+  return FPDFPageObjectMarkFromCPDFContentMarkItem(mark->GetItem(index));
+}
+
+FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
+FPDFPageObj_AddMark(FPDF_PAGEOBJECT page_object, FPDF_BYTESTRING name) {
+  if (!page_object)
+    return nullptr;
+
+  auto* mark = &CPDFPageObjectFromFPDFPageObject(page_object)->m_ContentMark;
+  mark->AddMark(name, nullptr, true);
+  unsigned long index = mark->CountItems() - 1;
+
+  return FPDFPageObjectMarkFromCPDFContentMarkItem(mark->GetItem(index));
 }
 
 FPDF_EXPORT unsigned long FPDF_CALLCONV
@@ -456,6 +492,32 @@
   return false;
 }
 
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetIntParam(FPDF_DOCUMENT document,
+                            FPDF_PAGEOBJECTMARK mark,
+                            FPDF_BYTESTRING key,
+                            int value) {
+  CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark);
+  if (!pParams)
+    return false;
+
+  pParams->SetNewFor<CPDF_Number>(key, value);
+  return true;
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetStringParam(FPDF_DOCUMENT document,
+                               FPDF_PAGEOBJECTMARK mark,
+                               FPDF_BYTESTRING key,
+                               FPDF_BYTESTRING value) {
+  CPDF_Dictionary* pParams = GetOrCreateMarkParamsDict(document, mark);
+  if (!pParams)
+    return false;
+
+  pParams->SetNewFor<CPDF_String>(key, value, false);
+  return true;
+}
+
 FPDF_EXPORT int FPDF_CALLCONV FPDFPageObj_GetType(FPDF_PAGEOBJECT pageObject) {
   if (!pageObject)
     return FPDF_PAGEOBJ_UNKNOWN;
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 51b5884..53d5a77 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -145,6 +145,9 @@
     CHK(FPDFPageObjMark_GetParamStringValueByKey);
     CHK(FPDFPageObjMark_GetParamValueType);
     CHK(FPDFPageObjMark_GetParamValueTypeByKey);
+    CHK(FPDFPageObjMark_SetIntParam);
+    CHK(FPDFPageObjMark_SetStringParam);
+    CHK(FPDFPageObj_AddMark);
     CHK(FPDFPageObj_CountMarks);
     CHK(FPDFPageObj_CreateNewPath);
     CHK(FPDFPageObj_CreateNewRect);
diff --git a/public/fpdf_edit.h b/public/fpdf_edit.h
index 380a0a2..b85537d 100644
--- a/public/fpdf_edit.h
+++ b/public/fpdf_edit.h
@@ -317,6 +317,19 @@
 FPDFPageObj_GetMark(FPDF_PAGEOBJECT page_object, unsigned long index);
 
 // Experimental API.
+// Add a new content mark to a |page_object|.
+//
+//   page_object - handle to a page object.
+//   name        - the name (tag) of the mark.
+//
+// Returns the handle to the content mark, or NULL on failure. The handle is
+// still owned by the library, and it should not be freed directly. It becomes
+// invalid if the page object is destroyed, either directly or indirectly by
+// unloading the page.
+FPDF_EXPORT FPDF_PAGEOBJECTMARK FPDF_CALLCONV
+FPDFPageObj_AddMark(FPDF_PAGEOBJECT page_object, FPDF_BYTESTRING name);
+
+// Experimental API.
 // Get name of a content mark. |buffer| is only modified if |buflen| is longer
 // than the length of the name.
 //
@@ -441,6 +454,40 @@
                                          unsigned long buflen,
                                          unsigned long* out_buflen);
 
+// Experimental API.
+// Set the value of an int property in a content mark by key. If a parameter
+// with key |key| exists, its value is set to |value|. Otherwise, it is added as
+// a new parameter.
+//
+//   document - handle to the document.
+//   mark     - handle to a content mark.
+//   key      - string key of the property.
+//   value    - int value to set.
+//
+// Returns TRUE if the operation succeeded, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetIntParam(FPDF_DOCUMENT document,
+                            FPDF_PAGEOBJECTMARK mark,
+                            FPDF_BYTESTRING key,
+                            int value);
+
+// Experimental API.
+// Set the value of a string property in a content mark by key. If a parameter
+// with key |key| exists, its value is set to |value|. Otherwise, it is added as
+// a new parameter.
+//
+//   document - handle to the document.
+//   mark     - handle to a content mark.
+//   key      - string key of the property.
+//   value    - string value to set.
+//
+// Returns TRUE if the operation succeeded, FALSE otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFPageObjMark_SetStringParam(FPDF_DOCUMENT document,
+                               FPDF_PAGEOBJECTMARK mark,
+                               FPDF_BYTESTRING key,
+                               FPDF_BYTESTRING value);
+
 // Load an image from a JPEG image file and then set it into |image_object|.
 //
 //   pages        - pointer to the start of all loaded pages, may be NULL.
diff --git a/public/fpdfview.h b/public/fpdfview.h
index de0421e..a35e34a 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -48,7 +48,7 @@
 typedef struct fpdf_page_t__* FPDF_PAGE;
 typedef struct fpdf_pagelink_t__* FPDF_PAGELINK;
 typedef struct fpdf_pageobject_t__* FPDF_PAGEOBJECT;  // (text, path, etc.)
-typedef const struct fpdf_pageobjectmark_t__* FPDF_PAGEOBJECTMARK;
+typedef struct fpdf_pageobjectmark_t__* FPDF_PAGEOBJECTMARK;
 typedef const struct fpdf_pagerange_t__* FPDF_PAGERANGE;
 typedef const struct fpdf_pathsegment_t* FPDF_PATHSEGMENT;
 typedef void* FPDF_RECORDER;  // Passed into skia.