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