Add FPDF_GetSignatureObject() API

This builds on top of the recently added FPDF_GetSignatureCount() API.
The combination of the two allows iterating over all signature objects,
which adds the possibility to later add getter functions to access the
actual properties of the returned FPDF_SIGNATURE.

Change-Id: I46e822d2b9aa7511850acb353472cbf058c85a2a
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/70990
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/fpdfsdk/cpdfsdk_helpers.h b/fpdfsdk/cpdfsdk_helpers.h
index 73ca946..9d47ad1 100644
--- a/fpdfsdk/cpdfsdk_helpers.h
+++ b/fpdfsdk/cpdfsdk_helpers.h
@@ -211,6 +211,11 @@
   return reinterpret_cast<CPDFSDK_FormFillEnvironment*>(handle);
 }
 
+inline FPDF_SIGNATURE FPDFSignatureFromCPDFDictionary(
+    CPDF_Dictionary* dictionary) {
+  return reinterpret_cast<FPDF_SIGNATURE>(dictionary);
+}
+
 CPDFSDK_InteractiveForm* FormHandleToInteractiveForm(FPDF_FORMHANDLE hHandle);
 
 ByteString ByteStringFromFPDFWideString(FPDF_WIDESTRING wide_string);
diff --git a/fpdfsdk/fpdf_signature.cpp b/fpdfsdk/fpdf_signature.cpp
index 0d898ef..7c1353f 100644
--- a/fpdfsdk/fpdf_signature.cpp
+++ b/fpdfsdk/fpdf_signature.cpp
@@ -8,31 +8,52 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "fpdfsdk/cpdfsdk_helpers.h"
+#include "third_party/base/stl_util.h"
+
+namespace {
+
+std::vector<CPDF_Dictionary*> CollectSignatures(CPDF_Document* doc) {
+  std::vector<CPDF_Dictionary*> signatures;
+  CPDF_Dictionary* root = doc->GetRoot();
+  if (!root)
+    return signatures;
+
+  const CPDF_Dictionary* acro_form = root->GetDictFor("AcroForm");
+  if (!acro_form)
+    return signatures;
+
+  const CPDF_Array* fields = acro_form->GetArrayFor("Fields");
+  if (!fields)
+    return signatures;
+
+  CPDF_ArrayLocker locker(fields);
+  for (auto& field : locker) {
+    CPDF_Dictionary* field_dict = field->GetDict();
+    if (field_dict && field_dict->GetNameFor("FT") == "Sig")
+      signatures.push_back(field_dict);
+  }
+  return signatures;
+}
+
+}  // namespace
 
 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document) {
   auto* doc = CPDFDocumentFromFPDFDocument(document);
   if (!doc)
     return -1;
 
-  CPDF_Dictionary* root = doc->GetRoot();
-  if (!root)
-    return 0;
+  return pdfium::CollectionSize<int>(CollectSignatures(doc));
+}
 
-  const CPDF_Dictionary* acro_form = root->GetDictFor("AcroForm");
-  if (!acro_form)
-    return 0;
+FPDF_EXPORT FPDF_SIGNATURE FPDF_CALLCONV
+FPDF_GetSignatureObject(FPDF_DOCUMENT document, int index) {
+  auto* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc)
+    return nullptr;
 
-  const CPDF_Array* fields = acro_form->GetArrayFor("Fields");
-  if (!fields)
-    return 0;
+  std::vector<CPDF_Dictionary*> signatures = CollectSignatures(doc);
+  if (!pdfium::IndexInBounds(signatures, index))
+    return nullptr;
 
-  int signature_count = 0;
-  CPDF_ArrayLocker locker(fields);
-  for (const auto& field : locker) {
-    const CPDF_Dictionary* field_dict = field->GetDict();
-    if (field_dict && field_dict->GetNameFor("FT") == "Sig")
-      ++signature_count;
-  }
-
-  return signature_count;
+  return FPDFSignatureFromCPDFDictionary(signatures[index]);
 }
diff --git a/fpdfsdk/fpdf_signature_embeddertest.cpp b/fpdfsdk/fpdf_signature_embeddertest.cpp
index fb8bb3a..851df00 100644
--- a/fpdfsdk/fpdf_signature_embeddertest.cpp
+++ b/fpdfsdk/fpdf_signature_embeddertest.cpp
@@ -19,3 +19,20 @@
   // Provide no document.
   EXPECT_EQ(-1, FPDF_GetSignatureCount(nullptr));
 }
+
+TEST_F(FPDFSignatureEmbedderTest, GetSignatureObject) {
+  EXPECT_TRUE(OpenDocument("two_signatures.pdf"));
+  // Different, non-null signature objects are returned.
+  FPDF_SIGNATURE signature1 = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_NE(nullptr, signature1);
+  FPDF_SIGNATURE signature2 = FPDF_GetSignatureObject(document(), 1);
+  EXPECT_NE(nullptr, signature2);
+  EXPECT_NE(signature1, signature2);
+
+  // Out of bounds.
+  EXPECT_EQ(nullptr, FPDF_GetSignatureObject(document(), -1));
+  EXPECT_EQ(nullptr, FPDF_GetSignatureObject(document(), 2));
+
+  // Provide no document.
+  EXPECT_EQ(nullptr, FPDF_GetSignatureObject(nullptr, 0));
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index d04d1f8..b0e4606 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -317,6 +317,7 @@
 
     // fpdf_signature.h
     CHK(FPDF_GetSignatureCount);
+    CHK(FPDF_GetSignatureObject);
 
     // fpdf_structtree.h
     CHK(FPDF_StructElement_CountChildren);
diff --git a/public/fpdf_signature.h b/public/fpdf_signature.h
index 7290bcb..df01a84 100644
--- a/public/fpdf_signature.h
+++ b/public/fpdf_signature.h
@@ -21,6 +21,19 @@
 //          Total number of signatures in the document on success, -1 on error.
 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document);
 
+// Experimental API.
+// Function: FPDF_GetSignatureObject
+//          Get the Nth signature of the document.
+// Parameters:
+//          document    -   Handle to document. Returned by FPDF_LoadDocument().
+//          index       -   Index into the array of signatures of the document.
+// Return value:
+//          Returns the handle to the signature, or NULL on failure. The caller
+//          does not take ownership of the returned FPDF_SIGNATURE. Instead, it
+//          remains valid until FPDF_CloseDocument() is called for the document.
+FPDF_EXPORT FPDF_SIGNATURE FPDF_CALLCONV
+FPDF_GetSignatureObject(FPDF_DOCUMENT document, int index);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/public/fpdfview.h b/public/fpdfview.h
index debe083..3181df3 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -71,6 +71,7 @@
 typedef const struct fpdf_pathsegment_t* FPDF_PATHSEGMENT;
 typedef void* FPDF_RECORDER;  // Passed into skia.
 typedef struct fpdf_schhandle_t__* FPDF_SCHHANDLE;
+typedef struct fpdf_signature_t__* FPDF_SIGNATURE;
 typedef struct fpdf_structelement_t__* FPDF_STRUCTELEMENT;
 typedef struct fpdf_structtree_t__* FPDF_STRUCTTREE;
 typedef struct fpdf_textpage_t__* FPDF_TEXTPAGE;