0

Populate Choice Fields in PDFiumPage

This CL introduces a method PDFiumPage::PopulateChoiceFields() which
reads choice form fields from the PDF document and stores relevant
information in a vector within PDFiumPage.

The CL also includes a new test file with sample choice fields and a
unit test to validate the new method.

Bug: 1030242
Change-Id: Ie8dc9da6c694747d8fef7721625f7690dda64124
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2249244
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Ankit Kumar 🌪️ <ankk@microsoft.com>
Commit-Queue: Mansi Awasthi <maawas@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#781102}
This commit is contained in:
Mansi Awasthi
2020-06-23 01:34:34 +00:00
committed by Commit Bot
parent 9555d6555d
commit b62d1d1c56
5 changed files with 386 additions and 1 deletions

@ -1240,12 +1240,45 @@ void PDFiumPage::PopulateTextField(FPDF_ANNOTATION annot) {
text_fields_.push_back(std::move(text_field));
}
void PDFiumPage::PopulateChoiceField(FPDF_ANNOTATION annot) {
DCHECK(annot);
FPDF_FORMHANDLE form_handle = engine_->form();
int form_field_type = FPDFAnnot_GetFormFieldType(form_handle, annot);
DCHECK(form_field_type == FPDF_FORMFIELD_LISTBOX ||
form_field_type == FPDF_FORMFIELD_COMBOBOX);
ChoiceField choice_field;
if (!PopulateFormFieldProperties(annot, &choice_field))
return;
int options_count = FPDFAnnot_GetOptionCount(form_handle, annot);
if (options_count < 0)
return;
choice_field.options.resize(options_count);
for (int i = 0; i < options_count; ++i) {
choice_field.options[i].name =
base::UTF16ToUTF8(CallPDFiumWideStringBufferApi(
base::BindRepeating(&FPDFAnnot_GetOptionLabel, form_handle, annot,
i),
/*check_expected_size=*/true));
choice_field.options[i].is_selected =
FPDFAnnot_IsOptionSelected(form_handle, annot, i);
}
choice_fields_.push_back(std::move(choice_field));
}
void PDFiumPage::PopulateFormField(FPDF_ANNOTATION annot) {
DCHECK_EQ(FPDFAnnot_GetSubtype(annot), FPDF_ANNOT_WIDGET);
int form_field_type = FPDFAnnot_GetFormFieldType(engine_->form(), annot);
// TODO(crbug.com/1030242): Populate other types of form fields too.
switch (form_field_type) {
case FPDF_FORMFIELD_COMBOBOX:
case FPDF_FORMFIELD_LISTBOX: {
PopulateChoiceField(annot);
break;
}
case FPDF_FORMFIELD_TEXTFIELD: {
PopulateTextField(annot);
break;
@ -1415,6 +1448,19 @@ PDFiumPage::TextField::TextField(const TextField& that) = default;
PDFiumPage::TextField::~TextField() = default;
PDFiumPage::ChoiceFieldOption::ChoiceFieldOption() = default;
PDFiumPage::ChoiceFieldOption::ChoiceFieldOption(
const ChoiceFieldOption& that) = default;
PDFiumPage::ChoiceFieldOption::~ChoiceFieldOption() = default;
PDFiumPage::ChoiceField::ChoiceField() = default;
PDFiumPage::ChoiceField::ChoiceField(const ChoiceField& that) = default;
PDFiumPage::ChoiceField::~ChoiceField() = default;
// static
uint32_t PDFiumPage::CountLinkHighlightOverlaps(
const std::vector<Link>& links,

@ -178,6 +178,7 @@ class PDFiumPage {
FRIEND_TEST_ALL_PREFIXES(PDFiumPageLinkTest, TestLinkGeneration);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageHighlightTest, TestPopulateHighlights);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageTextFieldTest, TestPopulateTextFields);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountPartialOverlaps);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountCompleteOverlaps);
@ -257,6 +258,25 @@ class PDFiumPage {
std::string value;
};
// Represents a choice field option.
struct ChoiceFieldOption {
ChoiceFieldOption();
ChoiceFieldOption(const ChoiceFieldOption& other);
~ChoiceFieldOption();
std::string name;
bool is_selected;
};
// Represents a choice field within the page.
struct ChoiceField : FormField {
ChoiceField();
ChoiceField(const ChoiceField& other);
~ChoiceField();
std::vector<ChoiceFieldOption> options;
};
// Returns a link index if the given character index is over a link, or -1
// otherwise.
int GetLink(int char_index, LinkTarget* target);
@ -274,7 +294,9 @@ class PDFiumPage {
void PopulateHighlight(FPDF_ANNOTATION annot);
// Populate |text_fields_| with |annot|.
void PopulateTextField(FPDF_ANNOTATION annot);
// Populate form fields like text field on the page.
// Populate |choice_fields_| with |annot|.
void PopulateChoiceField(FPDF_ANNOTATION annot);
// Populate form fields like text field and choice field on the page.
void PopulateFormField(FPDF_ANNOTATION annot);
// Returns link type and fills target associated with a destination. Returns
// NONSELECTABLE_AREA if detection failed.
@ -331,6 +353,7 @@ class PDFiumPage {
bool calculated_annotations_ = false;
std::vector<Highlight> highlights_;
std::vector<TextField> text_fields_;
std::vector<ChoiceField> choice_fields_;
bool logged_overlapping_annotations_ = false;
bool calculated_page_object_text_run_breaks_ = false;
// The set of character indices on which text runs need to be broken for page

@ -513,6 +513,94 @@ TEST_F(PDFiumPageTextFieldTest, TestPopulateTextFields) {
}
}
using PDFiumPageChoiceFieldTest = PDFiumTestBase;
TEST_F(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields) {
struct ExpectedChoiceFieldOption {
const char* name;
bool is_selected;
};
struct ExpectedChoiceField {
const char* name;
std::vector<struct ExpectedChoiceFieldOption> options;
pp::Rect bounding_rect;
int flags;
};
static const ExpectedChoiceField kExpectedChoiceFields[] = {
{"Listbox_SingleSelect",
{{"Foo", false}, {"Bar", false}, {"Qux", false}},
{138, 296, 135, 41},
0},
{"Combo1",
{{"Apple", false}, {"Banana", true}, {"Cherry", false}},
{138, 230, 135, 41},
131072},
{"Listbox_ReadOnly",
{{"Dog", false}, {"Elephant", false}, {"Frog", false}},
{138, 96, 135, 41},
1},
{"Listbox_MultiSelectMultipleIndices",
{
{"Albania", false},
{"Belgium", true},
{"Croatia", false},
{"Denmark", true},
{"Estonia", false},
},
{138, 430, 135, 41},
2097152},
{"Listbox_MultiSelectMultipleValues",
{
{"Alpha", false},
{"Beta", false},
{"Gamma", true},
{"Delta", false},
{"Epsilon", true},
},
{138, 496, 135, 41},
2097152},
{"Listbox_MultiSelectMultipleMismatch",
{
{"Alligator", true},
{"Bear", false},
{"Cougar", true},
{"Deer", false},
{"Echidna", false},
},
{138, 563, 135, 41},
2097152}};
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("form_choice_fields.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage* page = GetPDFiumPageForTest(engine.get(), 0);
ASSERT_TRUE(page);
page->PopulateAnnotations();
size_t choice_fields_count = page->choice_fields_.size();
ASSERT_EQ(base::size(kExpectedChoiceFields), choice_fields_count);
for (size_t i = 0; i < choice_fields_count; ++i) {
EXPECT_EQ(kExpectedChoiceFields[i].name, page->choice_fields_[i].name);
size_t choice_field_options_count = page->choice_fields_[i].options.size();
ASSERT_EQ(base::size(kExpectedChoiceFields[i].options),
choice_field_options_count);
for (size_t j = 0; j < choice_field_options_count; ++j) {
EXPECT_EQ(kExpectedChoiceFields[i].options[j].name,
page->choice_fields_[i].options[j].name);
EXPECT_EQ(kExpectedChoiceFields[i].options[j].is_selected,
page->choice_fields_[i].options[j].is_selected);
}
CompareRect(kExpectedChoiceFields[i].bounding_rect,
page->choice_fields_[i].bounding_rect);
EXPECT_EQ(kExpectedChoiceFields[i].flags, page->choice_fields_[i].flags);
}
}
using PDFiumPageOverlappingTest = PDFiumTestBase;
// The following scenarios are covered across both test cases:

@ -0,0 +1,106 @@
{{header}}
{{object 1 0}} <<
/Type /Catalog
/Pages 2 0 R
/AcroForm <<
/Fields [4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R]
>>
>>
endobj
{{object 2 0}} <<
/Type /Pages
/Count 1
/Kids [3 0 R]
>>
endobj
{{object 3 0}} <<
/Type /Page
/Parent 2 0 R
/Resources <<
/Font <<
/F1 <<
/Type /Font
/Subtype /Type1
/BaseFont /Helvetica
>>
>>
>>
/MediaBox [0 0 300 600]
/Annots [4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R]
>>
endobj
{{object 4 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 0
/T (Listbox_SingleSelect)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 350 200 380]
/Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
>>
endobj
{{object 5 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 131072
/T (Combo1)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 400 200 430]
/Opt [(Apple) (Banana) (Cherry)]
/V (Banana)
>>
endobj
{{object 6 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 1
/T (Listbox_ReadOnly)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 500 200 530]
/Opt [(Dog) (Elephant) (Frog)]
>>
endobj
{{object 7 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 2097152
/T (Listbox_MultiSelectMultipleIndices)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 250 200 280]
/Opt [(Albania) (Belgium) (Croatia) (Denmark) (Estonia)]
/I [1 3]
>>
endobj
{{object 8 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 2097152
/T (Listbox_MultiSelectMultipleValues)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 200 200 230]
/Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
/V [(Epsilon) (Gamma)]
>>
endobj
{{object 9 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 2097152
/T (Listbox_MultiSelectMultipleMismatch)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 150 200 180]
/Opt [(Alligator) (Bear) (Cougar) (Deer) (Echidna)]
/V [(Alligator) (Cougar)]
/I [1 3 4]
>>
endobj
{{xref}}
{{trailer}}
{{startxref}}
%%EOF

@ -0,0 +1,122 @@
%PDF-1.7
%<25><><EFBFBD><EFBFBD>
1 0 obj <<
/Type /Catalog
/Pages 2 0 R
/AcroForm <<
/Fields [4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R]
>>
>>
endobj
2 0 obj <<
/Type /Pages
/Count 1
/Kids [3 0 R]
>>
endobj
3 0 obj <<
/Type /Page
/Parent 2 0 R
/Resources <<
/Font <<
/F1 <<
/Type /Font
/Subtype /Type1
/BaseFont /Helvetica
>>
>>
>>
/MediaBox [0 0 300 600]
/Annots [4 0 R 5 0 R 6 0 R 7 0 R 8 0 R 9 0 R]
>>
endobj
4 0 obj <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 0
/T (Listbox_SingleSelect)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 350 200 380]
/Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
>>
endobj
5 0 obj <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 131072
/T (Combo1)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 400 200 430]
/Opt [(Apple) (Banana) (Cherry)]
/V (Banana)
>>
endobj
6 0 obj <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 1
/T (Listbox_ReadOnly)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 500 200 530]
/Opt [(Dog) (Elephant) (Frog)]
>>
endobj
7 0 obj <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 2097152
/T (Listbox_MultiSelectMultipleIndices)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 250 200 280]
/Opt [(Albania) (Belgium) (Croatia) (Denmark) (Estonia)]
/I [1 3]
>>
endobj
8 0 obj <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 2097152
/T (Listbox_MultiSelectMultipleValues)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 200 200 230]
/Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
/V [(Epsilon) (Gamma)]
>>
endobj
9 0 obj <<
/Type /Annot
/Subtype /Widget
/FT /Ch
/Ff 2097152
/T (Listbox_MultiSelectMultipleMismatch)
/DA (0 0 0 rg /F1 12 Tf)
/Rect [100 150 200 180]
/Opt [(Alligator) (Bear) (Cougar) (Deer) (Echidna)]
/V [(Alligator) (Cougar)]
/I [1 3 4]
>>
endobj
xref
0 10
0000000000 65535 f
0000000015 00000 n
0000000138 00000 n
0000000201 00000 n
0000000462 00000 n
0000000667 00000 n
0000000861 00000 n
0000001044 00000 n
0000001288 00000 n
0000001536 00000 n
trailer <<
/Root 1 0 R
/Size 10
>>
startxref
1806
%%EOF