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:

committed by
Commit Bot

parent
9555d6555d
commit
b62d1d1c56
@ -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:
|
||||
|
106
pdf/test/data/form_choice_fields.in
Normal file
106
pdf/test/data/form_choice_fields.in
Normal file
@ -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
|
122
pdf/test/data/form_choice_fields.pdf
Normal file
122
pdf/test/data/form_choice_fields.pdf
Normal file
@ -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
|
Reference in New Issue
Block a user