Commit 98c39cd6 authored by Mansi Awasthi's avatar Mansi Awasthi Committed by Commit Bot

Populate Buttons in PDFiumPage

This CL introduces a method PDFiumPage::PopulateButton() which reads
button form fields (radio button, checkbox and push button) from the PDF
document and stores relevant information in a vector within PDFiumPage.

The CL also includes a new test file with sample radio buttons, checkbox
and push button. A unit test is also added to validate the new method.

Bug: 1030242
Change-Id: I1f5102f8c20027abe785fa1833c2ecc9cbce8734
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2249285
Commit-Queue: Mansi Awasthi <maawas@microsoft.com>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: Ankit Kumar 🌪️ <ankk@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#784392}
parent 587c8026
...@@ -244,6 +244,11 @@ uint32_t CountInternalTextOverlaps(const std::vector<T>& text_objects) { ...@@ -244,6 +244,11 @@ uint32_t CountInternalTextOverlaps(const std::vector<T>& text_objects) {
return overlaps; return overlaps;
} }
bool IsRadioButtonOrCheckBox(int button_type) {
return button_type == FPDF_FORMFIELD_CHECKBOX ||
button_type == FPDF_FORMFIELD_RADIOBUTTON;
}
} // namespace } // namespace
PDFiumPage::LinkTarget::LinkTarget() : page(-1) {} PDFiumPage::LinkTarget::LinkTarget() : page(-1) {}
...@@ -1268,12 +1273,45 @@ void PDFiumPage::PopulateChoiceField(FPDF_ANNOTATION annot) { ...@@ -1268,12 +1273,45 @@ void PDFiumPage::PopulateChoiceField(FPDF_ANNOTATION annot) {
choice_fields_.push_back(std::move(choice_field)); choice_fields_.push_back(std::move(choice_field));
} }
void PDFiumPage::PopulateButton(FPDF_ANNOTATION annot) {
DCHECK(annot);
FPDF_FORMHANDLE form_handle = engine_->form();
int button_type = FPDFAnnot_GetFormFieldType(form_handle, annot);
DCHECK(button_type == FPDF_FORMFIELD_PUSHBUTTON ||
IsRadioButtonOrCheckBox(button_type));
Button button;
if (!PopulateFormFieldProperties(annot, &button))
return;
button.type = button_type;
if (IsRadioButtonOrCheckBox(button_type)) {
button.control_count = FPDFAnnot_GetFormControlCount(form_handle, annot);
if (button.control_count <= 0)
return;
button.control_index = FPDFAnnot_GetFormControlIndex(form_handle, annot);
button.value = base::UTF16ToUTF8(CallPDFiumWideStringBufferApi(
base::BindRepeating(&FPDFAnnot_GetFormFieldExportValue, form_handle,
annot),
/*check_expected_size=*/true));
button.is_checked = FPDFAnnot_IsChecked(form_handle, annot);
}
buttons_.push_back(std::move(button));
}
void PDFiumPage::PopulateFormField(FPDF_ANNOTATION annot) { void PDFiumPage::PopulateFormField(FPDF_ANNOTATION annot) {
DCHECK_EQ(FPDFAnnot_GetSubtype(annot), FPDF_ANNOT_WIDGET); DCHECK_EQ(FPDFAnnot_GetSubtype(annot), FPDF_ANNOT_WIDGET);
int form_field_type = FPDFAnnot_GetFormFieldType(engine_->form(), annot); int form_field_type = FPDFAnnot_GetFormFieldType(engine_->form(), annot);
// TODO(crbug.com/1030242): Populate other types of form fields too. // TODO(crbug.com/1030242): Populate other types of form fields too.
switch (form_field_type) { switch (form_field_type) {
case FPDF_FORMFIELD_PUSHBUTTON:
case FPDF_FORMFIELD_CHECKBOX:
case FPDF_FORMFIELD_RADIOBUTTON: {
PopulateButton(annot);
break;
}
case FPDF_FORMFIELD_COMBOBOX: case FPDF_FORMFIELD_COMBOBOX:
case FPDF_FORMFIELD_LISTBOX: { case FPDF_FORMFIELD_LISTBOX: {
PopulateChoiceField(annot); PopulateChoiceField(annot);
...@@ -1461,6 +1499,12 @@ PDFiumPage::ChoiceField::ChoiceField(const ChoiceField& that) = default; ...@@ -1461,6 +1499,12 @@ PDFiumPage::ChoiceField::ChoiceField(const ChoiceField& that) = default;
PDFiumPage::ChoiceField::~ChoiceField() = default; PDFiumPage::ChoiceField::~ChoiceField() = default;
PDFiumPage::Button::Button() = default;
PDFiumPage::Button::Button(const Button& that) = default;
PDFiumPage::Button::~Button() = default;
// static // static
uint32_t PDFiumPage::CountLinkHighlightOverlaps( uint32_t PDFiumPage::CountLinkHighlightOverlaps(
const std::vector<Link>& links, const std::vector<Link>& links,
......
...@@ -179,6 +179,7 @@ class PDFiumPage { ...@@ -179,6 +179,7 @@ class PDFiumPage {
FRIEND_TEST_ALL_PREFIXES(PDFiumPageHighlightTest, TestPopulateHighlights); FRIEND_TEST_ALL_PREFIXES(PDFiumPageHighlightTest, TestPopulateHighlights);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageTextFieldTest, TestPopulateTextFields); FRIEND_TEST_ALL_PREFIXES(PDFiumPageTextFieldTest, TestPopulateTextFields);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields); FRIEND_TEST_ALL_PREFIXES(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageButtonTest, TestPopulateButtons);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountPartialOverlaps); FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountPartialOverlaps);
FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountCompleteOverlaps); FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountCompleteOverlaps);
...@@ -277,6 +278,29 @@ class PDFiumPage { ...@@ -277,6 +278,29 @@ class PDFiumPage {
std::vector<ChoiceFieldOption> options; std::vector<ChoiceFieldOption> options;
}; };
// Represents a button within the page.
struct Button : FormField {
Button();
Button(const Button& other);
~Button();
std::string value;
// A button can be of type radio, checkbox or push button.
int type;
// Represents if the radio button or checkbox is checked.
bool is_checked = false;
// Represents count of controls in the control group. A group of
// interactive form annotations is collectively called a form control
// group. Here an interactive form annotation should be either a radio
// button or a checkbox.
uint32_t control_count = 0;
// Represents index of the control in the control group. A group of
// interactive form annotations is collectively called a form control
// group. Here an interactive form annotation should be either a radio
// button or a checkbox. Value of |control_index| is -1 for push button.
int control_index = -1;
};
// Returns a link index if the given character index is over a link, or -1 // Returns a link index if the given character index is over a link, or -1
// otherwise. // otherwise.
int GetLink(int char_index, LinkTarget* target); int GetLink(int char_index, LinkTarget* target);
...@@ -296,7 +320,9 @@ class PDFiumPage { ...@@ -296,7 +320,9 @@ class PDFiumPage {
void PopulateTextField(FPDF_ANNOTATION annot); void PopulateTextField(FPDF_ANNOTATION annot);
// Populate |choice_fields_| with |annot|. // Populate |choice_fields_| with |annot|.
void PopulateChoiceField(FPDF_ANNOTATION annot); void PopulateChoiceField(FPDF_ANNOTATION annot);
// Populate form fields like text field and choice field on the page. // Populate |buttons_| with |annot|.
void PopulateButton(FPDF_ANNOTATION annot);
// Populate form fields like text field, choice field and button on the page.
void PopulateFormField(FPDF_ANNOTATION annot); void PopulateFormField(FPDF_ANNOTATION annot);
// Returns link type and fills target associated with a destination. Returns // Returns link type and fills target associated with a destination. Returns
// NONSELECTABLE_AREA if detection failed. // NONSELECTABLE_AREA if detection failed.
...@@ -354,6 +380,7 @@ class PDFiumPage { ...@@ -354,6 +380,7 @@ class PDFiumPage {
std::vector<Highlight> highlights_; std::vector<Highlight> highlights_;
std::vector<TextField> text_fields_; std::vector<TextField> text_fields_;
std::vector<ChoiceField> choice_fields_; std::vector<ChoiceField> choice_fields_;
std::vector<Button> buttons_;
bool logged_overlapping_annotations_ = false; bool logged_overlapping_annotations_ = false;
bool calculated_page_object_text_run_breaks_ = false; bool calculated_page_object_text_run_breaks_ = false;
// The set of character indices on which text runs need to be broken for page // The set of character indices on which text runs need to be broken for page
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "pdf/test/test_utils.h" #include "pdf/test/test_utils.h"
#include "ppapi/c/private/ppb_pdf.h" #include "ppapi/c/private/ppb_pdf.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/pdfium/public/fpdf_formfill.h"
#include "ui/gfx/range/range.h" #include "ui/gfx/range/range.h"
namespace chrome_pdf { namespace chrome_pdf {
...@@ -601,6 +602,88 @@ TEST_F(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields) { ...@@ -601,6 +602,88 @@ TEST_F(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields) {
} }
} }
using PDFiumPageButtonTest = PDFiumTestBase;
TEST_F(PDFiumPageButtonTest, TestPopulateButtons) {
struct ExpectedButton {
const char* name;
const char* value;
int type;
int flags;
bool is_checked;
uint32_t control_count;
int control_index;
pp::Rect bounding_rect;
};
static const ExpectedButton kExpectedButtons[] = {{"readOnlyCheckbox",
"Yes",
FPDF_FORMFIELD_CHECKBOX,
1,
true,
1,
0,
{185, 43, 28, 28}},
{"checkbox",
"Yes",
FPDF_FORMFIELD_CHECKBOX,
2,
false,
1,
0,
{185, 96, 28, 28}},
{"RadioButton",
"value1",
FPDF_FORMFIELD_RADIOBUTTON,
49154,
false,
2,
0,
{185, 243, 28, 28}},
{"RadioButton",
"value2",
FPDF_FORMFIELD_RADIOBUTTON,
49154,
true,
2,
1,
{252, 243, 27, 28}},
{"PushButton",
"",
FPDF_FORMFIELD_PUSHBUTTON,
65536,
false,
0,
-1,
{118, 270, 55, 67}}};
TestClient client;
std::unique_ptr<PDFiumEngine> engine =
InitializeEngine(&client, FILE_PATH_LITERAL("form_buttons.pdf"));
ASSERT_TRUE(engine);
ASSERT_EQ(1, engine->GetNumberOfPages());
PDFiumPage* page = GetPDFiumPageForTest(engine.get(), 0);
ASSERT_TRUE(page);
page->PopulateAnnotations();
size_t buttons_count = page->buttons_.size();
ASSERT_EQ(base::size(kExpectedButtons), buttons_count);
for (size_t i = 0; i < buttons_count; ++i) {
EXPECT_EQ(kExpectedButtons[i].name, page->buttons_[i].name);
EXPECT_EQ(kExpectedButtons[i].value, page->buttons_[i].value);
EXPECT_EQ(kExpectedButtons[i].type, page->buttons_[i].type);
EXPECT_EQ(kExpectedButtons[i].flags, page->buttons_[i].flags);
EXPECT_EQ(kExpectedButtons[i].is_checked, page->buttons_[i].is_checked);
EXPECT_EQ(kExpectedButtons[i].control_count,
page->buttons_[i].control_count);
EXPECT_EQ(kExpectedButtons[i].control_index,
page->buttons_[i].control_index);
CompareRect(kExpectedButtons[i].bounding_rect,
page->buttons_[i].bounding_rect);
}
}
using PDFiumPageOverlappingTest = PDFiumTestBase; using PDFiumPageOverlappingTest = PDFiumTestBase;
// The following scenarios are covered across both test cases: // The following scenarios are covered across both test cases:
......
{{header}}
{{object 1 0}} <<
/Type /Catalog
/Pages 2 0 R
/AcroForm <<
/Fields [5 0 R 6 0 R 7 0 R 10 0 R]
>>
>>
endobj
{{object 2 0}} <<
/Type /Pages
/Count 1
/Kids [3 0 R]
>>
endobj
{{object 3 0}} <<
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 300 300]
/Resources <<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 4 0 R
>>
>>
/Annots [5 0 R 6 0 R 8 0 R 9 0 R 10 0 R]
>>
endobj
{{object 4 0}} <<
/Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Courier
>>
endobj
{{object 5 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Btn
/F 4
/Ff 1
/P 3 0 R
/Rect [135 250 155 270]
/T (readOnlyCheckbox)
/TU (readOnlyCheckbox)
/AP <<
/N <<
/Yes 11 0 R
/Off 12 0 R
>>
/D <<
/Yes 11 0 R
/Off 12 0 R
>>
/R <<
/Yes 11 0 R
/Off 12 0 R
>>
>>
/AS /Yes
/BS <<
/S /S
/W 1
>>
/H /N
/MK <<
/BC [1 0 1]
/BG [1 .752941 .796078]
/CA (4)
>>
/V /Yes
>>
endobj
{{object 6 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Btn
/F 4
/Ff 2
/P 3 0 R
/Rect [135 210 155 230]
/T (checkbox)
/TU (checkbox)
/AP <<
/N <<
/Yes 11 0 R
/Off 12 0 R
>>
/D <<
/Yes 11 0 R
/Off 12 0 R
>>
/R <<
/Yes 11 0 R
/Off 12 0 R
>>
>>
/AS /Off
/BS <<
/S /S
/W 2
>>
/H /N
/MK <<
/BC [.1 .1 .1]
/BG [.8 .843 1]
/CA (5)
>>
/V /Off
>>
endobj
{{object 7 0}} <<
/FT /Btn
/Ff 49154
/T (RadioButton)
/TU (RadioButton1)
/Kids [8 0 R 9 0 R]
/V /value2
>>
endobj
{{object 8 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Btn
/F 4
/P 3 0 R
/Parent 7 0 R
/Rect [135 100 155 120]
/AP <<
/N <<
/value1 13 0 R
/Off 14 0 R
>>
/D <<
/value1 13 0 R
/Off 14 0 R
>>
/R <<
/value1 13 0 R
/Off 14 0 R
>>
>>
/AS /Off
/BS <<
/S /S
/W 2
>>
/H /N
/MK <<
/BC [0 .501961 0]
/BG [0 0 1]
/CA (5)
>>
>>
endobj
{{object 9 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Btn
/F 4
/P 3 0 R
/Parent 7 0 R
/Rect [185 100 205 120]
/AP <<
/N <<
/value2 13 0 R
/Off 14 0 R
>>
/D <<
/value2 13 0 R
/Off 14 0 R
>>
/R <<
/value2 13 0 R
/Off 14 0 R
>>
>>
/AS /value2
/BS <<
/S /S
/W 2
>>
/H /N
/MK <<
/BC [0 .501961 0]
/BG [0 0 1]
/CA (5)
>>
>>
endobj
{{object 10 0}} <<
/Type /Annot
/Subtype /Widget
/F 4
/FT /Btn
/Rect [85 50 125 100]
/T (PushButton)
/Ff 65536
/H
/P
/MK <<
/BC [1 0 1]
/BG [1 .752941 .796078]
/CA (Submit)
>>
>>
endobj
{{object 11 0}} <<
/Type /XObject
/Subtype /Form
/FormType 1
/BBox [0 0 20 20]
/Resources <<
/ProcSet [/PDF]
>>
{{streamlen}}
>>
stream
q
1 0 1 RG 1 w 0.5 0.5 19 19 re s
Q
q
0 0 1 rg 0 0 1 RG
11.90655 14.48997 m
14.54638 17.62618 16.37396 18.07744 17.59234 18.07744 c
6.807382 2.418942 6.672006 2.148189 6.446379 2.012813 c
6.153064 1.877437 5.476184 1.877437 5.160306 1.877437 c
4.438301 1.877437 4.280362 1.877437 4.122423 1.990251 c
3.896797 2.148189 3.783983 2.464067 3.626045 2.779944 c
3.129666 3.817827 2.249721 6.074095 2.249721 7.202228 c
2.249721 7.585794 2.475348 7.766295 2.768663 7.969359 c
3.242479 8.307799 3.919359 8.691365 4.505989 8.691365 c
4.957242 8.691365 5.002368 8.307799 5.137744 7.946797 c
f
Q
endstream
endobj
{{object 12 0}} <<
/Type /XObject
/Subtype /Form
/FormType 1
/BBox [0 0 20 20]
/Resources <<
/ProcSet [/PDF]
>>
{{streamlen}}
>>
stream
q
1 0 1 RG 1 w 0.5 0.5 19 19 re s
Q
endstream
endobj
{{object 13 0}} <<
/Type /XObject
/Subtype /Form
/FormType 1
/BBox [0 0 20 20]
/Resources <<
/ProcSet [/PDF]
>>
{{streamlen}}
>>
stream
q
0 .501961 0 RG 2 w
1 0 0 1 10 10 cm
9 0 m
9 4.97079 4.97079 9 0 9 c
-4.97079 9 -9 4.97079 -9 0 c
-9 -4.97079 -4.97079 -9 0 -9 c
4.97079 -9 9 -4.97079 9 0 c
s
Q
q
1 .752941 .796078 rg 1 .752941 .796078 RG
17.2 15.95145 m
11.20694 10 l
17.2 4.027746 l
15.97225 2.8 l
10 8.793064 l
4.027746 2.8 l
2.8 4.027746 l
8.813873 10 l
2.8 15.97225 l
4.027746 17.2 l
10 11.20694 l
15.97225 17.2 l
17.2 15.95145 l
h
f
Q
endstream
endobj
{{object 14 0}} <<
/Type /XObject
/Subtype /Form
/FormType 1
/BBox [0 0 20 20]
/Resources <<
/ProcSet [/PDF]
>>
{{streamlen}}
>>
stream
q
0 .501961 0 RG 2 w
1 0 0 1 10 10 cm
9 0 m
9 4.97079 4.97079 9 0 9 c
-4.97079 9 -9 4.97079 -9 0 c
-9 -4.97079 -4.97079 -9 0 -9 c
4.97079 -9 9 -4.97079 9 0 c
s
Q
endstream
endobj
{{xref}}
{{trailer}}
{{startxref}}
%%EOF
This diff was suppressed by a .gitattributes entry.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment