Wrapper class for annotation dictionary + CPDF_Form

Simple wrapper class for annotation + its path parsing form object. This
will be used in APIs for extracting and setting annotation paths.

Bug=pdfium:737

Change-Id: I2e8131672d087613213735295c6d01e377b956e7
Reviewed-on: https://pdfium-review.googlesource.com/6730
Reviewed-by: dsinclair <dsinclair@chromium.org>
Commit-Queue: dsinclair <dsinclair@chromium.org>
diff --git a/fpdfsdk/fpdfannot.cpp b/fpdfsdk/fpdfannot.cpp
index 90927fd..6d47b04 100644
--- a/fpdfsdk/fpdfannot.cpp
+++ b/fpdfsdk/fpdfannot.cpp
@@ -8,6 +8,7 @@
 
 #include <utility>
 
+#include "core/fpdfapi/page/cpdf_form.h"
 #include "core/fpdfapi/page/cpdf_page.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
@@ -20,6 +21,8 @@
 #include "core/fpdfdoc/cpvt_generateap.h"
 #include "fpdfsdk/fsdk_define.h"
 
+namespace {
+
 // These checks ensure the consistency of annotation subtype values across core/
 // and public.
 static_assert(static_cast<int>(CPDF_Annot::Subtype::UNKNOWN) ==
@@ -98,6 +101,46 @@
                   FPDF_ANNOT_XFAWIDGET,
               "CPDF_Annot::XFAWIDGET value mismatch");
 
+class CPDF_AnnotContext {
+ public:
+  CPDF_AnnotContext(CPDF_Dictionary* pAnnotDict,
+                    CPDF_Page* pPage,
+                    CPDF_Stream* pStream)
+      : m_pAnnotDict(pAnnotDict), m_pPage(pPage) {
+    SetForm(pStream);
+  }
+  ~CPDF_AnnotContext() {}
+
+  bool HasForm() const { return !!m_pAnnotForm; }
+
+  void SetForm(CPDF_Stream* pStream) {
+    if (!pStream)
+      return;
+
+    m_pAnnotForm = pdfium::MakeUnique<CPDF_Form>(
+        m_pPage->m_pDocument.Get(), m_pPage->m_pResources.Get(), pStream);
+    m_pAnnotForm->ParseContent(nullptr, nullptr, nullptr);
+  }
+
+  CPDF_Form* GetForm() const { return m_pAnnotForm.get(); }
+  CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict.Get(); }
+
+ private:
+  std::unique_ptr<CPDF_Form> m_pAnnotForm;
+  CFX_UnownedPtr<CPDF_Dictionary> m_pAnnotDict;
+  CFX_UnownedPtr<CPDF_Page> m_pPage;
+};
+
+FPDF_ANNOTATION FPDFAnnotationFromCPDFAnnotContext(CPDF_AnnotContext* pAnnot) {
+  return static_cast<FPDF_ANNOTATION>(pAnnot);
+}
+
+CPDF_AnnotContext* CPDFAnnotContextFromFPDFAnnotation(FPDF_ANNOTATION annot) {
+  return static_cast<CPDF_AnnotContext*>(annot);
+}
+
+}  // namespace
+
 DLLEXPORT FPDF_BOOL STDCALL
 FPDFAnnot_IsSupportedSubtype(FPDF_ANNOTATION_SUBTYPE subtype) {
   return subtype == FPDF_ANNOT_CIRCLE || subtype == FPDF_ANNOT_HIGHLIGHT ||
@@ -121,8 +164,11 @@
   pDict->SetNewFor<CPDF_Name>("Subtype",
                               CPDF_Annot::AnnotSubtypeToString(
                                   static_cast<CPDF_Annot::Subtype>(subtype)));
-  if (annot)
-    *annot = FPDFAnnotationFromCPDFDictionary(pDict.get());
+  if (annot) {
+    auto pNewAnnot =
+        pdfium::MakeUnique<CPDF_AnnotContext>(pDict.get(), pPage, nullptr);
+    *annot = FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release());
+  }
 
   CPDF_Array* pAnnotList = pPage->m_pFormDict->GetArrayFor("Annots");
   if (!pAnnotList)
@@ -153,13 +199,19 @@
     return false;
 
   CPDF_Dictionary* pDict = ToDictionary(pAnnots->GetDirectObjectAt(index));
