1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "form_filler.h"
18
19 #include <algorithm>
20 #include <cmath>
21 #include <memory>
22 #include <span>
23 #include <string>
24 #include <utility>
25 #include <vector>
26
27 #include "cpp/fpdf_scopers.h"
28 #include "document.h"
29 #include "form_widget_info.h"
30 #include "fpdf_annot.h"
31 #include "fpdf_formfill.h"
32 #include "fpdf_fwlevent.h"
33 #include "fpdfview.h"
34 #include "pdfClient_formfillinfo.h"
35 #include "rect.h"
36 #include "utils/annot.h"
37 #include "utils/text.h"
38 #include "utils/utf.h"
39
40 // Utility methods to group common widget type and feature checks.
41 namespace {
IsClickActionType(int type)42 bool IsClickActionType(int type) {
43 return type == FPDF_FORMFIELD_PUSHBUTTON || type == FPDF_FORMFIELD_CHECKBOX ||
44 type == FPDF_FORMFIELD_RADIOBUTTON;
45 }
46
IsChoiceType(int type)47 bool IsChoiceType(int type) {
48 return type == FPDF_FORMFIELD_COMBOBOX || type == FPDF_FORMFIELD_LISTBOX;
49 }
50
IsTextFieldType(int type)51 bool IsTextFieldType(int type) {
52 return type == FPDF_FORMFIELD_TEXTFIELD;
53 }
54
IsSupportedType(int type)55 bool IsSupportedType(int type) {
56 return IsClickActionType(type) || IsChoiceType(type) || IsTextFieldType(type);
57 }
58
IsCheckType(int type)59 bool IsCheckType(int type) {
60 return type == FPDF_FORMFIELD_CHECKBOX || type == FPDF_FORMFIELD_RADIOBUTTON;
61 }
62
IsReadOnly(int formfield_flags)63 bool IsReadOnly(int formfield_flags) {
64 return (FPDF_FORMFLAG_READONLY & formfield_flags) != 0;
65 }
66
IsMultiSelect(int type,int formfield_flags)67 bool IsMultiSelect(int type, int formfield_flags) {
68 return type == FPDF_FORMFIELD_LISTBOX && ((1 << 21) & formfield_flags) != 0;
69 }
70
IsMultiLineText(int type,int formfield_flags)71 bool IsMultiLineText(int type, int formfield_flags) {
72 return type == FPDF_FORMFIELD_TEXTFIELD && (FPDF_FORMFLAG_TEXT_MULTILINE & formfield_flags) != 0;
73 }
74
IsEditableText(int type,int formfield_flags)75 bool IsEditableText(int type, int formfield_flags) {
76 if (type == FPDF_FORMFIELD_TEXTFIELD) {
77 return true;
78 }
79
80 if (type != FPDF_FORMFIELD_COMBOBOX) {
81 return false;
82 }
83 return (FPDF_FORMFLAG_CHOICE_EDIT & formfield_flags) != 0;
84 }
85 } // namespace
86
87 namespace {}
88 namespace pdfClient {
89
90 static const int kDefaultFormOperationModifiers = 0;
91 static const int kPdfiumACharacterOffset = 1; // see CPWL_EditCtrl::OnChar
92 static const Rectangle_i kDefaultAnnotationRect = IntRect(-1, -1, -1, -1);
93
FormFiller(Document * document,FPDF_DOCUMENT fpdf_document)94 FormFiller::FormFiller(Document* document, FPDF_DOCUMENT fpdf_document) : document_(document) {
95 // FPDF_FORMFILLINFO interface.
96 pdfClient::StubFormFillInfo(this);
97 FFI_Invalidate = &Invalidate;
98 form_handle_.reset(FPDFDOC_InitFormFillEnvironment(fpdf_document, this));
99 }
100
~FormFiller()101 FormFiller::~FormFiller() {}
102
RenderTile(FPDF_PAGE page,FPDF_BITMAP bitmap,FS_MATRIX transform,FS_RECTF clip,int render_mode) const103 bool FormFiller::RenderTile(FPDF_PAGE page, FPDF_BITMAP bitmap, FS_MATRIX transform, FS_RECTF clip,
104 int render_mode) const {
105 if (form_handle_) {
106 // This renders forms - checkboxes, text fields, and annotations.
107 FPDF_FFLDrawWithMatrix(form_handle_.get(), bitmap, page, &transform, &clip, render_mode);
108 return true;
109 }
110 return false;
111 }
112
NotifyAfterPageLoad(FPDF_PAGE page)113 void FormFiller::NotifyAfterPageLoad(FPDF_PAGE page) {
114 FORM_OnAfterLoadPage(page, form_handle_.get());
115 }
116
NotifyBeforePageClose(FPDF_PAGE page)117 void FormFiller::NotifyBeforePageClose(FPDF_PAGE page) {
118 FORM_OnBeforeClosePage(page, form_handle_.get());
119 }
120
GetFormWidgetInfo(FPDF_PAGE page,const Point_d point)121 FormWidgetInfo FormFiller::GetFormWidgetInfo(FPDF_PAGE page, const Point_d point) {
122 ScopedFPDFAnnotation annotation = GetFormAnnotation(page, point);
123 return GetFormWidgetInfo(page, annotation.get());
124 }
125
GetFormWidgetInfo(FPDF_PAGE page,const int annotation_index)126 FormWidgetInfo FormFiller::GetFormWidgetInfo(FPDF_PAGE page, const int annotation_index) {
127 ScopedFPDFAnnotation annotation = GetFormAnnotation(page, annotation_index);
128 return GetFormWidgetInfo(page, annotation.get());
129 }
130
GetFormWidgetInfo(FPDF_PAGE page,FPDF_ANNOTATION annotation)131 FormWidgetInfo FormFiller::GetFormWidgetInfo(FPDF_PAGE page, FPDF_ANNOTATION annotation) {
132 if (!page || !annotation) {
133 return FormWidgetInfo();
134 }
135
136 int type = GetFormFieldType(page, annotation);
137
138 // No form filling operation, no index to return.
139 if (!IsSupportedType(type)) {
140 return FormWidgetInfo();
141 }
142
143 int formfield_flags = FPDFAnnot_GetFormFieldFlags(form_handle_.get(), annotation);
144
145 FormWidgetInfo result;
146 result.set_widget_type(type);
147 result.set_widget_index(GetAnnotationIndex(page, annotation));
148 result.set_widget_rect(GetAnnotationRect(annotation));
149 result.set_accessibility_label(GetAccessibilityLabel(annotation));
150
151 // No form filling operation permitted, valid widget info to return.
152 if (IsReadOnly(formfield_flags)) {
153 result.set_read_only(true);
154 // Provide the best value we can at this point for screen reading.
155 result.set_text_value(GetReadOnlyTextValue(type, annotation));
156 return result;
157 }
158
159 // We have all the info we need already, return.
160 if (IsClickActionType(type)) {
161 result.set_text_value(GetReadOnlyTextValue(type, annotation));
162 return result;
163 }
164
165 SetFormFocus(page, annotation);
166
167 result.set_text_value(pdfClient_utils::FORM_GetFocusedText(form_handle_.get(), page));
168
169 if (IsChoiceType(type)) {
170 result.set_options(GetOptions(page, annotation));
171 if (type == FPDF_FORMFIELD_LISTBOX) {
172 result.set_multiselect(IsMultiSelect(type, formfield_flags));
173 }
174 }
175
176 bool editable_text = IsEditableText(type, formfield_flags);
177 result.set_editable_text(editable_text);
178
179 if (editable_text) {
180 result.set_max_length(GetMaxLen(annotation));
181 result.set_font_size(GetFontSize(annotation));
182 }
183
184 if (type == FPDF_FORMFIELD_TEXTFIELD) {
185 result.set_multi_line_text(IsMultiLineText(type, formfield_flags));
186 }
187
188 KillFormFocus();
189
190 return result;
191 }
192
GetFormWidgetInfos(FPDF_PAGE page,const std::unordered_set<int> & type_ids,std::vector<FormWidgetInfo> * widget_infos)193 void FormFiller::GetFormWidgetInfos(FPDF_PAGE page, const std::unordered_set<int>& type_ids,
194 std::vector<FormWidgetInfo>* widget_infos) {
195 std::vector<ScopedFPDFAnnotation> widget_annots;
196 pdfClient_utils::GetVisibleAnnotsOfType(page, {FPDF_ANNOT_WIDGET}, &widget_annots);
197
198 if (widget_annots.empty()) {
199 return;
200 }
201
202 bool filter_by_type = !type_ids.empty();
203 for (const auto& widget_annot : widget_annots) {
204 if (filter_by_type &&
205 !(type_ids.find(GetFormFieldType(page, widget_annot.get())) != type_ids.end())) {
206 continue;
207 }
208 FormWidgetInfo result = GetFormWidgetInfo(page, widget_annot.get());
209 if (result.FoundWidget()) {
210 widget_infos->push_back(std::move(result));
211 }
212 }
213 }
214
ClickOnPoint(FPDF_PAGE page,const Point_d point)215 bool FormFiller::ClickOnPoint(FPDF_PAGE page, const Point_d point) {
216 int type = GetFormFieldType(page, point);
217
218 if (!IsClickActionType(type)) {
219 return false;
220 }
221
222 ScopedFPDFAnnotation annotation = GetFormAnnotation(page, point);
223 int formfield_flags = FPDFAnnot_GetFormFieldFlags(form_handle_.get(), annotation.get());
224
225 if (IsReadOnly(formfield_flags)) {
226 return false;
227 }
228
229 PerformClick(page, point);
230 KillFormFocus();
231 return true;
232 }
233
SetText(FPDF_PAGE page,const int annotation_index,const std::string_view text)234 bool FormFiller::SetText(FPDF_PAGE page, const int annotation_index, const std::string_view text) {
235 ScopedFPDFAnnotation annotation = GetFormAnnotation(page, annotation_index);
236 if (!annotation) {
237 return false;
238 }
239
240 int formfield_flags = FPDFAnnot_GetFormFieldFlags(form_handle_.get(), annotation.get());
241
242 if (IsReadOnly(formfield_flags)) {
243 return false;
244 }
245
246 int type = GetFormFieldType(page, annotation.get());
247 if (!IsEditableText(type, formfield_flags)) {
248 return false;
249 }
250
251 SetFormFocus(page, annotation.get());
252 SetFieldText(page, text);
253 KillFormFocus();
254
255 return true;
256 }
257
SetChoiceSelection(FPDF_PAGE page,const int annotation_index,std::span<const int> selected_indices)258 bool FormFiller::SetChoiceSelection(FPDF_PAGE page, const int annotation_index,
259 std::span<const int> selected_indices) {
260 ScopedFPDFAnnotation annotation = GetFormAnnotation(page, annotation_index);
261 if (!annotation) {
262 return false;
263 }
264
265 int type = GetFormFieldType(page, annotation.get());
266 int formfield_flags = FPDFAnnot_GetFormFieldFlags(form_handle_.get(), annotation.get());
267
268 if (!IsChoiceType(type) || IsReadOnly(formfield_flags)) {
269 return false;
270 }
271
272 int option_count = FPDFAnnot_GetOptionCount(form_handle_.get(), annotation.get());
273
274 // Confirm all requested indices valid.
275 for (int i = 0; i < selected_indices.size(); i++) {
276 if (selected_indices[i] < 0 || selected_indices[i] >= option_count) {
277 return false;
278 }
279 }
280
281 // Combobox must have exactly one selection.
282 if (type == FPDF_FORMFIELD_COMBOBOX && selected_indices.size() != 1) {
283 return false;
284 }
285
286 // Non-multiselect Listbox must have 0 or 1 selections.
287 if (type == FPDF_FORMFIELD_LISTBOX && !IsMultiSelect(type, formfield_flags) &&
288 selected_indices.size() > 1) {
289 return false;
290 }
291
292 SetFormFocus(page, annotation.get());
293
294 if (type == FPDF_FORMFIELD_COMBOBOX) {
295 FORM_SetIndexSelected(form_handle_.get(), page, selected_indices[0], true);
296 } else {
297 // Deselect all indices.
298 for (int i = 0; i < option_count; i++) {
299 FORM_SetIndexSelected(form_handle_.get(), page, i, false);
300 }
301
302 // Select the requested indices.
303 for (int i = 0; i < selected_indices.size(); i++) {
304 FORM_SetIndexSelected(form_handle_.get(), page, selected_indices[i], true);
305 }
306 }
307 KillFormFocus();
308 return true;
309 }
310
IsWidget(FPDF_ANNOTATION annotation)311 bool FormFiller::IsWidget(FPDF_ANNOTATION annotation) {
312 return FPDFAnnot_GetSubtype(annotation) == FPDF_ANNOT_WIDGET;
313 }
314
GetFormAnnotation(FPDF_PAGE page,Point_d point)315 ScopedFPDFAnnotation FormFiller::GetFormAnnotation(FPDF_PAGE page, Point_d point) {
316 const FS_POINTF point_f = {static_cast<float>(point.x), static_cast<float>(point.y)};
317 FPDF_ANNOTATION annotation = FPDFAnnot_GetFormFieldAtPoint(form_handle_.get(), page, &point_f);
318
319 return ScopedFPDFAnnotation(annotation);
320 }
321
GetFormAnnotation(FPDF_PAGE page,int index)322 ScopedFPDFAnnotation FormFiller::GetFormAnnotation(FPDF_PAGE page, int index) {
323 FPDF_ANNOTATION annotation = FPDFPage_GetAnnot(page, index);
324
325 if (!annotation || !IsWidget(annotation)) {
326 return nullptr;
327 }
328
329 return ScopedFPDFAnnotation(annotation);
330 }
331
GetFormFieldType(FPDF_PAGE page,Point_d point)332 int FormFiller::GetFormFieldType(FPDF_PAGE page, Point_d point) {
333 return FPDFPage_HasFormFieldAtPoint(form_handle_.get(), page, point.x, point.y);
334 }
335
GetFormFieldType(FPDF_PAGE page,FPDF_ANNOTATION annotation)336 int FormFiller::GetFormFieldType(FPDF_PAGE page, FPDF_ANNOTATION annotation) {
337 Rectangle_i rect = GetAnnotationRect(annotation);
338 double mid_height = rect.top - ((rect.top - rect.bottom) / 2);
339 double mid_width = rect.right - ((rect.right - rect.left) / 2);
340 return GetFormFieldType(page, DoublePoint(mid_width, mid_height));
341 }
342
GetAnnotationRect(FPDF_ANNOTATION annotation)343 Rectangle_i FormFiller::GetAnnotationRect(FPDF_ANNOTATION annotation) {
344 FS_RECTF rect;
345 FPDF_BOOL success = FPDFAnnot_GetRect(annotation, &rect);
346
347 if (!success) {
348 return kDefaultAnnotationRect;
349 }
350
351 return Rectangle_i{
352 static_cast<int>(std::floor(rect.left)), static_cast<int>(std::ceil(rect.top)),
353 static_cast<int>(std::ceil(rect.right)), static_cast<int>(std::floor(rect.bottom))};
354 }
355
GetAnnotationIndex(FPDF_PAGE page,FPDF_ANNOTATION annotation)356 int FormFiller::GetAnnotationIndex(FPDF_PAGE page, FPDF_ANNOTATION annotation) {
357 return FPDFPage_GetAnnotIndex(page, annotation);
358 }
359
GetOptionCount(FPDF_ANNOTATION annotation)360 int FormFiller::GetOptionCount(FPDF_ANNOTATION annotation) {
361 return FPDFAnnot_GetOptionCount(form_handle_.get(), annotation);
362 }
363
GetOptions(FPDF_PAGE page,FPDF_ANNOTATION annotation)364 std::vector<Option> FormFiller::GetOptions(FPDF_PAGE page, FPDF_ANNOTATION annotation) {
365 int option_count = GetOptionCount(annotation);
366 std::vector<Option> options;
367 options.reserve(std::max(option_count, 0));
368
369 for (int i = 0; i < option_count; i++) {
370 std::string label =
371 pdfClient_utils::FPDFAnnot_GetOptionLabel(form_handle_.get(), annotation, i);
372 FPDF_BOOL selected = FORM_IsIndexSelected(form_handle_.get(), page, i);
373 options.push_back(Option{i, std::move(label), static_cast<bool>(selected)});
374 }
375 return options;
376 }
377
GetMaxLen(FPDF_ANNOTATION annotation)378 int FormFiller::GetMaxLen(FPDF_ANNOTATION annotation) {
379 float value;
380 FPDF_BOOL found = FPDFAnnot_GetNumberValue(annotation, "MaxLen", &value);
381 if (!found) {
382 return -1;
383 }
384
385 return static_cast<int>(value);
386 }
387
GetFontSize(FPDF_ANNOTATION annotation)388 float FormFiller::GetFontSize(FPDF_ANNOTATION annotation) {
389 float value;
390 if (!FPDFAnnot_GetFontSize(form_handle_.get(), annotation, &value)) {
391 return 0.f;
392 }
393 return value;
394 }
395
PerformClick(FPDF_PAGE page,const Point_d point)396 void FormFiller::PerformClick(FPDF_PAGE page, const Point_d point) {
397 FORM_OnMouseMove(form_handle_.get(), page, kDefaultFormOperationModifiers, point.x, point.y);
398 FORM_OnLButtonDown(form_handle_.get(), page, kDefaultFormOperationModifiers, point.x, point.y);
399 FORM_OnLButtonUp(form_handle_.get(), page, kDefaultFormOperationModifiers, point.x, point.y);
400 }
401
GetAccessibilityLabel(FPDF_ANNOTATION annotation)402 std::string FormFiller::GetAccessibilityLabel(FPDF_ANNOTATION annotation) {
403 std::string value = pdfClient_utils::FPDFAnnot_GetStringValue(annotation, "TU");
404
405 if (value.empty()) {
406 value = pdfClient_utils::FPDFAnnot_GetStringValue(annotation, "T");
407 }
408
409 return value;
410 }
411
GetReadOnlyTextValue(int type,FPDF_ANNOTATION annotation)412 std::string FormFiller::GetReadOnlyTextValue(int type, FPDF_ANNOTATION annotation) {
413 if (IsCheckType(type)) {
414 return FPDFAnnot_IsChecked(form_handle_.get(), annotation) ? "true" : "false";
415 }
416 return pdfClient_utils::FPDFAnnot_GetStringValue(annotation, "V");
417 }
418
SetFieldText(FPDF_PAGE page,std::string_view text)419 void FormFiller::SetFieldText(FPDF_PAGE page, std::string_view text) {
420 SelectAllFieldText(page);
421 ReplaceSelectedText(page, text);
422 }
423
SelectAllFieldText(FPDF_PAGE page)424 void FormFiller::SelectAllFieldText(FPDF_PAGE page) {
425 FORM_OnKeyDown(form_handle_.get(), page, kPdfiumACharacterOffset, FWL_EVENTFLAG_ControlKey);
426 FORM_OnChar(form_handle_.get(), page, kPdfiumACharacterOffset, FWL_EVENTFLAG_ControlKey);
427 FORM_OnKeyUp(form_handle_.get(), page, kPdfiumACharacterOffset, FWL_EVENTFLAG_ControlKey);
428 }
429
ReplaceSelectedText(FPDF_PAGE page,std::string_view replacement_text)430 void FormFiller::ReplaceSelectedText(FPDF_PAGE page, std::string_view replacement_text) {
431 std::u16string utf_sixteen_replacement_text = pdfClient_utils::Utf8ToUtf16Le(replacement_text);
432 FPDF_WIDESTRING widestring_replacement_text =
433 reinterpret_cast<FPDF_WIDESTRING>(utf_sixteen_replacement_text.c_str());
434 FORM_ReplaceSelection(form_handle_.get(), page, widestring_replacement_text);
435 }
436
SetFormFocus(FPDF_PAGE page,const Point_d point)437 bool FormFiller::SetFormFocus(FPDF_PAGE page, const Point_d point) {
438 return FORM_OnFocus(form_handle_.get(), page, kDefaultFormOperationModifiers, point.x, point.y);
439 }
440
SetFormFocus(FPDF_PAGE page,FPDF_ANNOTATION annotation)441 bool FormFiller::SetFormFocus(FPDF_PAGE page, FPDF_ANNOTATION annotation) {
442 Rectangle_i rect = GetAnnotationRect(annotation);
443 double mid_height = rect.top - ((rect.top - rect.bottom) / 2);
444 double mid_width = rect.right - ((rect.right - rect.left) / 2);
445 return SetFormFocus(page, DoublePoint(mid_width, mid_height));
446 }
447
KillFormFocus()448 bool FormFiller::KillFormFocus() {
449 return FORM_ForceToKillFocus(form_handle_.get());
450 }
451
Invalidate(FPDF_FORMFILLINFO * pThis,FPDF_PAGE page,double left,double top,double right,double bottom)452 void FormFiller::Invalidate(FPDF_FORMFILLINFO* pThis, FPDF_PAGE page, double left, double top,
453 double right, double bottom) {
454 FormFiller* form_filler = static_cast<FormFiller*>(pThis);
455 Rectangle_i rect = IntRect(left, top, right, bottom);
456 form_filler->document_->NotifyInvalidRect(page, rect);
457 }
458 } // namespace pdfClient