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.