-  *annot = FPDFAnnotationFromCPDFDictionary(pDict);
-  return *annot ? true : false;
+  auto pNewAnnot = pdfium::MakeUnique<CPDF_AnnotContext>(pDict, pPage, nullptr);
+  *annot = FPDFAnnotationFromCPDFAnnotContext(pNewAnnot.release());
+  return true;
+}
+
+DLLEXPORT void STDCALL FPDFPage_CloseAnnot(FPDF_ANNOTATION annot) {
+  delete CPDFAnnotContextFromFPDFAnnotation(annot);
 }
 
 DLLEXPORT FPDF_ANNOTATION_SUBTYPE STDCALL
 FPDFAnnot_GetSubtype(FPDF_ANNOTATION annot) {
-  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFAnnotation(annot);
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
   if (!pAnnotDict)
     return FPDF_ANNOT_UNKNOWN;
 
@@ -173,7 +225,8 @@
                                                unsigned int G,
                                                unsigned int B,
                                                unsigned int A) {
-  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFAnnotation(annot);
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
   if (!pAnnotDict || R > 255 || G > 255 || B > 255 || A > 255)
     return false;
 
@@ -201,7 +254,8 @@
                                                unsigned int* G,
                                                unsigned int* B,
                                                unsigned int* A) {
-  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFAnnotation(annot);
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
   if (!pAnnotDict || !R || !G || !B || !A)
     return false;
 
@@ -267,7 +321,8 @@
   if (!annot || !FPDFAnnot_HasAttachmentPoints(annot))
     return false;
 
-  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFAnnotation(annot);
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
   CPDF_Array* pQuadPoints = pAnnotDict->GetArrayFor("QuadPoints");
   if (pQuadPoints)
     pQuadPoints->Clear();
@@ -292,7 +347,8 @@
     return false;
 
   CPDF_Array* pArray =
-      CPDFDictionaryFromFPDFAnnotation(annot)->GetArrayFor("QuadPoints");
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict()->GetArrayFor(
+          "QuadPoints");
   if (!pArray)
     return false;
 
@@ -309,7 +365,8 @@
 
 DLLEXPORT FPDF_BOOL STDCALL FPDFAnnot_SetRect(FPDF_ANNOTATION annot,
                                               FS_RECTF rect) {
-  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFAnnotation(annot);
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
   if (!pAnnotDict)
     return false;
 
@@ -328,7 +385,8 @@
 
 DLLEXPORT FPDF_BOOL STDCALL FPDFAnnot_GetRect(FPDF_ANNOTATION annot,
                                               FS_RECTF* rect) {
-  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFAnnotation(annot);
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
   if (!rect || !pAnnotDict)
     return false;
 
@@ -346,7 +404,8 @@
 DLLEXPORT FPDF_BOOL STDCALL FPDFAnnot_SetText(FPDF_ANNOTATION annot,
                                               FPDFANNOT_TEXTTYPE type,
                                               FPDF_WIDESTRING text) {
-  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFAnnotation(annot);
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
   if (!pAnnotDict)
     return false;
 
@@ -361,7 +420,8 @@
                                                   FPDFANNOT_TEXTTYPE type,
                                                   void* buffer,
                                                   unsigned long buflen) {
-  CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFAnnotation(annot);
+  CPDF_Dictionary* pAnnotDict =
+      CPDFAnnotContextFromFPDFAnnotation(annot)->GetAnnotDict();
   if (!pAnnotDict)
     return 0;
 
diff --git a/fpdfsdk/fpdfannot_embeddertest.cpp b/fpdfsdk/fpdfannot_embeddertest.cpp
index e1da7cc..85f9b68 100644
--- a/fpdfsdk/fpdfannot_embeddertest.cpp
+++ b/fpdfsdk/fpdfannot_embeddertest.cpp
@@ -88,6 +88,7 @@
   EXPECT_EQ(157.211182f, quadpoints.x4);
   EXPECT_EQ(706.264465f, quadpoints.y4);
 
+  FPDFPage_CloseAnnot(annot);
   UnloadPage(page);
 }
 
@@ -130,6 +131,7 @@
   EXPECT_EQ(475.336090f, rect.right);
   EXPECT_EQ(681.535034f, rect.top);
 
+  FPDFPage_CloseAnnot(annot);
   UnloadPage(page);
 }
 
@@ -162,6 +164,8 @@
 
   // Check that the subtype of the annotation is correct.
   EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot));
+  FPDFPage_CloseAnnot(annot);
+
   ASSERT_TRUE(FPDFPage_GetAnnot(page, 0, &annot));
   EXPECT_EQ(FPDF_ANNOT_TEXT, FPDFAnnot_GetSubtype(annot));
 
