Adding API to check if an option in a choice form field is selected.

This change adds a public API to determine whether or not the option at
|index| in |annot|'s "Opt" dictionary is selected. The API
FPDFAnnot_IsOptionSelected() is added in fpdf_annot.h and is intended
for use with listbox and combobox widget annotations.

This CL also includes tests to validate the API for listbox and combobox
widget annotations.

Bug: pdfium:1526
Change-Id: I4bd2ae6805ef348f1c34dca7e1136824490314f4
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/69790
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Mansi Awasthi <maawas@microsoft.com>
diff --git a/fpdfsdk/fpdf_annot.cpp b/fpdfsdk/fpdf_annot.cpp
index d02b1fc..b414c8f 100644
--- a/fpdfsdk/fpdf_annot.cpp
+++ b/fpdfsdk/fpdf_annot.cpp
@@ -1057,6 +1057,25 @@
 }
 
 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_IsOptionSelected(FPDF_FORMHANDLE handle,
+                           FPDF_ANNOTATION annot,
+                           int index) {
+  if (index < 0)
+    return false;
+
+  CPDF_FormField* form_field = GetFormField(handle, annot);
+  if (!form_field || index >= form_field->CountOptions())
+    return false;
+
+  if (form_field->GetFieldType() != FormFieldType::kComboBox &&
+      form_field->GetFieldType() != FormFieldType::kListBox) {
+    return false;
+  }
+
+  return form_field->IsItemSelected(index);
+}
+
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
 FPDFAnnot_GetFontSize(FPDF_FORMHANDLE hHandle,
                       FPDF_ANNOTATION annot,
                       float* value) {
diff --git a/fpdfsdk/fpdf_annot_embeddertest.cpp b/fpdfsdk/fpdf_annot_embeddertest.cpp
index 6eccf74..686fe95 100644
--- a/fpdfsdk/fpdf_annot_embeddertest.cpp
+++ b/fpdfsdk/fpdf_annot_embeddertest.cpp
@@ -2049,6 +2049,147 @@
   UnloadPage(page);
 }
 
+TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedCombobox) {
+  // Open a file with combobox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Checks for Combobox with no Values (/V) or Selected Indices (/I) objects.
+    int count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(3, count);
+    for (int i = 0; i < count; i++) {
+      EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // Checks for Combobox with Values (/V) object which is just a string.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(26, count);
+    for (int i = 0; i < count; i++) {
+      EXPECT_EQ(i == 1,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    // Checks for index outside bound i.e. (index >= CountOption()).
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
+                                            /*index=*/26));
+    // Checks for negetive index.
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
+                                            /*index=*/-1));
+
+    // Checks for bad form handle/annot.
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(nullptr, nullptr, /*index=*/0));
+    EXPECT_FALSE(
+        FPDFAnnot_IsOptionSelected(form_handle(), nullptr, /*index=*/0));
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(nullptr, annot.get(), /*index=*/0));
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedListbox) {
+  // Open a file with listbox widget annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("listbox_form.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with no Values (/V) or Selected Indices (/I) objects.
+    int count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(3, count);
+    for (int i = 0; i < count; i++) {
+      EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 1));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with Values (/V) object which is just a string.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(26, count);
+    for (int i = 0; i < count; i++) {
+      EXPECT_EQ(i == 1,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 3));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with only Selected indices (/I) object which is an
+    // array with multiple objects.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(5, count);
+    for (int i = 0; i < count; i++) {
+      bool expected = (i == 1 || i == 3);
+      EXPECT_EQ(expected,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 4));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with Values (/V) object which is an array with
+    // multiple objects.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(5, count);
+    for (int i = 0; i < count; i++) {
+      bool expected = (i == 2 || i == 4);
+      EXPECT_EQ(expected,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+
+    annot.reset(FPDFPage_GetAnnot(page, 5));
+    ASSERT_TRUE(annot);
+
+    // Checks for Listbox with both Values (/V) and Selected Indices (/I)
+    // objects conflict with different lengths.
+    count = FPDFAnnot_GetOptionCount(form_handle(), annot.get());
+    ASSERT_EQ(5, count);
+    for (int i = 0; i < count; i++) {
+      bool expected = (i == 0 || i == 2);
+      EXPECT_EQ(expected,
+                FPDFAnnot_IsOptionSelected(form_handle(), annot.get(), i));
+    }
+  }
+
+  UnloadPage(page);
+}
+
+TEST_F(FPDFAnnotEmbedderTest, IsOptionSelectedInvalidAnnotations) {
+  // Open a file with multiple form field annotations and load its first page.
+  ASSERT_TRUE(OpenDocument("multiple_form_types.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  {
+    ScopedFPDFAnnotation annot(FPDFPage_GetAnnot(page, 0));
+    ASSERT_TRUE(annot);
+
+    // Checks for link annotation.
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
+                                            /*index=*/0));
+
+    annot.reset(FPDFPage_GetAnnot(page, 3));
+    ASSERT_TRUE(annot);
+
+    // Checks for text field annotation.
+    EXPECT_FALSE(FPDFAnnot_IsOptionSelected(form_handle(), annot.get(),
+                                            /*index=*/0));
+  }
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFAnnotEmbedderTest, GetFontSizeCombobox) {
   // Open a file with combobox annotations and load its first page.
   ASSERT_TRUE(OpenDocument("combobox_form.pdf"));
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index aebffe5..c2c77d6 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -70,6 +70,7 @@
     CHK(FPDFAnnot_HasKey);
     CHK(FPDFAnnot_IsChecked);
     CHK(FPDFAnnot_IsObjectSupportedSubtype);
+    CHK(FPDFAnnot_IsOptionSelected);
     CHK(FPDFAnnot_IsSupportedSubtype);
     CHK(FPDFAnnot_RemoveInkList);
     CHK(FPDFAnnot_RemoveObject);
diff --git a/public/fpdf_annot.h b/public/fpdf_annot.h
index f764ed3..b1fe901 100644
--- a/public/fpdf_annot.h
+++ b/public/fpdf_annot.h
@@ -81,6 +81,7 @@
 // interactive form choice fields.
 #define FPDF_FORMFLAG_CHOICE_COMBO (1 << 17)
 #define FPDF_FORMFLAG_CHOICE_EDIT (1 << 18)
+#define FPDF_FORMFLAG_CHOICE_MULTI_SELECT (1 << 21)
 
 typedef enum FPDFANNOT_COLORTYPE {
   FPDFANNOT_COLORTYPE_Color = 0,
@@ -656,6 +657,22 @@
                          unsigned long buflen);
 
 // Experimental API.
+// Determine whether or not the option at |index| in |annot|'s "Opt" dictionary
+// is selected. Intended for use with listbox and combobox widget annotations.
+//
+//   handle  - handle to the form fill module, returned by
+//             FPDFDOC_InitFormFillEnvironment.
+//   annot   - handle to an annotation.
+//   index   - numeric index of the option in the "Opt" array.
+//
+// Returns true if the option at |index| in |annot|'s "Opt" dictionary is
+// selected, false otherwise.
+FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
+FPDFAnnot_IsOptionSelected(FPDF_FORMHANDLE handle,
+                           FPDF_ANNOTATION annot,
+                           int index);
+
+// Experimental API.
 // Get the float value of the font size for an |annot| with variable text.
 // If 0, the font is to be auto-sized: its size is computed as a function of
 // the height of the annotation rectangle.