@@ -222,6 +226,7 @@
                GetPlatformWString(reinterpret_cast<unsigned short*>(buf.data()))
                    .c_str());
 
+  FPDFPage_CloseAnnot(annot);
   UnloadPage(page);
 }
 
@@ -242,12 +247,14 @@
   EXPECT_EQ(718.913940f, quadpoints.y1);
   EXPECT_EQ(157.211182f, quadpoints.x4);
   EXPECT_EQ(706.264465f, quadpoints.y4);
+  FPDFPage_CloseAnnot(annot);
 
   // Add an underline annotation to the page and set its quadpoints.
   ASSERT_TRUE(FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE, &annot));
   quadpoints.x1 = 140.802643f;
   quadpoints.x3 = 140.802643f;
   ASSERT_TRUE(FPDFAnnot_SetAttachmentPoints(annot, quadpoints));
+  FPDFPage_CloseAnnot(annot);
 
   // Save the document, closing the page and document.
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
@@ -280,6 +287,7 @@
   EXPECT_NEAR(quadpoints.x4, new_quadpoints.x4, 0.001f);
   EXPECT_NEAR(quadpoints.y4, new_quadpoints.y4, 0.001f);
 
+  FPDFPage_CloseAnnot(new_annot);
   FPDF_ClosePage(new_page);
   FPDF_CloseDocument(new_doc);
 }
diff --git a/fpdfsdk/fpdfedit_embeddertest.cpp b/fpdfsdk/fpdfedit_embeddertest.cpp
index f7bade9..fcf7e39 100644
--- a/fpdfsdk/fpdfedit_embeddertest.cpp
+++ b/fpdfsdk/fpdfedit_embeddertest.cpp
@@ -900,8 +900,7 @@
   ASSERT_TRUE(page);
 
   // Add an underline annotation to the page without specifying its rectangle.
-  FPDF_ANNOTATION annot;
-  ASSERT_TRUE(FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE, &annot));
+  ASSERT_TRUE(FPDFPage_CreateAnnot(page, FPDF_ANNOT_UNDERLINE, nullptr));
 
   // FPDFPage_TransformAnnots() should run without errors when modifying
   // annotation rectangles.
diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp
index 6eafdee..68ef377 100644
--- a/fpdfsdk/fpdfview.cpp
+++ b/fpdfsdk/fpdfview.cpp
@@ -305,14 +305,6 @@
 #endif  // PDF_ENABLE_XFA
 }
 
-FPDF_ANNOTATION FPDFAnnotationFromCPDFDictionary(CPDF_Dictionary* pDict) {
-  return static_cast<FPDF_ANNOTATION>(pDict);
-}
-
-CPDF_Dictionary* CPDFDictionaryFromFPDFAnnotation(FPDF_ANNOTATION annot) {
-  return static_cast<CPDF_Dictionary*>(annot);
-}
-
 CPDF_PathObject* CPDFPathObjectFromFPDFPageObject(FPDF_PAGEOBJECT page_object) {
   return static_cast<CPDF_PathObject*>(page_object);
 }
diff --git a/fpdfsdk/fpdfview_c_api_test.c b/fpdfsdk/fpdfview_c_api_test.c
index c8fdc47..0deaf9d 100644
--- a/fpdfsdk/fpdfview_c_api_test.c
+++ b/fpdfsdk/fpdfview_c_api_test.c
@@ -39,6 +39,7 @@
     CHK(FPDFPage_CreateAnnot);
     CHK(FPDFPage_GetAnnotCount);
     CHK(FPDFPage_GetAnnot);
+    CHK(FPDFPage_CloseAnnot);
     CHK(FPDFAnnot_GetSubtype);
     CHK(FPDFAnnot_SetColor);
     CHK(FPDFAnnot_GetColor);
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index adfddec..bd18898 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -97,6 +97,13 @@
                                               int index,
                                               FPDF_ANNOTATION* annot);
 
+// Close an annotation. Must be called when the annotation returned by
+// FPDFPage_CreateAnnot() or FPDFPage_GetAnnot() is no longer needed. This
+// function does not remove the annotation from the document.
+//
+//   annot  - handle to an annotation.
+DLLEXPORT void STDCALL FPDFPage_CloseAnnot(FPDF_ANNOTATION annot);
+
 // Get the subtype of an annotation.
 //
 //   annot  - handle to an annotation.