1//========================================================================
2//
3// Form.cc
4//
5// This file is licensed under the GPLv2 or later
6//
7// Copyright 2006-2008 Julien Rebetez <julienr@svn.gnome.org>
8// Copyright 2007-2012, 2015-2023 Albert Astals Cid <aacid@kde.org>
9// Copyright 2007-2008, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
10// Copyright 2007, 2013, 2016, 2019, 2022 Adrian Johnson <ajohnson@redneon.com>
11// Copyright 2007 Iñigo Martínez <inigomartinez@gmail.com>
12// Copyright 2008, 2011 Pino Toscano <pino@kde.org>
13// Copyright 2008 Michael Vrable <mvrable@cs.ucsd.edu>
14// Copyright 2009 Matthias Drochner <M.Drochner@fz-juelich.de>
15// Copyright 2009 KDAB via Guillermo Amaral <gamaral@amaral.com.mx>
16// Copyright 2010, 2012 Mark Riedesel <mark@klowner.com>
17// Copyright 2012 Fabio D'Urso <fabiodurso@hotmail.it>
18// Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
19// Copyright 2015 André Esser <bepandre@hotmail.com>
20// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
21// Copyright 2017 Bernd Kuhls <berndkuhls@hotmail.com>
22// Copyright 2018 Andre Heinecke <aheinecke@intevation.de>
23// Copyright 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
24// Copyright 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@protonmail.com>
25// Copyright 2018 Adam Reichold <adam.reichold@t-online.de>
26// Copyright 2018-2022 Nelson Benítez León <nbenitezl@gmail.com>
27// Copyright 2019, 2020 2024, Oliver Sander <oliver.sander@tu-dresden.de>
28// Copyright 2019 Tomoyuki Kubota <himajin100000@gmail.com>
29// Copyright 2019 João Netto <joaonetto901@gmail.com>
30// Copyright 2020-2022 Marek Kasik <mkasik@redhat.com>
31// Copyright 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de>
32// Copyright 2020, 2023, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
33// Copyright 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net.
34// Copyright 2021 Theofilos Intzoglou <int.teo@gmail.com>
35// Copyright 2021 Even Rouault <even.rouault@spatialys.com>
36// Copyright 2022 Alexander Sulfrian <asulfrian@zedat.fu-berlin.de>
37// Copyright 2022, 2024 Erich E. Hoover <erich.e.hoover@gmail.com>
38// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
39//
40//========================================================================
41
42#include <config.h>
43
44#include <array>
45#include <set>
46#include <limits>
47#include <cstddef>
48#include <cstdlib>
49#include <cstring>
50#include <cctype>
51#include "goo/ft_utils.h"
52#include "goo/gmem.h"
53#include "goo/gfile.h"
54#include "goo/GooString.h"
55#include "Error.h"
56#include "ErrorCodes.h"
57#include "CharCodeToUnicode.h"
58#include "Object.h"
59#include "Array.h"
60#include "Dict.h"
61#include "Gfx.h"
62#include "GfxFont.h"
63#include "GlobalParams.h"
64#include "Form.h"
65#include "PDFDoc.h"
66#include "DateInfo.h"
67#include "CryptoSignBackend.h"
68#include "SignatureInfo.h"
69#include "CertificateInfo.h"
70#include "XRef.h"
71#include "PDFDocEncoding.h"
72#include "Annot.h"
73#include "Link.h"
74#include "Lexer.h"
75#include "Parser.h"
76#include "CIDFontsWidthsBuilder.h"
77#include "UTF.h"
78
79#include "fofi/FoFiTrueType.h"
80#include "fofi/FoFiIdentifier.h"
81
82#include <ft2build.h>
83#include FT_FREETYPE_H
84#include <unordered_set>
85
86// helper for using std::visit to get a dependent false for static_asserts
87// to help get compile errors if one ever extends variants
88template<class>
89inline constexpr bool always_false_v = false;
90
91// return a newly allocated char* containing an UTF16BE string of size length
92char *pdfDocEncodingToUTF16(const std::string &orig, int *length)
93{
94 // double size, a unicode char takes 2 char, add 2 for the unicode marker
95 *length = 2 + 2 * orig.size();
96 char *result = new char[(*length)];
97 const char *cstring = orig.c_str();
98 // unicode marker
99 result[0] = '\xfe';
100 result[1] = '\xff';
101 // convert to utf16
102 for (int i = 2, j = 0; i < (*length); i += 2, j++) {
103 Unicode u = pdfDocEncoding[(unsigned int)((unsigned char)cstring[j])] & 0xffff;
104 result[i] = (u >> 8) & 0xff;
105 result[i + 1] = u & 0xff;
106 }
107 return result;
108}
109
110static GooString *convertToUtf16(GooString *pdfDocEncodingString)
111{
112 int tmp_length;
113 char *tmp_str = pdfDocEncodingToUTF16(orig: pdfDocEncodingString->toStr(), length: &tmp_length);
114 delete pdfDocEncodingString;
115 pdfDocEncodingString = new GooString(tmp_str + 2, tmp_length - 2); // Remove the unicode BOM
116 delete[] tmp_str;
117 return pdfDocEncodingString;
118}
119
120FormWidget::FormWidget(PDFDoc *docA, Object *aobj, unsigned num, Ref aref, FormField *fieldA)
121{
122 ref = aref;
123 ID = 0;
124 childNum = num;
125 doc = docA;
126 xref = doc->getXRef();
127 obj = aobj->copy();
128 type = formUndef;
129 field = fieldA;
130 widget = nullptr;
131}
132
133FormWidget::~FormWidget()
134{
135 if (widget) {
136 widget->decRefCnt();
137 }
138}
139
140void FormWidget::print(int indent)
141{
142 printf(format: "%*s+ (%d %d): [widget]\n", indent, "", ref.num, ref.gen);
143}
144
145void FormWidget::createWidgetAnnotation()
146{
147 if (widget) {
148 return;
149 }
150
151 Object obj1(ref);
152 widget = new AnnotWidget(doc, &obj, &obj1, field);
153}
154
155bool FormWidget::inRect(double x, double y) const
156{
157 return widget ? widget->inRect(x, y) : false;
158}
159
160void FormWidget::getRect(double *x1, double *y1, double *x2, double *y2) const
161{
162 if (widget) {
163 widget->getRect(x1, y1, x2, y2);
164 }
165}
166
167bool FormWidget::isReadOnly() const
168{
169 return field->isReadOnly();
170}
171
172void FormWidget::setReadOnly(bool value)
173{
174 field->setReadOnly(value);
175}
176
177int FormWidget::encodeID(unsigned pageNum, unsigned fieldNum)
178{
179 return (pageNum << 4 * sizeof(unsigned)) + fieldNum;
180}
181
182void FormWidget::decodeID(unsigned id, unsigned *pageNum, unsigned *fieldNum)
183{
184 *pageNum = id >> 4 * sizeof(unsigned);
185 *fieldNum = (id << 4 * sizeof(unsigned)) >> 4 * sizeof(unsigned);
186}
187
188const GooString *FormWidget::getPartialName() const
189{
190 return field->getPartialName();
191}
192
193void FormWidget::setPartialName(const GooString &name)
194{
195 field->setPartialName(name);
196}
197
198const GooString *FormWidget::getAlternateUiName() const
199{
200 return field->getAlternateUiName();
201}
202
203const GooString *FormWidget::getMappingName() const
204{
205 return field->getMappingName();
206}
207
208GooString *FormWidget::getFullyQualifiedName()
209{
210 return field->getFullyQualifiedName();
211}
212
213LinkAction *FormWidget::getActivationAction()
214{
215 return widget ? widget->getAction() : nullptr;
216}
217
218std::unique_ptr<LinkAction> FormWidget::getAdditionalAction(Annot::FormAdditionalActionsType t)
219{
220 return widget ? widget->getFormAdditionalAction(type: t) : nullptr;
221}
222
223bool FormWidget::setAdditionalAction(Annot::FormAdditionalActionsType t, const std::string &js)
224{
225 if (!widget) {
226 return false;
227 }
228
229 return widget->setFormAdditionalAction(type: t, js);
230}
231
232FormWidgetButton::FormWidgetButton(PDFDoc *docA, Object *dictObj, unsigned num, Ref refA, FormField *p) : FormWidget(docA, dictObj, num, refA, p)
233{
234 type = formButton;
235 onStr = nullptr;
236
237 // Find the name of the ON state in the AP dictionary
238 // The reference say the Off state, if it exists, _must_ be stored in the AP dict under the name /Off
239 // The "on" state may be stored under any other name
240 Object obj1 = obj.dictLookup(key: "AP");
241 if (obj1.isDict()) {
242 Object obj2 = obj1.dictLookup(key: "N");
243 if (obj2.isDict()) {
244 for (int i = 0; i < obj2.dictGetLength(); i++) {
245 const char *key = obj2.dictGetKey(i);
246 if (strcmp(s1: key, s2: "Off") != 0) {
247 onStr = new GooString(key);
248 break;
249 }
250 }
251 }
252 }
253}
254
255const char *FormWidgetButton::getOnStr() const
256{
257 if (onStr) {
258 return onStr->c_str();
259 }
260
261 // 12.7.4.2.3 Check Boxes
262 // Yes should be used as the name for the on state
263 return parent()->getButtonType() == formButtonCheck ? "Yes" : nullptr;
264}
265
266FormWidgetButton::~FormWidgetButton()
267{
268 delete onStr;
269}
270
271FormButtonType FormWidgetButton::getButtonType() const
272{
273 return parent()->getButtonType();
274}
275
276void FormWidgetButton::setAppearanceState(const char *state)
277{
278 if (!widget) {
279 return;
280 }
281 widget->setAppearanceState(state);
282}
283
284void FormWidgetButton::updateWidgetAppearance()
285{
286 // The appearance stream must NOT be regenerated for this widget type
287}
288
289void FormWidgetButton::setState(bool astate)
290{
291 // pushButtons don't have state
292 if (parent()->getButtonType() == formButtonPush) {
293 return;
294 }
295
296 // Silently return if can't set ON state
297 if (astate && !getOnStr()) {
298 return;
299 }
300
301 parent()->setState(state: astate ? getOnStr() : (char *)"Off");
302 // Parent will call setAppearanceState()
303
304 // Now handle standAlone fields which are related to this one by having the same
305 // fully qualified name. This is *partially* by spec, as seen in "Field names"
306 // section inside "8.6.2 Field Dictionaries" in 1.7 PDF spec. Issue #1034
307
308 if (!astate) { // We're only interested when this field is being set to ON,
309 return; // to check if it has related fields and then set them OFF
310 }
311
312 unsigned this_page_num, this_field_num;
313 decodeID(id: getID(), pageNum: &this_page_num, fieldNum: &this_field_num);
314 Page *this_page = doc->getCatalog()->getPage(i: this_page_num);
315 const FormField *this_field = getField();
316 if (!this_page->hasStandaloneFields() || this_field == nullptr) {
317 return;
318 }
319
320 auto this_page_widgets = this_page->getFormWidgets();
321 const FormButtonType this_button_type = getButtonType();
322
323 const int tot = this_page_widgets->getNumWidgets();
324 for (int i = 0; i < tot; i++) {
325 bool found_related = false;
326 FormWidget *wid = this_page_widgets->getWidget(i);
327 const bool same_fqn = wid->getFullyQualifiedName()->cmp(str: getFullyQualifiedName()) == 0;
328 const bool same_button_type = wid->getType() == formButton && static_cast<const FormWidgetButton *>(wid)->getButtonType() == this_button_type;
329
330 if (same_fqn && same_button_type) {
331 if (this_field->isStandAlone()) {
332 //'this_field' is standAlone, so we need to search in both standAlone fields and normal fields
333 if (this_field != wid->getField()) { // so take care to not choose our same field
334 found_related = true;
335 }
336 } else {
337 //'this_field' is not standAlone, so we just need to search in standAlone fields
338 if (wid->getField()->isStandAlone()) {
339 found_related = true;
340 }
341 }
342 }
343
344 if (found_related) {
345 FormFieldButton *ffb = static_cast<FormFieldButton *>(wid->getField());
346 if (ffb == nullptr) {
347 error(category: errInternal, pos: -1, msg: "FormWidgetButton::setState : FormFieldButton expected\n");
348 continue;
349 }
350 ffb->setState(state: (char *)"Off", ignoreToggleOff: true);
351 }
352 }
353}
354
355bool FormWidgetButton::getState() const
356{
357 return getOnStr() ? parent()->getState(state: getOnStr()) : false;
358}
359
360FormFieldButton *FormWidgetButton::parent() const
361{
362 return static_cast<FormFieldButton *>(field);
363}
364
365FormWidgetText::FormWidgetText(PDFDoc *docA, Object *dictObj, unsigned num, Ref refA, FormField *p) : FormWidget(docA, dictObj, num, refA, p)
366{
367 type = formText;
368}
369
370const GooString *FormWidgetText::getContent() const
371{
372 return parent()->getContent();
373}
374
375void FormWidgetText::updateWidgetAppearance()
376{
377 if (widget) {
378 widget->updateAppearanceStream();
379 }
380}
381
382bool FormWidgetText::isMultiline() const
383{
384 return parent()->isMultiline();
385}
386
387bool FormWidgetText::isPassword() const
388{
389 return parent()->isPassword();
390}
391
392bool FormWidgetText::isFileSelect() const
393{
394 return parent()->isFileSelect();
395}
396
397bool FormWidgetText::noSpellCheck() const
398{
399 return parent()->noSpellCheck();
400}
401
402bool FormWidgetText::noScroll() const
403{
404 return parent()->noScroll();
405}
406
407bool FormWidgetText::isComb() const
408{
409 return parent()->isComb();
410}
411
412bool FormWidgetText::isRichText() const
413{
414 return parent()->isRichText();
415}
416
417int FormWidgetText::getMaxLen() const
418{
419 return parent()->getMaxLen();
420}
421
422double FormWidgetText::getTextFontSize()
423{
424 return parent()->getTextFontSize();
425}
426
427void FormWidgetText::setTextFontSize(int fontSize)
428{
429 parent()->setTextFontSize(fontSize);
430}
431
432void FormWidgetText::setContent(const GooString *new_content)
433{
434 parent()->setContentCopy(new_content);
435}
436
437void FormWidgetText::setAppearanceContent(const GooString *new_content)
438{
439 parent()->setAppearanceContentCopy(new_content);
440}
441
442FormFieldText *FormWidgetText::parent() const
443{
444 return static_cast<FormFieldText *>(field);
445}
446
447FormWidgetChoice::FormWidgetChoice(PDFDoc *docA, Object *dictObj, unsigned num, Ref refA, FormField *p) : FormWidget(docA, dictObj, num, refA, p)
448{
449 type = formChoice;
450}
451
452FormWidgetChoice::~FormWidgetChoice() { }
453
454bool FormWidgetChoice::_checkRange(int i) const
455{
456 if (i < 0 || i >= parent()->getNumChoices()) {
457 error(category: errInternal, pos: -1, msg: "FormWidgetChoice::_checkRange i out of range : {0:d}", i);
458 return false;
459 }
460 return true;
461}
462
463void FormWidgetChoice::select(int i)
464{
465 if (!_checkRange(i)) {
466 return;
467 }
468 parent()->select(i);
469}
470
471void FormWidgetChoice::toggle(int i)
472{
473 if (!_checkRange(i)) {
474 return;
475 }
476 parent()->toggle(i);
477}
478
479void FormWidgetChoice::deselectAll()
480{
481 parent()->deselectAll();
482}
483
484const GooString *FormWidgetChoice::getEditChoice() const
485{
486 if (!hasEdit()) {
487 error(category: errInternal, pos: -1, msg: "FormFieldChoice::getEditChoice called on a non-editable choice\n");
488 return nullptr;
489 }
490 return parent()->getEditChoice();
491}
492
493void FormWidgetChoice::updateWidgetAppearance()
494{
495 if (widget) {
496 widget->updateAppearanceStream();
497 }
498}
499
500bool FormWidgetChoice::isSelected(int i) const
501{
502 if (!_checkRange(i)) {
503 return false;
504 }
505 return parent()->isSelected(i);
506}
507
508void FormWidgetChoice::setEditChoice(const GooString *new_content)
509{
510 if (!hasEdit()) {
511 error(category: errInternal, pos: -1, msg: "FormFieldChoice::setEditChoice : trying to edit an non-editable choice\n");
512 return;
513 }
514
515 parent()->setEditChoice(new_content);
516}
517
518int FormWidgetChoice::getNumChoices() const
519{
520 return parent()->getNumChoices();
521}
522
523const GooString *FormWidgetChoice::getChoice(int i) const
524{
525 return parent()->getChoice(i);
526}
527
528const GooString *FormWidgetChoice::getExportVal(int i) const
529{
530 return parent()->getExportVal(i);
531}
532
533bool FormWidgetChoice::isCombo() const
534{
535 return parent()->isCombo();
536}
537
538bool FormWidgetChoice::hasEdit() const
539{
540 return parent()->hasEdit();
541}
542
543bool FormWidgetChoice::isMultiSelect() const
544{
545 return parent()->isMultiSelect();
546}
547
548bool FormWidgetChoice::noSpellCheck() const
549{
550 return parent()->noSpellCheck();
551}
552
553bool FormWidgetChoice::commitOnSelChange() const
554{
555 return parent()->commitOnSelChange();
556}
557
558bool FormWidgetChoice::isListBox() const
559{
560 return parent()->isListBox();
561}
562
563FormFieldChoice *FormWidgetChoice::parent() const
564{
565 return static_cast<FormFieldChoice *>(field);
566}
567
568FormWidgetSignature::FormWidgetSignature(PDFDoc *docA, Object *dictObj, unsigned num, Ref refA, FormField *p) : FormWidget(docA, dictObj, num, refA, p)
569{
570 type = formSignature;
571}
572
573const GooString *FormWidgetSignature::getSignature() const
574{
575 return static_cast<FormFieldSignature *>(field)->getSignature();
576}
577
578SignatureInfo *FormWidgetSignature::validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function<void()> &doneCallback)
579{
580 return static_cast<FormFieldSignature *>(field)->validateSignatureAsync(doVerifyCert, forceRevalidation, validationTime, ocspRevocationCheck, enableAIA, doneCallback);
581}
582
583CertificateValidationStatus FormWidgetSignature::validateSignatureResult()
584{
585 return static_cast<FormFieldSignature *>(field)->validateSignatureResult();
586}
587
588// update hash with the specified range of data from the file
589static bool hashFileRange(FILE *f, CryptoSign::SigningInterface *handler, Goffset start, Goffset end)
590{
591 if (!handler) {
592 return false;
593 }
594 const int BUF_SIZE = 65536;
595
596 unsigned char *buf = new unsigned char[BUF_SIZE];
597
598 while (start < end) {
599 if (Gfseek(f, offset: start, SEEK_SET) != 0) {
600 delete[] buf;
601 return false;
602 }
603 int len = BUF_SIZE;
604 if (end - start < len) {
605 len = static_cast<int>(end - start);
606 }
607 if (fread(ptr: buf, size: 1, n: len, stream: f) != static_cast<size_t>(len)) {
608 delete[] buf;
609 return false;
610 }
611 handler->addData(data_block: buf, data_len: len);
612 start += len;
613 }
614 delete[] buf;
615 return true;
616}
617
618bool FormWidgetSignature::signDocument(const std::string &saveFilename, const std::string &certNickname, const std::string &password, const GooString *reason, const GooString *location, const std::optional<GooString> &ownerPassword,
619 const std::optional<GooString> &userPassword)
620{
621 auto backend = CryptoSign::Factory::createActive();
622 if (!backend) {
623 return false;
624 }
625 if (certNickname.empty()) {
626 fprintf(stderr, format: "signDocument: Empty nickname\n");
627 return false;
628 }
629
630 auto sigHandler = backend->createSigningHandler(certID: certNickname, digestAlgTag: HashAlgorithm::Sha256);
631
632 FormFieldSignature *signatureField = static_cast<FormFieldSignature *>(field);
633 std::unique_ptr<X509CertificateInfo> certInfo = sigHandler->getCertificateInfo();
634 if (!certInfo) {
635 fprintf(stderr, format: "signDocument: error getting signature info\n");
636 return false;
637 }
638 const std::string signerName = certInfo->getSubjectInfo().commonName;
639 signatureField->setCertificateInfo(certInfo);
640 updateWidgetAppearance(); // add visible signing info to appearance
641
642 Object vObj(new Dict(xref));
643 Ref vref = xref->addIndirectObject(o: vObj);
644 if (!createSignature(vObj, vRef: vref, name: GooString(signerName), placeholderLength: CryptoSign::maxSupportedSignatureSize, reason, location)) {
645 return false;
646 }
647
648 // Incremental save to avoid breaking any existing signatures
649 const GooString fname(saveFilename);
650 if (doc->saveAs(name: fname, mode: writeForceIncremental) != errNone) {
651 fprintf(stderr, format: "signDocument: error saving to file \"%s\"\n", saveFilename.c_str());
652 return false;
653 }
654
655 // Get start/end offset of signature object in the saved PDF
656 Goffset objStart, objEnd;
657 if (!getObjectStartEnd(filename: fname, objNum: vref.num, objStart: &objStart, objEnd: &objEnd, ownerPassword, userPassword)) {
658 fprintf(stderr, format: "signDocument: unable to get signature object offsets\n");
659 return false;
660 }
661
662 // Update byte range of signature in the saved PDF
663 Goffset sigStart, sigEnd, fileSize;
664 FILE *file = openFile(path: saveFilename.c_str(), mode: "r+b");
665 if (!updateOffsets(f: file, objStart, objEnd, sigStart: &sigStart, sigEnd: &sigEnd, fileSize: &fileSize)) {
666 fprintf(stderr, format: "signDocument: unable update byte range\n");
667 fclose(stream: file);
668 return false;
669 }
670
671 // compute hash of byte ranges
672 if (!hashFileRange(f: file, handler: sigHandler.get(), start: 0LL, end: sigStart)) {
673 fclose(stream: file);
674 return false;
675 }
676 if (!hashFileRange(f: file, handler: sigHandler.get(), start: sigEnd, end: fileSize)) {
677 fclose(stream: file);
678 return false;
679 }
680
681 // and sign it
682 auto signature = sigHandler->signDetached(password);
683 if (!signature) {
684 fclose(stream: file);
685 return false;
686 }
687
688 if (signature->getLength() > CryptoSign::maxSupportedSignatureSize) {
689 fclose(stream: file);
690 return false;
691 }
692
693 // pad with zeroes to placeholder length
694 auto length = signature->getLength();
695 signature->append(str: std::string(CryptoSign::maxSupportedSignatureSize - length, '\0'));
696
697 // write signature to saved file
698 if (!updateSignature(f: file, sigStart, sigEnd, signature: signature.value())) {
699 fprintf(stderr, format: "signDocument: unable update signature\n");
700 fclose(stream: file);
701 return false;
702 }
703 signatureField->setSignature(*signature);
704
705 fclose(stream: file);
706
707 return true;
708}
709
710static std::tuple<double, double> calculateDxDy(int rot, const PDFRectangle *rect)
711{
712 switch (rot) {
713 case 90:
714 return { rect->y2 - rect->y1, rect->x2 - rect->x1 };
715
716 case 180:
717 return { rect->x2 - rect->y2, rect->y2 - rect->y1 };
718
719 case 270:
720 return { rect->y2 - rect->y1, rect->x2 - rect->x1 };
721
722 default: // assume rot == 0
723 return { rect->x2 - rect->x1, rect->y2 - rect->y1 };
724 }
725}
726
727bool FormWidgetSignature::signDocumentWithAppearance(const std::string &saveFilename, const std::string &certNickname, const std::string &password, const GooString *reason, const GooString *location,
728 const std::optional<GooString> &ownerPassword, const std::optional<GooString> &userPassword, const GooString &signatureText, const GooString &signatureTextLeft, double fontSize,
729 double leftFontSize, std::unique_ptr<AnnotColor> &&fontColor, double borderWidth, std::unique_ptr<AnnotColor> &&borderColor, std::unique_ptr<AnnotColor> &&backgroundColor)
730{
731 // Set the appearance
732 GooString *aux = getField()->getDefaultAppearance();
733 std::string originalDefaultAppearance = aux ? aux->toStr() : std::string();
734
735 Form *form = doc->getCatalog()->getCreateForm();
736 const std::string pdfFontName = form->findPdfFontNameToUseForSigning();
737 if (pdfFontName.empty()) {
738 return false;
739 }
740 std::shared_ptr<GfxFont> font = form->getDefaultResources()->lookupFont(name: pdfFontName.c_str());
741
742 double x1, y1, x2, y2;
743 getRect(x1: &x1, y1: &y1, x2: &x2, y2: &y2);
744 const PDFRectangle rect(x1, y1, x2, y2);
745 std::unique_ptr<AnnotAppearanceCharacs> origAppearCharacs = getWidgetAnnotation()->getAppearCharacs() ? getWidgetAnnotation()->getAppearCharacs()->copy() : nullptr;
746 const int rot = origAppearCharacs ? origAppearCharacs->getRotation() : 0;
747 const auto dxdy = calculateDxDy(rot, rect: &rect);
748 const double dx = std::get<0>(t: dxdy);
749 const double dy = std::get<1>(t: dxdy);
750 const double wMax = dx - 2 * borderWidth - 4;
751 const double hMax = dy - 2 * borderWidth;
752 if (fontSize == 0) {
753 fontSize = Annot::calculateFontSize(form, font: font.get(), text: &signatureText, wMax: wMax / 2.0, hMax);
754 }
755 if (leftFontSize == 0) {
756 leftFontSize = Annot::calculateFontSize(form, font: font.get(), text: &signatureTextLeft, wMax: wMax / 2.0, hMax);
757 }
758 const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) };
759 getField()->setDefaultAppearance(da.toAppearanceString());
760
761 auto appearCharacs = std::make_unique<AnnotAppearanceCharacs>(args: nullptr);
762 appearCharacs->setBorderColor(std::move(borderColor));
763 appearCharacs->setBackColor(std::move(backgroundColor));
764 getWidgetAnnotation()->setAppearCharacs(std::move(appearCharacs));
765
766 std::unique_ptr<AnnotBorder> origBorderCopy = getWidgetAnnotation()->getBorder() ? getWidgetAnnotation()->getBorder()->copy() : nullptr;
767 std::unique_ptr<AnnotBorder> border(new AnnotBorderArray());
768 border->setWidth(borderWidth);
769 getWidgetAnnotation()->setBorder(std::move(border));
770
771 getWidgetAnnotation()->generateFieldAppearance();
772 getWidgetAnnotation()->updateAppearanceStream();
773
774 form->ensureFontsForAllCharacters(unicodeText: &signatureText, pdfFontNameToEmulate: pdfFontName);
775 form->ensureFontsForAllCharacters(unicodeText: &signatureTextLeft, pdfFontNameToEmulate: pdfFontName);
776
777 ::FormFieldSignature *ffs = static_cast<::FormFieldSignature *>(getField());
778 ffs->setCustomAppearanceContent(signatureText);
779 ffs->setCustomAppearanceLeftContent(signatureTextLeft);
780 ffs->setCustomAppearanceLeftFontSize(leftFontSize);
781
782 // say that there a now signatures and that we should append only
783 doc->getCatalog()->getAcroForm()->dictSet(key: "SigFlags", val: Object(3));
784
785 const bool success = signDocument(saveFilename, certNickname, password, reason, location, ownerPassword, userPassword);
786
787 // Now bring back the annotation appearance back to what it was
788 ffs->setDefaultAppearance(originalDefaultAppearance);
789 ffs->setCustomAppearanceContent({});
790 ffs->setCustomAppearanceLeftContent({});
791 getWidgetAnnotation()->setAppearCharacs(std::move(origAppearCharacs));
792 getWidgetAnnotation()->setBorder(std::move(origBorderCopy));
793 getWidgetAnnotation()->generateFieldAppearance();
794 getWidgetAnnotation()->updateAppearanceStream();
795
796 return success;
797}
798
799// Get start and end file position of objNum in the PDF named filename.
800bool FormWidgetSignature::getObjectStartEnd(const GooString &filename, int objNum, Goffset *objStart, Goffset *objEnd, const std::optional<GooString> &ownerPassword, const std::optional<GooString> &userPassword)
801{
802 PDFDoc newDoc(std::unique_ptr<GooString>(filename.copy()), ownerPassword, userPassword);
803 if (!newDoc.isOk()) {
804 return false;
805 }
806
807 XRef *newXref = newDoc.getXRef();
808 XRefEntry *entry = newXref->getEntry(i: objNum);
809 if (entry->type != xrefEntryUncompressed) {
810 return false;
811 }
812
813 *objStart = entry->offset;
814 newXref->fetch(num: objNum, gen: entry->gen, recursion: 0, endPos: objEnd);
815 return true;
816}
817
818// find next offset containing the dummy offset '9999999999' and overwrite with offset
819static char *setNextOffset(char *start, Goffset offset)
820{
821 char buf[50];
822 sprintf(s: buf, format: "%lld", offset);
823 strcat(dest: buf, src: " "); // add some padding
824
825 char *p = strstr(haystack: start, needle: "9999999999");
826 if (p) {
827 memcpy(dest: p, src: buf, n: 10); // overwrite exact size.
828 p += 10;
829 } else {
830 return nullptr;
831 }
832 return p;
833}
834
835// Updates the ByteRange array of the signature object in the file.
836// Returns start/end of signature string and file size.
837bool FormWidgetSignature::updateOffsets(FILE *f, Goffset objStart, Goffset objEnd, Goffset *sigStart, Goffset *sigEnd, Goffset *fileSize)
838{
839 if (Gfseek(f, offset: 0, SEEK_END) != 0) {
840 return false;
841 }
842 *fileSize = Gftell(f);
843
844 if (objEnd > *fileSize) {
845 objEnd = *fileSize;
846 }
847
848 // sanity check object offsets
849 if (objEnd <= objStart || (objEnd - objStart >= INT_MAX)) {
850 return false;
851 }
852
853 const size_t bufSize = static_cast<size_t>(objEnd - objStart);
854 if (Gfseek(f, offset: objStart, SEEK_SET) != 0) {
855 return false;
856 }
857 std::vector<char> buf(bufSize + 1);
858 if (fread(ptr: buf.data(), size: 1, n: bufSize, stream: f) != bufSize) {
859 return false;
860 }
861 buf[bufSize] = 0; // prevent string functions from searching past the end
862
863 // search for the Contents field which contains the signature placeholder
864 // which always must start with hex digits 000
865 *sigStart = -1;
866 *sigEnd = -1;
867 for (size_t i = 0; i < bufSize - 14; i++) {
868 if (buf[i] == '/' && strncmp(s1: &buf[i], s2: "/Contents <000", n: 14) == 0) {
869 *sigStart = objStart + i + 10;
870 char *p = strchr(s: &buf[i], c: '>');
871 if (p) {
872 *sigEnd = objStart + (p - buf.data()) + 1;
873 }
874 break;
875 }
876 }
877
878 if (*sigStart == -1 || *sigEnd == -1) {
879 return false;
880 }
881
882 // Search for ByteRange array and update offsets
883 for (size_t i = 0; i < bufSize - 10; i++) {
884 if (buf[i] == '/' && strncmp(s1: &buf[i], s2: "/ByteRange", n: 10) == 0) {
885 // update range
886 char *p = setNextOffset(start: &buf[i], offset: *sigStart);
887 if (!p) {
888 return false;
889 }
890 p = setNextOffset(start: p, offset: *sigEnd);
891 if (!p) {
892 return false;
893 }
894 p = setNextOffset(start: p, offset: *fileSize - *sigEnd);
895 if (!p) {
896 return false;
897 }
898 break;
899 }
900 }
901
902 // write buffer back to disk
903 if (Gfseek(f, offset: objStart, SEEK_SET) != 0) {
904 return false;
905 }
906 fwrite(ptr: buf.data(), size: bufSize, n: 1, s: f);
907 return true;
908}
909
910// Overwrite signature string in the file with new signature
911bool FormWidgetSignature::updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString &signature)
912{
913 if (signature.getLength() * 2 + 2 != sigEnd - sigStart) {
914 return false;
915 }
916
917 if (Gfseek(f, offset: sigStart, SEEK_SET) != 0) {
918 return false;
919 }
920 const char *c = signature.c_str();
921 fprintf(stream: f, format: "<");
922 for (int i = 0; i < signature.getLength(); i++) {
923 unsigned char value = *(c + i) & 0x000000ff;
924 fprintf(stream: f, format: "%2.2x", value);
925 }
926 fprintf(stream: f, format: "> ");
927 return true;
928}
929
930bool FormWidgetSignature::createSignature(Object &vObj, Ref vRef, const GooString &name, int placeholderLength, const GooString *reason, const GooString *location)
931{
932 vObj.dictAdd(key: "Type", val: Object(objName, "Sig"));
933 vObj.dictAdd(key: "Filter", val: Object(objName, "Adobe.PPKLite"));
934 vObj.dictAdd(key: "SubFilter", val: Object(objName, "adbe.pkcs7.detached"));
935 vObj.dictAdd(key: "Name", val: Object(name.copy()));
936 GooString *date = timeToDateString(timeA: nullptr);
937 vObj.dictAdd(key: "M", val: Object(date));
938 if (reason && (reason->getLength() > 0)) {
939 vObj.dictAdd(key: "Reason", val: Object(reason->copy()));
940 }
941 if (location && (location->getLength() > 0)) {
942 vObj.dictAdd(key: "Location", val: Object(location->copy()));
943 }
944
945 vObj.dictAdd(key: "Contents", val: Object(objHexString, new GooString(std::string(placeholderLength, '\0'))));
946 Object bObj(new Array(xref));
947 // reserve space in byte range for maximum number of bytes
948 bObj.arrayAdd(elem: Object(static_cast<long long>(0LL)));
949 bObj.arrayAdd(elem: Object(static_cast<long long>(9999999999LL)));
950 bObj.arrayAdd(elem: Object(static_cast<long long>(9999999999LL)));
951 bObj.arrayAdd(elem: Object(static_cast<long long>(9999999999LL)));
952 vObj.dictAdd(key: "ByteRange", val: bObj.copy());
953 obj.dictSet(key: "V", val: Object(vRef));
954 xref->setModifiedObject(o: &obj, r: ref);
955 return true;
956}
957
958std::vector<Goffset> FormWidgetSignature::getSignedRangeBounds() const
959{
960 return static_cast<FormFieldSignature *>(field)->getSignedRangeBounds();
961}
962
963std::optional<GooString> FormWidgetSignature::getCheckedSignature(Goffset *checkedFileSize)
964{
965 return static_cast<FormFieldSignature *>(field)->getCheckedSignature(checkedFileSize);
966}
967
968void FormWidgetSignature::updateWidgetAppearance()
969{
970 if (widget) {
971 widget->updateAppearanceStream();
972 }
973}
974
975//========================================================================
976// FormField
977//========================================================================
978
979FormField::FormField(PDFDoc *docA, Object &&aobj, const Ref aref, FormField *parentA, std::set<int> *usedParents, FormFieldType ty)
980{
981 doc = docA;
982 xref = doc->getXRef();
983 obj = std::move(aobj);
984 Dict *dict = obj.getDict();
985 ref = aref;
986 type = ty;
987 parent = parentA;
988 numChildren = 0;
989 children = nullptr;
990 terminal = false;
991 widgets = nullptr;
992 readOnly = false;
993 defaultAppearance = nullptr;
994 fullyQualifiedName = nullptr;
995 quadding = VariableTextQuadding::leftJustified;
996 hasQuadding = false;
997 standAlone = false;
998
999 // childs
1000 Object obj1 = dict->lookup(key: "Kids");
1001 if (obj1.isArray()) {
1002 // Load children
1003 for (int i = 0; i < obj1.arrayGetLength(); i++) {
1004 Ref childRef;
1005 Object childObj = obj1.getArray()->get(i, returnRef: &childRef);
1006 if (childRef == Ref::INVALID()) {
1007 error(category: errSyntaxError, pos: -1, msg: "Invalid form field renference");
1008 continue;
1009 }
1010 if (!childObj.isDict()) {
1011 error(category: errSyntaxError, pos: -1, msg: "Form field child is not a dictionary");
1012 continue;
1013 }
1014
1015 if (usedParents->find(x: childRef.num) == usedParents->end()) {
1016 // Field child: it could be a form field or a widget or composed dict
1017 const Object &objParent = childObj.dictLookupNF(key: "Parent");
1018 Object obj3 = childObj.dictLookup(key: "Parent");
1019 if (objParent.isRef() || obj3.isDict()) {
1020 // Child is a form field or composed dict
1021 // We create the field, if it's composed
1022 // it will create the widget as a child
1023 std::set<int> usedParentsAux = *usedParents;
1024 usedParentsAux.insert(x: childRef.num);
1025
1026 if (terminal) {
1027 error(category: errSyntaxWarning, pos: -1, msg: "Field can't have both Widget AND Field as kids\n");
1028 continue;
1029 }
1030
1031 numChildren++;
1032 children = (FormField **)greallocn(p: children, count: numChildren, size: sizeof(FormField *));
1033 children[numChildren - 1] = Form::createFieldFromDict(obj: std::move(childObj), docA: doc, aref: childRef, parent: this, usedParents: &usedParentsAux);
1034 } else {
1035 Object obj2 = childObj.dictLookup(key: "Subtype");
1036 if (obj2.isName(nameA: "Widget")) {
1037 // Child is a widget annotation
1038 if (!terminal && numChildren > 0) {
1039 error(category: errSyntaxWarning, pos: -1, msg: "Field can't have both Widget AND Field as kids\n");
1040 continue;
1041 }
1042 _createWidget(obj: &childObj, aref: childRef);
1043 }
1044 }
1045 }
1046 }
1047 } else {
1048 // No children, if it's a composed dict, create the child widget
1049 obj1 = dict->lookup(key: "Subtype");
1050 if (obj1.isName(nameA: "Widget")) {
1051 _createWidget(obj: &obj, aref: ref);
1052 }
1053 }
1054
1055 // flags
1056 obj1 = Form::fieldLookup(field: dict, key: "Ff");
1057 if (obj1.isInt()) {
1058 int flags = obj1.getInt();
1059 if (flags & 0x1) { // 1 -> ReadOnly
1060 readOnly = true;
1061 }
1062 if (flags & 0x2) { // 2 -> Required
1063 // TODO
1064 }
1065 if (flags & 0x4) { // 3 -> NoExport
1066 // TODO
1067 }
1068 }
1069
1070 // Variable Text
1071 obj1 = Form::fieldLookup(field: dict, key: "DA");
1072 if (obj1.isString()) {
1073 defaultAppearance = obj1.getString()->copy();
1074 }
1075
1076 obj1 = Form::fieldLookup(field: dict, key: "Q");
1077 if (obj1.isInt()) {
1078 const VariableTextQuadding aux = static_cast<VariableTextQuadding>(obj1.getInt());
1079 hasQuadding = aux == VariableTextQuadding::leftJustified || aux == VariableTextQuadding::centered || aux == VariableTextQuadding::rightJustified;
1080 if (likely(hasQuadding)) {
1081 quadding = static_cast<VariableTextQuadding>(aux);
1082 }
1083 }
1084
1085 obj1 = dict->lookup(key: "T");
1086 if (obj1.isString()) {
1087 partialName = obj1.getString()->copy();
1088 } else {
1089 partialName = nullptr;
1090 }
1091
1092 obj1 = dict->lookup(key: "TU");
1093 if (obj1.isString()) {
1094 alternateUiName = obj1.getString()->copy();
1095 } else {
1096 alternateUiName = nullptr;
1097 }
1098
1099 obj1 = dict->lookup(key: "TM");
1100 if (obj1.isString()) {
1101 mappingName = obj1.getString()->copy();
1102 } else {
1103 mappingName = nullptr;
1104 }
1105}
1106
1107void FormField::setDefaultAppearance(const std::string &appearance)
1108{
1109 delete defaultAppearance;
1110 defaultAppearance = new GooString(appearance);
1111}
1112
1113void FormField::setPartialName(const GooString &name)
1114{
1115 delete partialName;
1116 partialName = name.copy();
1117
1118 obj.getDict()->set(key: "T", val: Object(name.copy()));
1119 xref->setModifiedObject(o: &obj, r: ref);
1120}
1121
1122FormField::~FormField()
1123{
1124 if (!terminal) {
1125 if (children) {
1126 for (int i = 0; i < numChildren; i++) {
1127 delete children[i];
1128 }
1129 gfree(p: children);
1130 }
1131 } else {
1132 for (int i = 0; i < numChildren; ++i) {
1133 delete widgets[i];
1134 }
1135 gfree(p: widgets);
1136 }
1137
1138 delete defaultAppearance;
1139 delete partialName;
1140 delete alternateUiName;
1141 delete mappingName;
1142 delete fullyQualifiedName;
1143}
1144
1145void FormField::print(int indent)
1146{
1147 printf(format: "%*s- (%d %d): [container] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren);
1148}
1149
1150void FormField::printTree(int indent)
1151{
1152 print(indent);
1153 if (terminal) {
1154 for (int i = 0; i < numChildren; i++) {
1155 widgets[i]->print(indent: indent + 4);
1156 }
1157 } else {
1158 for (int i = 0; i < numChildren; i++) {
1159 children[i]->printTree(indent: indent + 4);
1160 }
1161 }
1162}
1163
1164void FormField::fillChildrenSiblingsID()
1165{
1166 if (terminal) {
1167 return;
1168 }
1169 for (int i = 0; i < numChildren; i++) {
1170 children[i]->fillChildrenSiblingsID();
1171 }
1172}
1173
1174void FormField::createWidgetAnnotations()
1175{
1176 if (terminal) {
1177 for (int i = 0; i < numChildren; i++) {
1178 widgets[i]->createWidgetAnnotation();
1179 }
1180 } else {
1181 for (int i = 0; i < numChildren; i++) {
1182 children[i]->createWidgetAnnotations();
1183 }
1184 }
1185}
1186
1187void FormField::_createWidget(Object *objA, Ref aref)
1188{
1189 terminal = true;
1190 numChildren++;
1191 widgets = (FormWidget **)greallocn(p: widgets, count: numChildren, size: sizeof(FormWidget *));
1192 // ID = index in "widgets" table
1193 switch (type) {
1194 case formButton:
1195 widgets[numChildren - 1] = new FormWidgetButton(doc, objA, numChildren - 1, aref, this);
1196 break;
1197 case formText:
1198 widgets[numChildren - 1] = new FormWidgetText(doc, objA, numChildren - 1, aref, this);
1199 break;
1200 case formChoice:
1201 widgets[numChildren - 1] = new FormWidgetChoice(doc, objA, numChildren - 1, aref, this);
1202 break;
1203 case formSignature:
1204 widgets[numChildren - 1] = new FormWidgetSignature(doc, objA, numChildren - 1, aref, this);
1205 break;
1206 default:
1207 error(category: errSyntaxWarning, pos: -1, msg: "SubType on non-terminal field, invalid document?");
1208 numChildren--;
1209 }
1210}
1211
1212FormWidget *FormField::findWidgetByRef(Ref aref)
1213{
1214 if (terminal) {
1215 for (int i = 0; i < numChildren; i++) {
1216 if (widgets[i]->getRef() == aref) {
1217 return widgets[i];
1218 }
1219 }
1220 } else {
1221 for (int i = 0; i < numChildren; i++) {
1222 FormWidget *result = children[i]->findWidgetByRef(aref);
1223 if (result) {
1224 return result;
1225 }
1226 }
1227 }
1228 return nullptr;
1229}
1230
1231GooString *FormField::getFullyQualifiedName()
1232{
1233 Object parentObj;
1234 const GooString *parent_name;
1235 bool unicode_encoded = false;
1236
1237 if (fullyQualifiedName) {
1238 return fullyQualifiedName;
1239 }
1240
1241 fullyQualifiedName = new GooString();
1242
1243 std::set<int> parsedRefs;
1244 Ref parentRef;
1245 parentObj = obj.getDict()->lookup(key: "Parent", returnRef: &parentRef);
1246 if (parentRef != Ref::INVALID()) {
1247 parsedRefs.insert(x: parentRef.num);
1248 }
1249 while (parentObj.isDict()) {
1250 Object obj2 = parentObj.dictLookup(key: "T");
1251 if (obj2.isString()) {
1252 parent_name = obj2.getString();
1253
1254 if (unicode_encoded) {
1255 fullyQualifiedName->insert(i: 0, str: "\0.", lengthA: 2); // 2-byte unicode period
1256 if (hasUnicodeByteOrderMark(s: parent_name->toStr())) {
1257 fullyQualifiedName->insert(i: 0, str: parent_name->c_str() + 2, lengthA: parent_name->getLength() - 2); // Remove the unicode BOM
1258 } else {
1259 int tmp_length;
1260 char *tmp_str = pdfDocEncodingToUTF16(orig: parent_name->toStr(), length: &tmp_length);
1261 fullyQualifiedName->insert(i: 0, str: tmp_str + 2, lengthA: tmp_length - 2); // Remove the unicode BOM
1262 delete[] tmp_str;
1263 }
1264 } else {
1265 fullyQualifiedName->insert(i: 0, c: '.'); // 1-byte ascii period
1266 if (hasUnicodeByteOrderMark(s: parent_name->toStr())) {
1267 unicode_encoded = true;
1268 fullyQualifiedName = convertToUtf16(pdfDocEncodingString: fullyQualifiedName);
1269 fullyQualifiedName->insert(i: 0, str: parent_name->c_str() + 2, lengthA: parent_name->getLength() - 2); // Remove the unicode BOM
1270 } else {
1271 fullyQualifiedName->insert(i: 0, str: parent_name);
1272 }
1273 }
1274 }
1275 parentObj = parentObj.getDict()->lookup(key: "Parent", returnRef: &parentRef);
1276 if (parentRef != Ref::INVALID() && !parsedRefs.insert(x: parentRef.num).second) {
1277 error(category: errSyntaxError, pos: -1, msg: "FormField: Loop while trying to look for Parents\n");
1278 return fullyQualifiedName;
1279 }
1280 }
1281
1282 if (partialName) {
1283 if (unicode_encoded) {
1284 if (hasUnicodeByteOrderMark(s: partialName->toStr())) {
1285 fullyQualifiedName->append(str: partialName->c_str() + 2, lengthA: partialName->getLength() - 2); // Remove the unicode BOM
1286 } else {
1287 int tmp_length;
1288 char *tmp_str = pdfDocEncodingToUTF16(orig: partialName->toStr(), length: &tmp_length);
1289 fullyQualifiedName->append(str: tmp_str + 2, lengthA: tmp_length - 2); // Remove the unicode BOM
1290 delete[] tmp_str;
1291 }
1292 } else {
1293 if (hasUnicodeByteOrderMark(s: partialName->toStr())) {
1294 unicode_encoded = true;
1295 fullyQualifiedName = convertToUtf16(pdfDocEncodingString: fullyQualifiedName);
1296 fullyQualifiedName->append(str: partialName->c_str() + 2, lengthA: partialName->getLength() - 2); // Remove the unicode BOM
1297 } else {
1298 fullyQualifiedName->append(str: partialName);
1299 }
1300 }
1301 } else {
1302 int len = fullyQualifiedName->getLength();
1303 // Remove the last period
1304 if (unicode_encoded) {
1305 if (len > 1) {
1306 fullyQualifiedName->del(i: len - 2, n: 2);
1307 }
1308 } else {
1309 if (len > 0) {
1310 fullyQualifiedName->del(i: len - 1, n: 1);
1311 }
1312 }
1313 }
1314
1315 if (unicode_encoded) {
1316 prependUnicodeByteOrderMark(s&: fullyQualifiedName->toNonConstStr());
1317 }
1318
1319 return fullyQualifiedName;
1320}
1321
1322void FormField::updateChildrenAppearance()
1323{
1324 // Recursively update each child's appearance
1325 if (terminal) {
1326 for (int i = 0; i < numChildren; i++) {
1327 widgets[i]->updateWidgetAppearance();
1328 }
1329 } else {
1330 for (int i = 0; i < numChildren; i++) {
1331 children[i]->updateChildrenAppearance();
1332 }
1333 }
1334}
1335
1336void FormField::setReadOnly(bool value)
1337{
1338 if (value == readOnly) {
1339 return;
1340 }
1341
1342 readOnly = value;
1343
1344 Dict *dict = obj.getDict();
1345
1346 const Object obj1 = Form::fieldLookup(field: dict, key: "Ff");
1347 int flags = 0;
1348 if (obj1.isInt()) {
1349 flags = obj1.getInt();
1350 }
1351 if (value) {
1352 flags |= 1;
1353 } else {
1354 flags &= ~1;
1355 }
1356
1357 dict->set(key: "Ff", val: Object(flags));
1358 xref->setModifiedObject(o: &obj, r: ref);
1359 updateChildrenAppearance();
1360}
1361
1362void FormField::reset(const std::vector<std::string> &excludedFields)
1363{
1364 resetChildren(excludedFields);
1365}
1366
1367void FormField::resetChildren(const std::vector<std::string> &excludedFields)
1368{
1369 if (!terminal) {
1370 for (int i = 0; i < numChildren; i++) {
1371 children[i]->reset(excludedFields);
1372 }
1373 }
1374}
1375
1376bool FormField::isAmongExcludedFields(const std::vector<std::string> &excludedFields)
1377{
1378 Ref fieldRef;
1379
1380 for (const std::string &field : excludedFields) {
1381 if (field.compare(pos: field.size() - 2, n1: 2, s: " R") == 0) {
1382 if (sscanf(s: field.c_str(), format: "%d %d R", &fieldRef.num, &fieldRef.gen) == 2 && fieldRef == getRef()) {
1383 return true;
1384 }
1385 } else {
1386 if (field == getFullyQualifiedName()->toStr()) {
1387 return true;
1388 }
1389 }
1390 }
1391
1392 return false;
1393}
1394
1395FormField *FormField::findFieldByRef(Ref aref)
1396{
1397 if (terminal) {
1398 if (this->getRef() == aref) {
1399 return this;
1400 }
1401 } else {
1402 for (int i = 0; i < numChildren; i++) {
1403 FormField *result = children[i]->findFieldByRef(aref);
1404 if (result) {
1405 return result;
1406 }
1407 }
1408 }
1409 return nullptr;
1410}
1411
1412FormField *FormField::findFieldByFullyQualifiedName(const std::string &name)
1413{
1414 if (terminal) {
1415 if (getFullyQualifiedName()->cmp(sA: name.c_str()) == 0) {
1416 return this;
1417 }
1418 } else {
1419 for (int i = 0; i < numChildren; i++) {
1420 FormField *result = children[i]->findFieldByFullyQualifiedName(name);
1421 if (result) {
1422 return result;
1423 }
1424 }
1425 }
1426 return nullptr;
1427}
1428
1429//------------------------------------------------------------------------
1430// FormFieldButton
1431//------------------------------------------------------------------------
1432FormFieldButton::FormFieldButton(PDFDoc *docA, Object &&dictObj, const Ref refA, FormField *parentA, std::set<int> *usedParents) : FormField(docA, std::move(dictObj), refA, parentA, usedParents, formButton)
1433{
1434 Dict *dict = obj.getDict();
1435 active_child = -1;
1436 noAllOff = false;
1437 siblings = nullptr;
1438 numSiblings = 0;
1439 appearanceState.setToNull();
1440 defaultAppearanceState.setToNull();
1441
1442 btype = formButtonCheck;
1443 Object obj1 = Form::fieldLookup(field: dict, key: "Ff");
1444 if (obj1.isInt()) {
1445 int flags = obj1.getInt();
1446
1447 if (flags & 0x10000) { // 17 -> push button
1448 btype = formButtonPush;
1449 } else if (flags & 0x8000) { // 16 -> radio button
1450 btype = formButtonRadio;
1451 if (flags & 0x4000) { // 15 -> noToggleToOff
1452 noAllOff = true;
1453 }
1454 }
1455 if (flags & 0x1000000) { // 26 -> radiosInUnison
1456 error(category: errUnimplemented, pos: -1, msg: "FormFieldButton:: radiosInUnison flag unimplemented, please report a bug with a testcase\n");
1457 }
1458 }
1459
1460 bool isChildRadiobutton = btype == formButtonRadio && terminal && parent && parent->getType() == formButton;
1461 // Ignore "V" for child radiobuttons, so FormFieldButton::getState() does not use it and instead uses the
1462 // "V" of the parent, which is the real value indicating the active field in the radio group. Issue #159
1463 if (btype != formButtonPush && !isChildRadiobutton) {
1464 // Even though V is inheritable we are interested in the value of this
1465 // field, if not present it's probably because it's a button in a set.
1466 appearanceState = dict->lookup(key: "V");
1467 defaultAppearanceState = Form::fieldLookup(field: dict, key: "DV");
1468 }
1469}
1470
1471static const char *_getButtonType(FormButtonType type)
1472{
1473 switch (type) {
1474 case formButtonPush:
1475 return "push";
1476 case formButtonCheck:
1477 return "check";
1478 case formButtonRadio:
1479 return "radio";
1480 default:
1481 break;
1482 }
1483 return "unknown";
1484}
1485
1486void FormFieldButton::print(int indent)
1487{
1488 printf(format: "%*s- (%d %d): [%s] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, _getButtonType(type: btype), terminal ? "Yes" : "No", numChildren);
1489}
1490
1491void FormFieldButton::setNumSiblings(int num)
1492{
1493 numSiblings = num;
1494 siblings = (FormFieldButton **)greallocn(p: siblings, count: numSiblings, size: sizeof(FormFieldButton *));
1495}
1496
1497void FormFieldButton::fillChildrenSiblingsID()
1498{
1499 if (!terminal) {
1500 for (int i = 0; i < numChildren; i++) {
1501 FormFieldButton *child = dynamic_cast<FormFieldButton *>(children[i]);
1502 if (child != nullptr) {
1503 // Fill the siblings of this node childs
1504 child->setNumSiblings(numChildren - 1);
1505 for (int j = 0, counter = 0; j < numChildren; j++) {
1506 FormFieldButton *otherChild = dynamic_cast<FormFieldButton *>(children[j]);
1507 if (i == j) {
1508 continue;
1509 }
1510 if (child == otherChild) {
1511 continue;
1512 }
1513 child->setSibling(i: counter, id: otherChild);
1514 counter++;
1515 }
1516
1517 // now call ourselves on the child
1518 // to fill its children data
1519 child->fillChildrenSiblingsID();
1520 }
1521 }
1522 }
1523}
1524
1525bool FormFieldButton::setState(const char *state, bool ignoreToggleOff)
1526{
1527 // A check button could behave as a radio button
1528 // when it's in a set of more than 1 buttons
1529 if (btype != formButtonRadio && btype != formButtonCheck) {
1530 return false;
1531 }
1532
1533 if (terminal && parent && parent->getType() == formButton && appearanceState.isNull()) {
1534 // It's button in a set, set state on parent
1535 if (static_cast<FormFieldButton *>(parent)->setState(state)) {
1536 return true;
1537 }
1538 return false;
1539 }
1540
1541 bool isOn = strcmp(s1: state, s2: "Off") != 0;
1542
1543 if (!isOn && noAllOff && !ignoreToggleOff) {
1544 return false; // Don't allow to set all radio to off
1545 }
1546
1547 const char *current = getAppearanceState();
1548 bool currentFound = false, newFound = false;
1549
1550 for (int i = 0; i < numChildren; i++) {
1551 FormWidgetButton *widget;
1552
1553 // If radio button is a terminal field we want the widget at i, but
1554 // if it's not terminal, the child widget is a composed dict, so
1555 // we want the ony child widget of the children at i
1556 if (terminal) {
1557 widget = static_cast<FormWidgetButton *>(widgets[i]);
1558 } else {
1559 widget = static_cast<FormWidgetButton *>(children[i]->getWidget(i: 0));
1560 }
1561
1562 if (!widget->getOnStr()) {
1563 continue;
1564 }
1565
1566 const char *onStr = widget->getOnStr();
1567 if (current && strcmp(s1: current, s2: onStr) == 0) {
1568 widget->setAppearanceState("Off");
1569 if (!isOn) {
1570 break;
1571 }
1572 currentFound = true;
1573 }
1574
1575 if (isOn && strcmp(s1: state, s2: onStr) == 0) {
1576 widget->setAppearanceState(state);
1577 newFound = true;
1578 }
1579
1580 if (currentFound && newFound) {
1581 break;
1582 }
1583 }
1584
1585 updateState(state);
1586
1587 return true;
1588}
1589
1590bool FormFieldButton::getState(const char *state) const
1591{
1592 if (appearanceState.isName(nameA: state)) {
1593 return true;
1594 }
1595
1596 return (parent && parent->getType() == formButton) ? static_cast<FormFieldButton *>(parent)->getState(state) : false;
1597}
1598
1599void FormFieldButton::updateState(const char *state)
1600{
1601 appearanceState = Object(objName, state);
1602 obj.getDict()->set(key: "V", val: appearanceState.copy());
1603 xref->setModifiedObject(o: &obj, r: ref);
1604}
1605
1606FormFieldButton::~FormFieldButton()
1607{
1608 if (siblings) {
1609 gfree(p: siblings);
1610 }
1611}
1612
1613void FormFieldButton::reset(const std::vector<std::string> &excludedFields)
1614{
1615 if (!isAmongExcludedFields(excludedFields)) {
1616 if (getDefaultAppearanceState()) {
1617 setState(state: getDefaultAppearanceState());
1618 } else {
1619 obj.getDict()->remove(key: "V");
1620
1621 // Clear check button if it doesn't have default value.
1622 // This behaviour is what Adobe Reader does, it is not written in specification.
1623 if (btype == formButtonCheck) {
1624 setState(state: "Off");
1625 }
1626 }
1627 }
1628
1629 resetChildren(excludedFields);
1630}
1631
1632//------------------------------------------------------------------------
1633// FormFieldText
1634//------------------------------------------------------------------------
1635FormFieldText::FormFieldText(PDFDoc *docA, Object &&dictObj, const Ref refA, FormField *parentA, std::set<int> *usedParents) : FormField(docA, std::move(dictObj), refA, parentA, usedParents, formText)
1636{
1637 Dict *dict = obj.getDict();
1638 Object obj1;
1639 content = nullptr;
1640 internalContent = nullptr;
1641 defaultContent = nullptr;
1642 multiline = password = fileSelect = doNotSpellCheck = doNotScroll = comb = richText = false;
1643 maxLen = 0;
1644
1645 obj1 = Form::fieldLookup(field: dict, key: "Ff");
1646 if (obj1.isInt()) {
1647 int flags = obj1.getInt();
1648 if (flags & 0x1000) { // 13 -> Multiline
1649 multiline = true;
1650 }
1651 if (flags & 0x2000) { // 14 -> Password
1652 password = true;
1653 }
1654 if (flags & 0x100000) { // 21 -> FileSelect
1655 fileSelect = true;
1656 }
1657 if (flags & 0x400000) { // 23 -> DoNotSpellCheck
1658 doNotSpellCheck = true;
1659 }
1660 if (flags & 0x800000) { // 24 -> DoNotScroll
1661 doNotScroll = true;
1662 }
1663 if (flags & 0x1000000) { // 25 -> Comb
1664 comb = true;
1665 }
1666 if (flags & 0x2000000) { // 26 -> RichText
1667 richText = true;
1668 }
1669 }
1670
1671 obj1 = Form::fieldLookup(field: dict, key: "MaxLen");
1672 if (obj1.isInt()) {
1673 maxLen = obj1.getInt();
1674 }
1675
1676 fillContent(fillType: fillDefaultValue);
1677 fillContent(fillType: fillValue);
1678}
1679
1680void FormFieldText::fillContent(FillValueType fillType)
1681{
1682 Dict *dict = obj.getDict();
1683 Object obj1;
1684
1685 obj1 = Form::fieldLookup(field: dict, key: fillType == fillDefaultValue ? "DV" : "V");
1686 if (obj1.isString()) {
1687 if (hasUnicodeByteOrderMark(s: obj1.getString()->toStr())) {
1688 if (obj1.getString()->getLength() > 2) {
1689 if (fillType == fillDefaultValue) {
1690 defaultContent = obj1.getString()->copy();
1691 } else {
1692 content = obj1.getString()->copy();
1693 }
1694 }
1695 } else if (obj1.getString()->getLength() > 0) {
1696 // non-unicode string -- assume pdfDocEncoding and try to convert to UTF16BE
1697 int tmp_length;
1698 char *tmp_str = pdfDocEncodingToUTF16(orig: obj1.getString()->toStr(), length: &tmp_length);
1699
1700 if (fillType == fillDefaultValue) {
1701 defaultContent = new GooString(tmp_str, tmp_length);
1702 } else {
1703 content = new GooString(tmp_str, tmp_length);
1704 }
1705
1706 delete[] tmp_str;
1707 }
1708 }
1709}
1710
1711void FormFieldText::print(int indent)
1712{
1713 printf(format: "%*s- (%d %d): [text] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren);
1714}
1715
1716void FormFieldText::setContentCopy(const GooString *new_content)
1717{
1718 delete content;
1719 content = nullptr;
1720
1721 if (new_content) {
1722 content = new_content->copy();
1723
1724 // append the unicode marker <FE FF> if needed
1725 if (!hasUnicodeByteOrderMark(s: content->toStr())) {
1726 prependUnicodeByteOrderMark(s&: content->toNonConstStr());
1727 }
1728 Form *form = doc->getCatalog()->getForm();
1729 if (form) {
1730 DefaultAppearance da(defaultAppearance);
1731 if (da.getFontName().isName()) {
1732 const std::string fontName = da.getFontName().getName();
1733 if (!fontName.empty()) {
1734 // Use the field resource dictionary if it exists
1735 Object fieldResourcesDictObj = obj.dictLookup(key: "DR");
1736 if (fieldResourcesDictObj.isDict()) {
1737 GfxResources fieldResources(doc->getXRef(), fieldResourcesDictObj.getDict(), form->getDefaultResources());
1738 const std::vector<Form::AddFontResult> newFonts = form->ensureFontsForAllCharacters(unicodeText: content, pdfFontNameToEmulate: fontName, fieldResources: &fieldResources);
1739 // If we added new fonts to the Form object default resuources we also need to add them (we only add the ref so this is cheap)
1740 // to the field DR dictionary
1741 for (const Form::AddFontResult &afr : newFonts) {
1742 fieldResourcesDictObj.dictLookup(key: "Font").dictAdd(key: afr.fontName.c_str(), val: Object(afr.ref));
1743 }
1744 } else {
1745 form->ensureFontsForAllCharacters(unicodeText: content, pdfFontNameToEmulate: fontName);
1746 }
1747 }
1748 } else {
1749 // This is wrong, there has to be a Tf in DA
1750 }
1751 }
1752 }
1753
1754 obj.getDict()->set(key: "V", val: Object(content ? content->copy() : new GooString("")));
1755 xref->setModifiedObject(o: &obj, r: ref);
1756 updateChildrenAppearance();
1757}
1758
1759void FormFieldText::setAppearanceContentCopy(const GooString *new_content)
1760{
1761 delete internalContent;
1762 internalContent = nullptr;
1763
1764 if (new_content) {
1765 internalContent = new_content->copy();
1766 }
1767 updateChildrenAppearance();
1768}
1769
1770FormFieldText::~FormFieldText()
1771{
1772 delete content;
1773 delete internalContent;
1774 delete defaultContent;
1775}
1776
1777void FormFieldText::reset(const std::vector<std::string> &excludedFields)
1778{
1779 if (!isAmongExcludedFields(excludedFields)) {
1780 setContentCopy(defaultContent);
1781 if (defaultContent == nullptr) {
1782 obj.getDict()->remove(key: "V");
1783 }
1784 }
1785
1786 resetChildren(excludedFields);
1787}
1788
1789double FormFieldText::getTextFontSize()
1790{
1791 std::vector<std::string> daToks;
1792 int idx = parseDA(daToks: &daToks);
1793 double fontSize = -1;
1794 if (idx >= 0) {
1795 char *p = nullptr;
1796 fontSize = strtod(nptr: daToks[idx].c_str(), endptr: &p);
1797 if (!p || *p) {
1798 fontSize = -1;
1799 }
1800 }
1801 return fontSize;
1802}
1803
1804void FormFieldText::setTextFontSize(int fontSize)
1805{
1806 if (fontSize > 0 && obj.isDict()) {
1807 std::vector<std::string> daToks;
1808 int idx = parseDA(daToks: &daToks);
1809 if (idx == -1) {
1810 error(category: errSyntaxError, pos: -1, msg: "FormFieldText:: invalid DA object\n");
1811 return;
1812 }
1813 if (defaultAppearance) {
1814 delete defaultAppearance;
1815 }
1816 defaultAppearance = new GooString;
1817 for (std::size_t i = 0; i < daToks.size(); ++i) {
1818 if (i > 0) {
1819 defaultAppearance->append(c: ' ');
1820 }
1821 if (i == (std::size_t)idx) {
1822 defaultAppearance->appendf(fmt: "{0:d}", fontSize);
1823 } else {
1824 defaultAppearance->append(str: daToks[i]);
1825 }
1826 }
1827 obj.dictSet(key: "DA", val: Object(defaultAppearance->copy()));
1828 xref->setModifiedObject(o: &obj, r: ref);
1829 updateChildrenAppearance();
1830 }
1831}
1832
1833int FormFieldText::tokenizeDA(const std::string &da, std::vector<std::string> *daToks, const char *searchTok)
1834{
1835 int idx = -1;
1836 if (daToks) {
1837 size_t i = 0;
1838 size_t j = 0;
1839 while (i < da.size()) {
1840 while (i < da.size() && Lexer::isSpace(c: da[i])) {
1841 ++i;
1842 }
1843 if (i < da.size()) {
1844 for (j = i + 1; j < da.size() && !Lexer::isSpace(c: da[j]); ++j) { }
1845 std::string tok(da, i, j - i);
1846 if (searchTok && tok == searchTok) {
1847 idx = daToks->size();
1848 }
1849 daToks->emplace_back(args: std::move(tok));
1850 i = j;
1851 }
1852 }
1853 }
1854 return idx;
1855}
1856
1857int FormFieldText::parseDA(std::vector<std::string> *daToks)
1858{
1859 int idx = -1;
1860 if (obj.isDict()) {
1861 Object objDA(obj.dictLookup(key: "DA"));
1862 if (objDA.isString()) {
1863 const GooString *da = objDA.getString();
1864 idx = tokenizeDA(da: da->toStr(), daToks, searchTok: "Tf") - 1;
1865 }
1866 }
1867 return idx;
1868}
1869
1870//------------------------------------------------------------------------
1871// FormFieldChoice
1872//------------------------------------------------------------------------
1873FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object &&aobj, const Ref refA, FormField *parentA, std::set<int> *usedParents) : FormField(docA, std::move(aobj), refA, parentA, usedParents, formChoice)
1874{
1875 numChoices = 0;
1876 choices = nullptr;
1877 defaultChoices = nullptr;
1878 editedChoice = nullptr;
1879 topIdx = 0;
1880
1881 Dict *dict = obj.getDict();
1882 Object obj1;
1883
1884 combo = edit = multiselect = doNotSpellCheck = doCommitOnSelChange = false;
1885
1886 obj1 = Form::fieldLookup(field: dict, key: "Ff");
1887 if (obj1.isInt()) {
1888 int flags = obj1.getInt();
1889 if (flags & 0x20000) { // 18 -> Combo
1890 combo = true;
1891 }
1892 if (flags & 0x40000) { // 19 -> Edit
1893 edit = true;
1894 }
1895 if (flags & 0x200000) { // 22 -> MultiSelect
1896 multiselect = true;
1897 }
1898 if (flags & 0x400000) { // 23 -> DoNotSpellCheck
1899 doNotSpellCheck = true;
1900 }
1901 if (flags & 0x4000000) { // 27 -> CommitOnSelChange
1902 doCommitOnSelChange = true;
1903 }
1904 }
1905
1906 obj1 = dict->lookup(key: "TI");
1907 if (obj1.isInt()) {
1908 topIdx = obj1.getInt();
1909 if (topIdx < 0) {
1910 error(category: errSyntaxError, pos: -1, msg: "FormFieldChoice:: invalid topIdx entry\n");
1911 topIdx = 0;
1912 }
1913 }
1914
1915 obj1 = Form::fieldLookup(field: dict, key: "Opt");
1916 if (obj1.isArray()) {
1917 numChoices = obj1.arrayGetLength();
1918 choices = new ChoiceOpt[numChoices];
1919 memset(s: choices, c: 0, n: sizeof(ChoiceOpt) * numChoices);
1920
1921 for (int i = 0; i < numChoices; i++) {
1922 Object obj2 = obj1.arrayGet(i);
1923 if (obj2.isString()) {
1924 choices[i].optionName = obj2.getString()->copy();
1925 } else if (obj2.isArray()) { // [Export_value, Displayed_text]
1926 if (obj2.arrayGetLength() < 2) {
1927 error(category: errSyntaxError, pos: -1, msg: "FormWidgetChoice:: invalid Opt entry -- array's length < 2\n");
1928 continue;
1929 }
1930 Object obj3 = obj2.arrayGet(i: 0);
1931 if (obj3.isString()) {
1932 choices[i].exportVal = obj3.getString()->copy();
1933 } else {
1934 error(category: errSyntaxError, pos: -1, msg: "FormWidgetChoice:: invalid Opt entry -- exported value not a string\n");
1935 }
1936
1937 obj3 = obj2.arrayGet(i: 1);
1938 if (obj3.isString()) {
1939 choices[i].optionName = obj3.getString()->copy();
1940 } else {
1941 error(category: errSyntaxError, pos: -1, msg: "FormWidgetChoice:: invalid Opt entry -- choice name not a string\n");
1942 }
1943 } else {
1944 error(category: errSyntaxError, pos: -1, msg: "FormWidgetChoice:: invalid {0:d} Opt entry\n", i);
1945 }
1946 }
1947 } else {
1948 // empty choice
1949 }
1950
1951 // Find selected items
1952 // Note: PDF specs say that /V has precedence over /I, but acroread seems to
1953 // do the opposite. We do the same.
1954 obj1 = Form::fieldLookup(field: dict, key: "I");
1955 if (obj1.isArray()) {
1956 for (int i = 0; i < obj1.arrayGetLength(); i++) {
1957 Object obj2 = obj1.arrayGet(i);
1958 if (obj2.isInt() && obj2.getInt() >= 0 && obj2.getInt() < numChoices) {
1959 choices[obj2.getInt()].selected = true;
1960 }
1961 }
1962 } else {
1963 // Note: According to PDF specs, /V should *never* contain the exportVal.
1964 // However, if /Opt is an array of (exportVal,optionName) pairs, acroread
1965 // seems to expect the exportVal instead of the optionName and so we do too.
1966 fillChoices(fillType: fillValue);
1967 }
1968
1969 fillChoices(fillType: fillDefaultValue);
1970}
1971
1972void FormFieldChoice::fillChoices(FillValueType fillType)
1973{
1974 const char *key = fillType == fillDefaultValue ? "DV" : "V";
1975 Dict *dict = obj.getDict();
1976 Object obj1;
1977
1978 obj1 = Form::fieldLookup(field: dict, key);
1979 if (obj1.isString() || obj1.isArray()) {
1980 if (fillType == fillDefaultValue) {
1981 defaultChoices = new bool[numChoices];
1982 memset(s: defaultChoices, c: 0, n: sizeof(bool) * numChoices);
1983 }
1984
1985 if (obj1.isString()) {
1986 bool optionFound = false;
1987
1988 for (int i = 0; i < numChoices; i++) {
1989 if (choices[i].exportVal) {
1990 if (choices[i].exportVal->cmp(str: obj1.getString()) == 0) {
1991 optionFound = true;
1992 }
1993 } else if (choices[i].optionName) {
1994 if (choices[i].optionName->cmp(str: obj1.getString()) == 0) {
1995 optionFound = true;
1996 }
1997 }
1998
1999 if (optionFound) {
2000 if (fillType == fillDefaultValue) {
2001 defaultChoices[i] = true;
2002 } else {
2003 choices[i].selected = true;
2004 }
2005 break; // We've determined that this option is selected. No need to keep on scanning
2006 }
2007 }
2008
2009 // Set custom value if /V doesn't refer to any predefined option and the field is user-editable
2010 if (fillType == fillValue && !optionFound && edit) {
2011 editedChoice = obj1.getString()->copy();
2012 }
2013 } else if (obj1.isArray()) {
2014 for (int i = 0; i < numChoices; i++) {
2015 for (int j = 0; j < obj1.arrayGetLength(); j++) {
2016 const Object obj2 = obj1.arrayGet(i: j);
2017 if (!obj2.isString()) {
2018 error(category: errSyntaxError, pos: -1, msg: "FormWidgetChoice:: {0:s} array contains a non string object", key);
2019 continue;
2020 }
2021
2022 bool matches = false;
2023
2024 if (choices[i].exportVal) {
2025 if (choices[i].exportVal->cmp(str: obj2.getString()) == 0) {
2026 matches = true;
2027 }
2028 } else if (choices[i].optionName) {
2029 if (choices[i].optionName->cmp(str: obj2.getString()) == 0) {
2030 matches = true;
2031 }
2032 }
2033
2034 if (matches) {
2035 if (fillType == fillDefaultValue) {
2036 defaultChoices[i] = true;
2037 } else {
2038 choices[i].selected = true;
2039 }
2040 break; // We've determined that this option is selected. No need to keep on scanning
2041 }
2042 }
2043 }
2044 }
2045 }
2046}
2047
2048FormFieldChoice::~FormFieldChoice()
2049{
2050 for (int i = 0; i < numChoices; i++) {
2051 delete choices[i].exportVal;
2052 delete choices[i].optionName;
2053 }
2054 delete[] choices;
2055 delete[] defaultChoices;
2056 delete editedChoice;
2057}
2058
2059void FormFieldChoice::print(int indent)
2060{
2061 printf(format: "%*s- (%d %d): [choice] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren);
2062}
2063
2064void FormFieldChoice::updateSelection()
2065{
2066 Object objV;
2067 Object objI(objNull);
2068
2069 if (edit && editedChoice) {
2070 // This is an editable combo-box with user-entered text
2071 objV = Object(editedChoice->copy());
2072 } else {
2073 const int numSelected = getNumSelected();
2074
2075 // Create /I array only if multiple selection is allowed (as per PDF spec)
2076 if (multiselect) {
2077 objI = Object(new Array(xref));
2078 }
2079
2080 if (numSelected == 0) {
2081 // No options are selected
2082 objV = Object(new GooString(""));
2083 } else if (numSelected == 1) {
2084 // Only one option is selected
2085 for (int i = 0; i < numChoices; i++) {
2086 if (choices[i].selected) {
2087 if (multiselect) {
2088 objI.arrayAdd(elem: Object(i));
2089 }
2090
2091 if (choices[i].exportVal) {
2092 objV = Object(choices[i].exportVal->copy());
2093 } else if (choices[i].optionName) {
2094 objV = Object(choices[i].optionName->copy());
2095 }
2096
2097 break; // We've just written the selected option. No need to keep on scanning
2098 }
2099 }
2100 } else {
2101 // More than one option is selected
2102 objV = Object(new Array(xref));
2103 for (int i = 0; i < numChoices; i++) {
2104 if (choices[i].selected) {
2105 if (multiselect) {
2106 objI.arrayAdd(elem: Object(i));
2107 }
2108
2109 if (choices[i].exportVal) {
2110 objV.arrayAdd(elem: Object(choices[i].exportVal->copy()));
2111 } else if (choices[i].optionName) {
2112 objV.arrayAdd(elem: Object(choices[i].optionName->copy()));
2113 }
2114 }
2115 }
2116 }
2117 }
2118
2119 obj.getDict()->set(key: "V", val: std::move(objV));
2120 obj.getDict()->set(key: "I", val: std::move(objI));
2121 xref->setModifiedObject(o: &obj, r: ref);
2122 updateChildrenAppearance();
2123}
2124
2125void FormFieldChoice::unselectAll()
2126{
2127 for (int i = 0; i < numChoices; i++) {
2128 choices[i].selected = false;
2129 }
2130}
2131
2132void FormFieldChoice::deselectAll()
2133{
2134 delete editedChoice;
2135 editedChoice = nullptr;
2136
2137 unselectAll();
2138 updateSelection();
2139}
2140
2141void FormFieldChoice::toggle(int i)
2142{
2143 delete editedChoice;
2144 editedChoice = nullptr;
2145
2146 choices[i].selected = !choices[i].selected;
2147 updateSelection();
2148}
2149
2150void FormFieldChoice::select(int i)
2151{
2152 delete editedChoice;
2153 editedChoice = nullptr;
2154
2155 if (!multiselect) {
2156 unselectAll();
2157 }
2158
2159 choices[i].selected = true;
2160 updateSelection();
2161}
2162
2163void FormFieldChoice::setEditChoice(const GooString *new_content)
2164{
2165 delete editedChoice;
2166 editedChoice = nullptr;
2167
2168 unselectAll();
2169
2170 if (new_content) {
2171 editedChoice = new_content->copy();
2172
2173 // append the unicode marker <FE FF> if needed
2174 if (!hasUnicodeByteOrderMark(s: editedChoice->toStr())) {
2175 prependUnicodeByteOrderMark(s&: editedChoice->toNonConstStr());
2176 }
2177 }
2178 updateSelection();
2179}
2180
2181const GooString *FormFieldChoice::getEditChoice() const
2182{
2183 return editedChoice;
2184}
2185
2186int FormFieldChoice::getNumSelected()
2187{
2188 int cnt = 0;
2189 for (int i = 0; i < numChoices; i++) {
2190 if (choices[i].selected) {
2191 cnt++;
2192 }
2193 }
2194 return cnt;
2195}
2196
2197const GooString *FormFieldChoice::getSelectedChoice() const
2198{
2199 if (edit && editedChoice) {
2200 return editedChoice;
2201 }
2202
2203 for (int i = 0; i < numChoices; i++) {
2204 if (choices[i].optionName && choices[i].selected) {
2205 return choices[i].optionName;
2206 }
2207 }
2208
2209 return nullptr;
2210}
2211
2212void FormFieldChoice::reset(const std::vector<std::string> &excludedFields)
2213{
2214 if (!isAmongExcludedFields(excludedFields)) {
2215 delete editedChoice;
2216 editedChoice = nullptr;
2217
2218 if (defaultChoices) {
2219 for (int i = 0; i < numChoices; i++) {
2220 choices[i].selected = defaultChoices[i];
2221 }
2222 } else {
2223 unselectAll();
2224 }
2225 }
2226
2227 resetChildren(excludedFields);
2228
2229 updateSelection();
2230}
2231
2232//------------------------------------------------------------------------
2233// FormFieldSignature
2234//------------------------------------------------------------------------
2235FormFieldSignature::FormFieldSignature(PDFDoc *docA, Object &&dict, const Ref refA, FormField *parentA, std::set<int> *usedParents)
2236 : FormField(docA, std::move(dict), refA, parentA, usedParents, formSignature), signature_type(unsigned_signature_field), signature(nullptr)
2237{
2238 signature_info = new SignatureInfo();
2239 parseInfo();
2240}
2241
2242FormFieldSignature::~FormFieldSignature()
2243{
2244 delete signature_info;
2245 delete signature;
2246}
2247
2248void FormFieldSignature::setSignature(const GooString &sig)
2249{
2250 delete signature;
2251 signature = sig.copy();
2252}
2253
2254const GooString &FormFieldSignature::getCustomAppearanceContent() const
2255{
2256 return customAppearanceContent;
2257}
2258
2259void FormFieldSignature::setCustomAppearanceContent(const GooString &s)
2260{
2261 customAppearanceContent = GooString(s.toStr());
2262}
2263
2264const GooString &FormFieldSignature::getCustomAppearanceLeftContent() const
2265{
2266 return customAppearanceLeftContent;
2267}
2268
2269void FormFieldSignature::setCustomAppearanceLeftContent(const GooString &s)
2270{
2271 customAppearanceLeftContent = GooString(s.toStr());
2272}
2273
2274double FormFieldSignature::getCustomAppearanceLeftFontSize() const
2275{
2276 return customAppearanceLeftFontSize;
2277}
2278
2279void FormFieldSignature::setCustomAppearanceLeftFontSize(double size)
2280{
2281 customAppearanceLeftFontSize = size;
2282}
2283
2284Ref FormFieldSignature::getImageResource() const
2285{
2286 return imageResource;
2287}
2288
2289void FormFieldSignature::setImageResource(const Ref imageResourceA)
2290{
2291 imageResource = imageResourceA;
2292}
2293
2294void FormFieldSignature::setCertificateInfo(std::unique_ptr<X509CertificateInfo> &certInfo)
2295{
2296 certificate_info.swap(u&: certInfo);
2297}
2298
2299FormWidget *FormFieldSignature::getCreateWidget()
2300{
2301 ::FormWidget *fw = getWidget(i: 0);
2302 if (!fw) {
2303 error(category: errSyntaxError, pos: 0, msg: "FormFieldSignature: was asked for widget and didn't had one, creating it");
2304 _createWidget(objA: &obj, aref: ref);
2305 fw = getWidget(i: 0);
2306 fw->createWidgetAnnotation();
2307 }
2308 return fw;
2309}
2310
2311void FormFieldSignature::parseInfo()
2312{
2313 if (!obj.isDict()) {
2314 return;
2315 }
2316
2317 // retrieve PKCS#7
2318 Object sig_dict = obj.dictLookup(key: "V");
2319 if (!sig_dict.isDict()) {
2320 return;
2321 }
2322
2323 Object contents_obj = sig_dict.dictLookup(key: "Contents");
2324 if (contents_obj.isString()) {
2325 signature = contents_obj.getString()->copy();
2326 }
2327
2328 byte_range = sig_dict.dictLookup(key: "ByteRange");
2329
2330 const Object location_obj = sig_dict.dictLookup(key: "Location");
2331 if (location_obj.isString()) {
2332 signature_info->setLocation(location_obj.getString());
2333 }
2334
2335 const Object reason_obj = sig_dict.dictLookup(key: "Reason");
2336 if (reason_obj.isString()) {
2337 signature_info->setReason(reason_obj.getString());
2338 }
2339
2340 // retrieve SigningTime
2341 Object time_of_signing = sig_dict.dictLookup(key: "M");
2342 if (time_of_signing.isString()) {
2343 const GooString *time_str = time_of_signing.getString();
2344 signature_info->setSigningTime(dateStringToTime(dateString: time_str)); // Put this information directly in SignatureInfo object
2345 }
2346
2347 // check if subfilter is supported for signature validation, only detached signatures work for now
2348 Object subfilterName = sig_dict.dictLookup(key: "SubFilter");
2349 if (subfilterName.isName(nameA: "adbe.pkcs7.sha1")) {
2350 signature_type = adbe_pkcs7_sha1;
2351 signature_info->setSubFilterSupport(true);
2352 } else if (subfilterName.isName(nameA: "adbe.pkcs7.detached")) {
2353 signature_type = adbe_pkcs7_detached;
2354 signature_info->setSubFilterSupport(true);
2355 } else if (subfilterName.isName(nameA: "ETSI.CAdES.detached")) {
2356 signature_type = ETSI_CAdES_detached;
2357 signature_info->setSubFilterSupport(true);
2358 } else {
2359 signature_type = unknown_signature_type;
2360 }
2361}
2362
2363void FormFieldSignature::hashSignedDataBlock(CryptoSign::VerificationInterface *handler, Goffset block_len)
2364{
2365 if (!handler) {
2366 return;
2367 }
2368 const int BLOCK_SIZE = 4096;
2369 unsigned char signed_data_buffer[BLOCK_SIZE];
2370
2371 Goffset i = 0;
2372 while (i < block_len) {
2373 Goffset bytes_left = block_len - i;
2374 if (bytes_left < BLOCK_SIZE) {
2375 doc->getBaseStream()->doGetChars(nChars: static_cast<int>(bytes_left), buffer: signed_data_buffer);
2376 handler->addData(data_block: signed_data_buffer, data_len: static_cast<int>(bytes_left));
2377 i = block_len;
2378 } else {
2379 doc->getBaseStream()->doGetChars(nChars: BLOCK_SIZE, buffer: signed_data_buffer);
2380 handler->addData(data_block: signed_data_buffer, data_len: BLOCK_SIZE);
2381 i += BLOCK_SIZE;
2382 }
2383 }
2384}
2385
2386FormSignatureType FormWidgetSignature::signatureType() const
2387{
2388 return static_cast<FormFieldSignature *>(field)->getSignatureType();
2389}
2390
2391void FormWidgetSignature::setSignatureType(FormSignatureType fst)
2392{
2393 static_cast<FormFieldSignature *>(field)->setSignatureType(fst);
2394}
2395
2396SignatureInfo *FormFieldSignature::validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function<void()> &doneCallback)
2397{
2398 auto backend = CryptoSign::Factory::createActive();
2399 if (!backend) {
2400 if (doneCallback) {
2401 doneCallback();
2402 }
2403 return signature_info;
2404 }
2405
2406 if (signature_info->getSignatureValStatus() != SIGNATURE_NOT_VERIFIED && !forceRevalidation) {
2407 if (doneCallback) {
2408 doneCallback();
2409 }
2410 return signature_info;
2411 }
2412
2413 if (signature == nullptr) {
2414 error(category: errSyntaxError, pos: 0, msg: "Invalid or missing Signature string");
2415 if (doneCallback) {
2416 doneCallback();
2417 }
2418 return signature_info;
2419 }
2420
2421 if (!byte_range.isArray()) {
2422 error(category: errSyntaxError, pos: 0, msg: "Invalid or missing ByteRange array");
2423 if (doneCallback) {
2424 doneCallback();
2425 }
2426 return signature_info;
2427 }
2428
2429 int arrayLen = byte_range.arrayGetLength();
2430 if (arrayLen < 2) {
2431 error(category: errSyntaxError, pos: 0, msg: "Too few elements in ByteRange array");
2432 if (doneCallback) {
2433 doneCallback();
2434 }
2435 return signature_info;
2436 }
2437
2438 const int signature_len = signature->getLength();
2439 std::vector<unsigned char> signatureData(signature_len);
2440 memcpy(dest: signatureData.data(), src: signature->c_str(), n: signature_len);
2441 signature_handler = backend->createVerificationHandler(pkcs7: std::move(signatureData));
2442
2443 Goffset fileLength = doc->getBaseStream()->getLength();
2444 for (int i = 0; i < arrayLen / 2; i++) {
2445 Object offsetObj = byte_range.arrayGet(i: i * 2);
2446 Object lenObj = byte_range.arrayGet(i: i * 2 + 1);
2447
2448 if (!offsetObj.isIntOrInt64() || !lenObj.isIntOrInt64()) {
2449 error(category: errSyntaxError, pos: 0, msg: "Illegal values in ByteRange array");
2450 if (doneCallback) {
2451 doneCallback();
2452 }
2453 return signature_info;
2454 }
2455
2456 Goffset offset = offsetObj.getIntOrInt64();
2457 Goffset len = lenObj.getIntOrInt64();
2458
2459 if (offset < 0 || offset >= fileLength || len < 0 || len > fileLength || offset + len > fileLength) {
2460 error(category: errSyntaxError, pos: 0, msg: "Illegal values in ByteRange array");
2461 if (doneCallback) {
2462 doneCallback();
2463 }
2464 return signature_info;
2465 }
2466
2467 doc->getBaseStream()->setPos(pos: offset);
2468 hashSignedDataBlock(handler: signature_handler.get(), block_len: len);
2469 }
2470
2471 if (!signature_info->isSubfilterSupported()) {
2472 error(category: errUnimplemented, pos: 0, msg: "Unable to validate this type of signature");
2473 if (doneCallback) {
2474 doneCallback();
2475 }
2476 return signature_info;
2477 }
2478 const SignatureValidationStatus sig_val_state = signature_handler->validateSignature();
2479 signature_info->setSignatureValStatus(sig_val_state);
2480 signature_info->setSignerName(signature_handler->getSignerName());
2481 signature_info->setSubjectDN(signature_handler->getSignerSubjectDN());
2482 signature_info->setHashAlgorithm(signature_handler->getHashAlgorithm());
2483
2484 // verify if signature contains a 'signing time' attribute
2485 if (signature_handler->getSigningTime() != std::chrono::system_clock::time_point {}) {
2486 signature_info->setSigningTime(std::chrono::system_clock::to_time_t(t: signature_handler->getSigningTime()));
2487 }
2488
2489 signature_info->setCertificateInfo(signature_handler->getCertificateInfo());
2490
2491 if (sig_val_state != SIGNATURE_VALID || !doVerifyCert) {
2492 if (doneCallback) {
2493 doneCallback();
2494 }
2495 return signature_info;
2496 }
2497
2498 signature_handler->validateCertificateAsync(validation_time: std::chrono::system_clock::from_time_t(t: validationTime), ocspRevocationCheck, useAIACertFetch: enableAIA, doneCallback);
2499
2500 return signature_info;
2501}
2502
2503CertificateValidationStatus FormFieldSignature::validateSignatureResult()
2504{
2505 if (!signature_handler) {
2506 return CERTIFICATE_GENERIC_ERROR;
2507 }
2508 return signature_handler->validateCertificateResult();
2509}
2510
2511std::vector<Goffset> FormFieldSignature::getSignedRangeBounds() const
2512{
2513 std::vector<Goffset> range_vec;
2514 if (byte_range.isArray()) {
2515 if (byte_range.arrayGetLength() == 4) {
2516 for (int i = 0; i < 2; ++i) {
2517 const Object offsetObj(byte_range.arrayGet(i: 2 * i));
2518 const Object lenObj(byte_range.arrayGet(i: 2 * i + 1));
2519 if (offsetObj.isIntOrInt64() && lenObj.isIntOrInt64()) {
2520 const Goffset offset = offsetObj.getIntOrInt64();
2521 const Goffset len = lenObj.getIntOrInt64();
2522 range_vec.push_back(x: offset);
2523 range_vec.push_back(x: offset + len);
2524 }
2525 }
2526 }
2527 }
2528 return range_vec;
2529}
2530
2531std::optional<GooString> FormFieldSignature::getCheckedSignature(Goffset *checkedFileSize)
2532{
2533 Goffset start = 0;
2534 Goffset end = 0;
2535 const std::vector<Goffset> ranges = getSignedRangeBounds();
2536 if (ranges.size() == 4) {
2537 start = ranges[1];
2538 end = ranges[2];
2539 }
2540 if (end >= start + 6) {
2541 BaseStream *stream = doc->getBaseStream();
2542 *checkedFileSize = stream->getLength();
2543 Goffset len = end - start;
2544 stream->setPos(pos: end - 1);
2545 int c2 = stream->lookChar();
2546 stream->setPos(pos: start);
2547 int c1 = stream->getChar();
2548 // PDF signatures are first ASN1 DER, then hex encoded PKCS#7 structures,
2549 // possibly padded with 0 characters and enclosed in '<' and '>'.
2550 // The ASN1 DER encoding of a PKCS#7 structure must start with the tag 0x30
2551 // for SEQUENCE. The next byte must be 0x80 for ASN1 DER indefinite length
2552 // encoding or (0x80 + n) for ASN1 DER definite length encoding
2553 // where n is the number of subsequent "length bytes" which big-endian
2554 // encode the length of the content of the SEQUENCE following them.
2555 if (len <= std::numeric_limits<int>::max() && *checkedFileSize > end && c1 == '<' && c2 == '>') {
2556 GooString gstr;
2557 ++start;
2558 --end;
2559 len = end - start;
2560 Goffset pos = 0;
2561 do {
2562 c1 = stream->getChar();
2563 if (c1 == EOF) {
2564 return {};
2565 }
2566 gstr.append(c: static_cast<char>(c1));
2567 } while (++pos < len);
2568 if (gstr.getChar(i: 0) == '3' && gstr.getChar(i: 1) == '0') {
2569 if (gstr.getChar(i: 2) == '8' && gstr.getChar(i: 3) == '0') {
2570 // ASN1 DER indefinite length encoding:
2571 // We only check that all characters up to the enclosing '>'
2572 // are hex characters and that there are two hex encoded 0 bytes
2573 // just before the enclosing '>' marking the end of the indefinite
2574 // length encoding.
2575 int paddingCount = 0;
2576 while (gstr.getChar(i: len - 1) == '0' && gstr.getChar(i: len - 2) == '0') {
2577 ++paddingCount;
2578 len -= 2;
2579 }
2580 if (paddingCount < 2 || len % 2 == 1) {
2581 len = 0;
2582 }
2583 } else if (gstr.getChar(i: 2) == '8') {
2584 // ASN1 DER definite length encoding:
2585 // We calculate the length of the following bytes from the length bytes and
2586 // check that after the length bytes and the following calculated number of
2587 // bytes all bytes up to the enclosing '>' character are hex encoded 0 bytes.
2588 int lenBytes = gstr.getChar(i: 3) - '0';
2589 if (lenBytes > 0 && lenBytes <= 4) {
2590 int sigLen = 0;
2591 for (int i = 0; i < 2 * lenBytes; ++i) {
2592 sigLen <<= 4;
2593 char c = gstr.getChar(i: i + 4);
2594 if (isdigit(c)) {
2595 sigLen += c - '0';
2596 } else if (isxdigit(c) && c >= 'a') {
2597 sigLen += c - 'a' + 10;
2598 } else if (isxdigit(c) && c >= 'A') {
2599 sigLen += c - 'A' + 10;
2600 } else {
2601 len = 0;
2602 break;
2603 }
2604 }
2605 if (sigLen > 0 && 2 * (sigLen + lenBytes) <= len - 4) {
2606 for (Goffset i = 2 * (sigLen + lenBytes) + 4; i < len; ++i) {
2607 if (gstr.getChar(i) != '0') {
2608 len = 0;
2609 break;
2610 }
2611 }
2612 } else {
2613 len = 0;
2614 }
2615 }
2616 }
2617 for (const char c : gstr.toStr()) {
2618 if (!isxdigit(c)) {
2619 len = 0;
2620 }
2621 }
2622 if (len > 0) {
2623 return GooString(&gstr, 0, len);
2624 }
2625 }
2626 }
2627 }
2628 return {};
2629}
2630
2631void FormFieldSignature::print(int indent)
2632{
2633 printf(format: "%*s- (%d %d): [signature] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren);
2634}
2635
2636//------------------------------------------------------------------------
2637// Form
2638//------------------------------------------------------------------------
2639
2640Form::Form(PDFDoc *docA) : doc(docA)
2641{
2642 Object obj1;
2643
2644 XRef *xref = doc->getXRef();
2645
2646 size = 0;
2647 numFields = 0;
2648 rootFields = nullptr;
2649 quadding = VariableTextQuadding::leftJustified;
2650 defaultAppearance = nullptr;
2651 defaultResources = nullptr;
2652
2653 Object *acroForm = doc->getCatalog()->getAcroForm();
2654
2655 needAppearances = acroForm->dictLookup(key: "NeedAppearances").getBoolWithDefaultValue(defaultValue: false);
2656
2657 obj1 = acroForm->dictLookup(key: "DA");
2658 if (obj1.isString()) {
2659 defaultAppearance = obj1.getString()->copy();
2660 }
2661
2662 obj1 = acroForm->dictLookup(key: "Q");
2663 if (obj1.isInt()) {
2664 const VariableTextQuadding aux = static_cast<VariableTextQuadding>(obj1.getInt());
2665 if (aux == VariableTextQuadding::leftJustified || aux == VariableTextQuadding::centered || aux == VariableTextQuadding::rightJustified) {
2666 quadding = static_cast<VariableTextQuadding>(aux);
2667 }
2668 }
2669
2670 resDict = acroForm->dictLookup(key: "DR");
2671 if (resDict.isDict()) {
2672 // At a minimum, this dictionary shall contain a Font entry
2673 obj1 = resDict.dictLookup(key: "Font");
2674 if (obj1.isDict()) {
2675 defaultResources = new GfxResources(xref, resDict.getDict(), nullptr);
2676 }
2677 }
2678 if (!defaultResources) {
2679 resDict.setToNull();
2680 }
2681
2682 obj1 = acroForm->dictLookup(key: "Fields");
2683 if (obj1.isArray()) {
2684 Array *array = obj1.getArray();
2685 std::set<Ref> alreadyReadRefs;
2686 for (int i = 0; i < array->getLength(); i++) {
2687 Object obj2 = array->get(i);
2688 const Object &oref = array->getNF(i);
2689 if (!oref.isRef()) {
2690 error(category: errSyntaxWarning, pos: -1, msg: "Direct object in rootFields");
2691 continue;
2692 }
2693
2694 if (!obj2.isDict()) {
2695 error(category: errSyntaxWarning, pos: -1, msg: "Reference in Fields array to an invalid or non existent object");
2696 continue;
2697 }
2698
2699 if (alreadyReadRefs.find(x: oref.getRef()) != alreadyReadRefs.end()) {
2700 continue;
2701 }
2702 alreadyReadRefs.insert(x: oref.getRef());
2703
2704 if (numFields >= size) {
2705 size += 16;
2706 rootFields = (FormField **)greallocn(p: rootFields, count: size, size: sizeof(FormField *));
2707 }
2708
2709 std::set<int> usedParents;
2710 rootFields[numFields++] = createFieldFromDict(obj: std::move(obj2), docA: doc, aref: oref.getRef(), parent: nullptr, usedParents: &usedParents);
2711 }
2712 } else {
2713 error(category: errSyntaxError, pos: -1, msg: "Can't get Fields array\n");
2714 }
2715
2716 obj1 = acroForm->dictLookup(key: "CO");
2717 if (obj1.isArray()) {
2718 Array *array = obj1.getArray();
2719 calculateOrder.reserve(n: array->getLength());
2720 for (int i = 0; i < array->getLength(); i++) {
2721 const Object &oref = array->getNF(i);
2722 if (!oref.isRef()) {
2723 error(category: errSyntaxWarning, pos: -1, msg: "Direct object in CO");
2724 continue;
2725 }
2726 calculateOrder.push_back(x: oref.getRef());
2727 }
2728 }
2729
2730 // for (int i = 0; i < numFields; i++)
2731 // rootFields[i]->printTree();
2732}
2733
2734Form::~Form()
2735{
2736 int i;
2737 for (i = 0; i < numFields; ++i) {
2738 delete rootFields[i];
2739 }
2740 gfree(p: rootFields);
2741 delete defaultAppearance;
2742 delete defaultResources;
2743}
2744
2745// Look up an inheritable field dictionary entry.
2746static Object fieldLookup(Dict *field, const char *key, std::set<int> *usedParents)
2747{
2748 Dict *dict = field;
2749 Object obj = dict->lookup(key);
2750 if (!obj.isNull()) {
2751 return obj;
2752 }
2753 const Object &parent = dict->lookupNF(key: "Parent");
2754 if (parent.isRef()) {
2755 const Ref ref = parent.getRef();
2756 if (usedParents->find(x: ref.num) == usedParents->end()) {
2757 usedParents->insert(x: ref.num);
2758
2759 Object obj2 = parent.fetch(xref: dict->getXRef());
2760 if (obj2.isDict()) {
2761 return fieldLookup(field: obj2.getDict(), key, usedParents);
2762 }
2763 }
2764 } else if (parent.isDict()) {
2765 return fieldLookup(field: parent.getDict(), key, usedParents);
2766 }
2767 return Object(objNull);
2768}
2769
2770Object Form::fieldLookup(Dict *field, const char *key)
2771{
2772 std::set<int> usedParents;
2773 return ::fieldLookup(field, key, usedParents: &usedParents);
2774}
2775
2776FormField *Form::createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref, FormField *parent, std::set<int> *usedParents)
2777{
2778 FormField *field;
2779
2780 const Object obj2 = Form::fieldLookup(field: obj.getDict(), key: "FT");
2781 if (obj2.isName(nameA: "Btn")) {
2782 field = new FormFieldButton(docA, std::move(obj), aref, parent, usedParents);
2783 } else if (obj2.isName(nameA: "Tx")) {
2784 field = new FormFieldText(docA, std::move(obj), aref, parent, usedParents);
2785 } else if (obj2.isName(nameA: "Ch")) {
2786 field = new FormFieldChoice(docA, std::move(obj), aref, parent, usedParents);
2787 } else if (obj2.isName(nameA: "Sig")) {
2788 field = new FormFieldSignature(docA, std::move(obj), aref, parent, usedParents);
2789 } else { // we don't have an FT entry => non-terminal field
2790 field = new FormField(docA, std::move(obj), aref, parent, usedParents);
2791 }
2792
2793 return field;
2794}
2795
2796static const std::string kOurDictFontNamePrefix = "popplerfont";
2797
2798std::string Form::findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle) const
2799{
2800 if (!resDict.isDict()) {
2801 return {};
2802 }
2803
2804 const std::string fontFamilyAndStyle = fontStyle.empty() ? fontFamily : fontFamily + " " + fontStyle;
2805
2806 Object fontDictObj = resDict.dictLookup(key: "Font");
2807 assert(fontDictObj.isDict());
2808
2809 const Dict *fontDict = fontDictObj.getDict();
2810 for (int i = 0; i < fontDict->getLength(); ++i) {
2811 const char *key = fontDict->getKey(i);
2812 if (std::string_view(key).starts_with(x: kOurDictFontNamePrefix)) {
2813 const Object fontObj = fontDict->getVal(i);
2814 if (fontObj.isDict() && fontObj.dictIs(dictType: "Font")) {
2815 const Object fontBaseFontObj = fontObj.dictLookup(key: "BaseFont");
2816 if (fontBaseFontObj.isName(nameA: fontFamilyAndStyle.c_str())) {
2817 return key;
2818 }
2819 }
2820 }
2821 }
2822
2823 return {};
2824}
2825
2826Form::AddFontResult Form::addFontToDefaultResources(const std::string &fontFamily, const std::string &fontStyle, bool forceName)
2827{
2828 FamilyStyleFontSearchResult findFontRes = globalParams->findSystemFontFileForFamilyAndStyle(fontFamily, fontStyle);
2829 std::vector<std::string> filesToIgnore;
2830 while (!findFontRes.filepath.empty()) {
2831 Form::AddFontResult addFontRes = addFontToDefaultResources(filepath: findFontRes.filepath, faceIndex: findFontRes.faceIndex, fontFamily, fontStyle, forceName);
2832 if (!addFontRes.fontName.empty()) {
2833 return addFontRes;
2834 }
2835 filesToIgnore.emplace_back(args&: findFontRes.filepath);
2836 findFontRes = globalParams->findSystemFontFileForFamilyAndStyle(fontFamily, fontStyle, filesToIgnore);
2837 }
2838 return {};
2839}
2840
2841Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, int faceIndex, const std::string &fontFamily, const std::string &fontStyle, bool forceName)
2842{
2843 if (!filepath.ends_with(x: ".ttf") && !filepath.ends_with(x: ".ttc") && !filepath.ends_with(x: ".otf")) {
2844 error(category: errIO, pos: -1, msg: "We only support embedding ttf/ttc/otf fonts for now. The font file for {0:s} {1:s} was {2:s}", fontFamily.c_str(), fontStyle.c_str(), filepath.c_str());
2845 return {};
2846 }
2847
2848 const FoFiIdentifierType fontFoFiType = FoFiIdentifier::identifyFile(fileName: filepath.c_str());
2849 if (fontFoFiType != fofiIdTrueType && fontFoFiType != fofiIdTrueTypeCollection && fontFoFiType != fofiIdOpenTypeCFF8Bit && fontFoFiType != fofiIdOpenTypeCFFCID) {
2850 error(category: errIO, pos: -1, msg: "We only support embedding ttf/ttc/otf fonts for now. The font file for {0:s} {1:s} was {2:s} of type {3:d}", fontFamily.c_str(), fontStyle.c_str(), filepath.c_str(), fontFoFiType);
2851 return {};
2852 }
2853
2854 const std::string fontFamilyAndStyle = fontStyle.empty() ? fontFamily : fontFamily + " " + fontStyle;
2855
2856 if (forceName && defaultResources && defaultResources->lookupFont(name: fontFamilyAndStyle.c_str())) {
2857 error(category: errInternal, pos: -1, msg: "Form::addFontToDefaultResources: Asked to forceName but font name exists {0:s}", fontFamilyAndStyle.c_str());
2858 return {};
2859 }
2860
2861 XRef *xref = doc->getXRef();
2862 Object fontDict(new Dict(xref));
2863 fontDict.dictSet(key: "Type", val: Object(objName, "Font"));
2864 fontDict.dictSet(key: "Subtype", val: Object(objName, "Type0"));
2865 fontDict.dictSet(key: "BaseFont", val: Object(objName, fontFamilyAndStyle.c_str()));
2866
2867 fontDict.dictSet(key: "Encoding", val: Object(objName, "Identity-H"));
2868
2869 {
2870 std::unique_ptr<Array> descendantFonts = std::make_unique<Array>(args&: xref);
2871
2872 const bool isTrueType = (fontFoFiType == fofiIdTrueType || fontFoFiType == fofiIdTrueTypeCollection);
2873 std::unique_ptr<Dict> descendantFont = std::make_unique<Dict>(args&: xref);
2874 descendantFont->set(key: "Type", val: Object(objName, "Font"));
2875 descendantFont->set(key: "Subtype", val: Object(objName, isTrueType ? "CIDFontType2" : "CIDFontType0"));
2876 descendantFont->set(key: "BaseFont", val: Object(objName, fontFamilyAndStyle.c_str()));
2877
2878 {
2879 // We only support fonts with identity cmaps for now
2880 Dict *cidSystemInfo = new Dict(xref);
2881 cidSystemInfo->set(key: "Registry", val: Object(new GooString("Adobe")));
2882 cidSystemInfo->set(key: "Ordering", val: Object(new GooString("Identity")));
2883 cidSystemInfo->set(key: "Supplement", val: Object(0));
2884 descendantFont->set(key: "CIDSystemInfo", val: Object(cidSystemInfo));
2885 }
2886
2887 FT_Library freetypeLib;
2888 if (FT_Init_FreeType(alibrary: &freetypeLib)) {
2889 error(category: errIO, pos: -1, msg: "FT_Init_FreeType failed");
2890 return {};
2891 }
2892 const std::unique_ptr<FT_Library, void (*)(FT_Library *)> freetypeLibDeleter(&freetypeLib, [](FT_Library *l) { FT_Done_FreeType(library: *l); });
2893
2894 FT_Face face;
2895 if (ft_new_face_from_file(library: freetypeLib, filename_utf8: filepath.c_str(), face_index: faceIndex, aface: &face)) {
2896 error(category: errIO, pos: -1, msg: "ft_new_face_from_file failed for {0:s}", filepath.c_str());
2897 return {};
2898 }
2899 const std::unique_ptr<FT_Face, void (*)(FT_Face *)> faceDeleter(&face, [](FT_Face *f) { FT_Done_Face(face: *f); });
2900
2901 if (FT_Set_Char_Size(face, char_width: 1000, char_height: 1000, horz_resolution: 0, vert_resolution: 0)) {
2902 error(category: errIO, pos: -1, msg: "FT_Set_Char_Size failed for {0:s}", filepath.c_str());
2903 return {};
2904 }
2905
2906 {
2907 std::unique_ptr<Dict> fontDescriptor = std::make_unique<Dict>(args&: xref);
2908 fontDescriptor->set(key: "Type", val: Object(objName, "FontDescriptor"));
2909 fontDescriptor->set(key: "FontName", val: Object(objName, fontFamilyAndStyle.c_str()));
2910
2911 // a bit arbirary but the Flags field is mandatory...
2912 const std::string lowerCaseFontFamily = GooString::toLowerCase(s: fontFamily);
2913 if (lowerCaseFontFamily.find(s: "serif") != std::string::npos && lowerCaseFontFamily.find(s: "sans") == std::string::npos) {
2914 fontDescriptor->set(key: "Flags", val: Object(2)); // Serif
2915 } else {
2916 fontDescriptor->set(key: "Flags", val: Object(0)); // Sans Serif
2917 }
2918
2919 Array *fontBBox = new Array(xref);
2920 fontBBox->add(elem: Object(static_cast<int>(face->bbox.xMin)));
2921 fontBBox->add(elem: Object(static_cast<int>(face->bbox.yMin)));
2922 fontBBox->add(elem: Object(static_cast<int>(face->bbox.xMax)));
2923 fontBBox->add(elem: Object(static_cast<int>(face->bbox.yMax)));
2924 fontDescriptor->set(key: "FontBBox", val: Object(fontBBox));
2925
2926 fontDescriptor->set(key: "Ascent", val: Object(static_cast<int>(face->ascender)));
2927
2928 fontDescriptor->set(key: "Descent", val: Object(static_cast<int>(face->descender)));
2929
2930 {
2931 const std::unique_ptr<GooFile> file(GooFile::open(fileName: filepath));
2932 if (!file) {
2933 error(category: errIO, pos: -1, msg: "Failed to open {0:s}", filepath.c_str());
2934 return {};
2935 }
2936 const Goffset fileSize = file->size();
2937 if (fileSize < 0) {
2938 error(category: errIO, pos: -1, msg: "Failed to get file size for {0:s}", filepath.c_str());
2939 return {};
2940 }
2941 // GooFile::read only takes an integer so for now we don't support huge fonts
2942 if (fileSize > std::numeric_limits<int>::max()) {
2943 error(category: errIO, pos: -1, msg: "Font size is too big {0:s}", filepath.c_str());
2944 return {};
2945 }
2946 char *dataPtr = static_cast<char *>(gmalloc(size: fileSize));
2947 const Goffset bytesRead = file->read(buf: dataPtr, n: static_cast<int>(fileSize), offset: 0);
2948 if (bytesRead != fileSize) {
2949 error(category: errIO, pos: -1, msg: "Failed to read contents of {0:s}", filepath.c_str());
2950 gfree(p: dataPtr);
2951 return {};
2952 }
2953
2954 if (isTrueType) {
2955 const Ref fontFile2Ref = xref->addStreamObject(dict: new Dict(xref), buffer: dataPtr, bufferSize: fileSize, compression: StreamCompression::Compress);
2956 fontDescriptor->set(key: "FontFile2", val: Object(fontFile2Ref));
2957 } else {
2958 Dict *fontFileStreamDict = new Dict(xref);
2959 fontFileStreamDict->set(key: "Subtype", val: Object(objName, "OpenType"));
2960 const Ref fontFile3Ref = xref->addStreamObject(dict: fontFileStreamDict, buffer: dataPtr, bufferSize: fileSize, compression: StreamCompression::Compress);
2961 fontDescriptor->set(key: "FontFile3", val: Object(fontFile3Ref));
2962 }
2963 }
2964
2965 const Ref fontDescriptorRef = xref->addIndirectObject(o: Object(fontDescriptor.release()));
2966 descendantFont->set(key: "FontDescriptor", val: Object(fontDescriptorRef));
2967 }
2968
2969 static const int basicMultilingualMaxCode = 65535;
2970
2971 const std::unique_ptr<FoFiTrueType> fft = FoFiTrueType::load(fileName: filepath.c_str());
2972 if (fft) {
2973
2974 // Look for the Unicode BMP cmaps, which are 0/3 or 3/1
2975 int unicodeBMPCMap = fft->findCmap(platform: 0, encoding: 3);
2976 if (unicodeBMPCMap < 0) {
2977 unicodeBMPCMap = fft->findCmap(platform: 3, encoding: 1);
2978 }
2979 if (unicodeBMPCMap < 0) {
2980 error(category: errIO, pos: -1, msg: "Font does not have an unicode BMP cmap {0:s}", filepath.c_str());
2981 return {};
2982 }
2983
2984 CIDFontsWidthsBuilder fontsWidths;
2985
2986 for (int code = 0; code <= basicMultilingualMaxCode; ++code) {
2987 const int glyph = fft->mapCodeToGID(i: unicodeBMPCMap, c: code);
2988 if (FT_Load_Glyph(face, glyph_index: glyph, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING)) {
2989 fontsWidths.addWidth(index: code, width: 0);
2990 } else {
2991 fontsWidths.addWidth(index: code, width: static_cast<int>(face->glyph->metrics.horiAdvance));
2992 }
2993 }
2994 Array *widths = new Array(xref);
2995 for (const auto &segment : fontsWidths.takeSegments()) {
2996 std::visit(
2997 visitor: [&widths, &xref](auto &&s) {
2998 using T = std::decay_t<decltype(s)>;
2999 if constexpr (std::is_same_v<T, CIDFontsWidthsBuilder::ListSegment>) {
3000 widths->add(elem: Object(s.first));
3001 auto widthsInner = std::make_unique<Array>(args&: xref);
3002 for (const auto &w : s.widths) {
3003 widthsInner->add(elem: Object(w));
3004 }
3005 widths->add(elem: Object(widthsInner.release()));
3006 } else if constexpr (std::is_same_v<T, CIDFontsWidthsBuilder::RangeSegment>) {
3007 widths->add(elem: Object(s.first));
3008 widths->add(elem: Object(s.last));
3009 widths->add(elem: Object(s.width));
3010 } else {
3011 static_assert(always_false_v<T>, "non-exhaustive visitor");
3012 }
3013 },
3014 variants: segment);
3015 }
3016 descendantFont->set(key: "W", val: Object(widths));
3017
3018 char *dataPtr = static_cast<char *>(gmalloc(size: 2 * (basicMultilingualMaxCode + 1)));
3019 int i = 0;
3020
3021 for (int code = 0; code <= basicMultilingualMaxCode; ++code) {
3022 const int glyph = fft->mapCodeToGID(i: unicodeBMPCMap, c: code);
3023 dataPtr[i++] = (unsigned char)(glyph >> 8);
3024 dataPtr[i++] = (unsigned char)(glyph & 0xff);
3025 }
3026 const Ref cidToGidMapStream = xref->addStreamObject(dict: new Dict(xref), buffer: dataPtr, bufferSize: basicMultilingualMaxCode * 2, compression: StreamCompression::Compress);
3027 descendantFont->set(key: "CIDToGIDMap", val: Object(cidToGidMapStream));
3028 }
3029
3030 descendantFonts->add(elem: Object(descendantFont.release()));
3031
3032 fontDict.dictSet(key: "DescendantFonts", val: Object(descendantFonts.release()));
3033 }
3034
3035 const Ref fontDictRef = xref->addIndirectObject(o: fontDict);
3036
3037 std::string dictFontName = forceName ? fontFamilyAndStyle : kOurDictFontNamePrefix;
3038 Object *acroForm = doc->getCatalog()->getAcroForm();
3039 if (resDict.isDict()) {
3040 Ref fontDictObjRef;
3041 Object fontDictObj = resDict.getDict()->lookup(key: "Font", returnRef: &fontDictObjRef);
3042 assert(fontDictObj.isDict());
3043 dictFontName = fontDictObj.getDict()->findAvailableKey(suggestedKey: dictFontName);
3044 fontDictObj.dictSet(key: dictFontName.c_str(), val: Object(fontDictRef));
3045
3046 if (fontDictObjRef != Ref::INVALID()) {
3047 xref->setModifiedObject(o: &fontDictObj, r: fontDictObjRef);
3048 } else {
3049 Ref resDictRef;
3050 acroForm->getDict()->lookup(key: "DR", returnRef: &resDictRef);
3051 if (resDictRef != Ref::INVALID()) {
3052 xref->setModifiedObject(o: &resDict, r: resDictRef);
3053 } else {
3054 doc->getCatalog()->setAcroFormModified();
3055 }
3056 }
3057
3058 // maybe we can do something to reuse the existing data instead of recreating from scratch?
3059 delete defaultResources;
3060 defaultResources = new GfxResources(xref, resDict.getDict(), nullptr);
3061 } else {
3062 Dict *fontsDict = new Dict(xref);
3063 fontsDict->set(key: dictFontName.c_str(), val: Object(fontDictRef));
3064
3065 Dict *defaultResourcesDict = new Dict(xref);
3066 defaultResourcesDict->set(key: "Font", val: Object(fontsDict));
3067
3068 assert(!defaultResources);
3069 defaultResources = new GfxResources(xref, defaultResourcesDict, nullptr);
3070 resDict = Object(defaultResourcesDict);
3071
3072 acroForm->dictSet(key: "DR", val: resDict.copy());
3073 doc->getCatalog()->setAcroFormModified();
3074 }
3075
3076 return { .fontName: dictFontName, .ref: fontDictRef };
3077}
3078
3079std::string Form::getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate) const
3080{
3081 const UCharFontSearchResult res = globalParams->findSystemFontFileForUChar(uChar, fontToEmulate);
3082
3083 return findFontInDefaultResources(fontFamily: res.family, fontStyle: res.style);
3084}
3085
3086std::vector<Form::AddFontResult> Form::ensureFontsForAllCharacters(const GooString *unicodeText, const std::string &pdfFontNameToEmulate, GfxResources *fieldResources)
3087{
3088 GfxResources *resources = fieldResources ? fieldResources : defaultResources;
3089 std::shared_ptr<GfxFont> f;
3090 if (!resources) {
3091 // There's no resources, so create one with the needed font name
3092 addFontToDefaultResources(fontFamily: pdfFontNameToEmulate, fontStyle: "", /*forceName*/ true);
3093 resources = defaultResources;
3094 }
3095 f = resources->lookupFont(name: pdfFontNameToEmulate.c_str());
3096 const CharCodeToUnicode *ccToUnicode = f ? f->getToUnicode() : nullptr;
3097 if (!ccToUnicode) {
3098 error(category: errInternal, pos: -1, msg: "Form::ensureFontsForAllCharacters: No ccToUnicode, this should not happen\n");
3099 return {}; // will never happen with current code
3100 }
3101
3102 std::vector<AddFontResult> newFonts;
3103
3104 // If the text has some characters that are not available in the font, try adding a font for those
3105 std::unordered_set<Unicode> seen;
3106 for (int i = 2; i < unicodeText->getLength(); i += 2) {
3107 Unicode uChar = (unsigned char)(unicodeText->getChar(i)) << 8;
3108 uChar += (unsigned char)(unicodeText->getChar(i: i + 1));
3109
3110 if (uChar < 128 && !std::isprint(static_cast<unsigned char>(uChar))) {
3111 continue;
3112 }
3113 if (seen.find(x: uChar) != seen.end()) {
3114 continue;
3115 }
3116 seen.insert(x: uChar);
3117
3118 CharCode c;
3119 bool addFont = false;
3120 if (ccToUnicode->mapToCharCode(u: &uChar, c: &c, usize: 1)) {
3121 if (f->isCIDFont()) {
3122 auto cidFont = static_cast<const GfxCIDFont *>(f.get());
3123 if (c < cidFont->getCIDToGIDLen() && c != 0 && c != '\r' && c != '\n') {
3124 const int glyph = cidFont->getCIDToGID()[c];
3125 if (glyph == 0) {
3126 addFont = true;
3127 }
3128 }
3129 }
3130 } else {
3131 addFont = true;
3132 }
3133
3134 if (addFont) {
3135 Form::AddFontResult res = doGetAddFontToDefaultResources(uChar, fontToEmulate: *f);
3136 if (res.ref != Ref::INVALID()) {
3137 newFonts.emplace_back(args&: res);
3138 }
3139 }
3140 }
3141
3142 return newFonts;
3143}
3144
3145Form::AddFontResult Form::doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate)
3146{
3147 const UCharFontSearchResult res = globalParams->findSystemFontFileForUChar(uChar, fontToEmulate);
3148
3149 std::string pdfFontName = findFontInDefaultResources(fontFamily: res.family, fontStyle: res.style);
3150 if (pdfFontName.empty()) {
3151 return addFontToDefaultResources(filepath: res.filepath, faceIndex: res.faceIndex, fontFamily: res.family, fontStyle: res.style);
3152 }
3153 return { .fontName: pdfFontName, .ref: Ref::INVALID() };
3154}
3155
3156void Form::postWidgetsLoad()
3157{
3158 // We create the widget annotations associated to
3159 // every form widget here, because the AnnotWidget constructor
3160 // needs the form object that gets from the catalog. When constructing
3161 // a FormWidget the Catalog is still creating the form object
3162 for (int i = 0; i < numFields; i++) {
3163 rootFields[i]->fillChildrenSiblingsID();
3164 rootFields[i]->createWidgetAnnotations();
3165 }
3166}
3167
3168FormWidget *Form::findWidgetByRef(Ref aref)
3169{
3170 for (int i = 0; i < numFields; i++) {
3171 FormWidget *result = rootFields[i]->findWidgetByRef(aref);
3172 if (result) {
3173 return result;
3174 }
3175 }
3176 return nullptr;
3177}
3178
3179FormField *Form::findFieldByRef(Ref aref) const
3180{
3181 for (int i = 0; i < numFields; i++) {
3182 FormField *result = rootFields[i]->findFieldByRef(aref);
3183 if (result) {
3184 return result;
3185 }
3186 }
3187 return nullptr;
3188}
3189
3190FormField *Form::findFieldByFullyQualifiedName(const std::string &name) const
3191{
3192 for (int i = 0; i < numFields; i++) {
3193 FormField *result = rootFields[i]->findFieldByFullyQualifiedName(name);
3194 if (result) {
3195 return result;
3196 }
3197 }
3198 return nullptr;
3199}
3200
3201void Form::reset(const std::vector<std::string> &fields, bool excludeFields)
3202{
3203 FormField *foundField;
3204 const bool resetAllFields = fields.empty();
3205
3206 if (resetAllFields) {
3207 for (int i = 0; i < numFields; i++) {
3208 rootFields[i]->reset(excludedFields: std::vector<std::string>());
3209 }
3210 } else {
3211 if (!excludeFields) {
3212 for (const std::string &field : fields) {
3213 Ref fieldRef;
3214
3215 if (field.size() > 1 && field.compare(pos: field.size() - 2, n1: 2, s: " R") == 0 && sscanf(s: field.c_str(), format: "%d %d R", &fieldRef.num, &fieldRef.gen) == 2) {
3216 foundField = findFieldByRef(aref: fieldRef);
3217 } else {
3218 foundField = findFieldByFullyQualifiedName(name: field);
3219 }
3220
3221 if (foundField) {
3222 foundField->reset(excludedFields: std::vector<std::string>());
3223 }
3224 }
3225 } else {
3226 for (int i = 0; i < numFields; i++) {
3227 rootFields[i]->reset(excludedFields: fields);
3228 }
3229 }
3230 }
3231}
3232
3233std::string Form::findPdfFontNameToUseForSigning()
3234{
3235 static constexpr std::array<const char *, 2> fontsToUseToSign = { "Helvetica", "Arial" };
3236 for (const char *fontToUseToSign : fontsToUseToSign) {
3237 std::string pdfFontName = findFontInDefaultResources(fontFamily: fontToUseToSign, fontStyle: "");
3238 if (!pdfFontName.empty()) {
3239 return pdfFontName;
3240 }
3241
3242 pdfFontName = addFontToDefaultResources(fontFamily: fontToUseToSign, fontStyle: "").fontName;
3243 if (!pdfFontName.empty()) {
3244 return pdfFontName;
3245 }
3246 }
3247
3248 error(category: errInternal, pos: -1, msg: "Form::findPdfFontNameToUseForSigning: No suitable font found'\n");
3249
3250 return {};
3251}
3252
3253//------------------------------------------------------------------------
3254// FormPageWidgets
3255//------------------------------------------------------------------------
3256
3257FormPageWidgets::FormPageWidgets(Annots *annots, unsigned int page, Form *form)
3258{
3259 numWidgets = 0;
3260 widgets = nullptr;
3261 size = 0;
3262
3263 if (annots && !annots->getAnnots().empty() && form) {
3264 size = annots->getAnnots().size();
3265 widgets = (FormWidget **)greallocn(p: widgets, count: size, size: sizeof(FormWidget *));
3266
3267 /* For each entry in the page 'Annots' dict, try to find
3268 a matching form field */
3269 for (Annot *annot : annots->getAnnots()) {
3270
3271 if (annot->getType() != Annot::typeWidget) {
3272 continue;
3273 }
3274
3275 if (!annot->getHasRef()) {
3276 /* Since all entry in a form field's kid dict needs to be
3277 indirect references, if this annot isn't indirect, it isn't
3278 related to a form field */
3279 continue;
3280 }
3281
3282 Ref r = annot->getRef();
3283
3284 /* Try to find a form field which either has this Annot in its Kids entry
3285 or is merged with this Annot */
3286 FormWidget *tmp = form->findWidgetByRef(aref: r);
3287 if (tmp) {
3288 // We've found a corresponding form field, link it
3289 tmp->setID(FormWidget::encodeID(pageNum: page, fieldNum: numWidgets));
3290 widgets[numWidgets++] = tmp;
3291 }
3292 }
3293 }
3294}
3295
3296void FormPageWidgets::addWidgets(const std::vector<FormField *> &addedWidgets, unsigned int page)
3297{
3298 if (addedWidgets.empty()) {
3299 return;
3300 }
3301
3302 size += addedWidgets.size();
3303 widgets = (FormWidget **)greallocn(p: widgets, count: size, size: sizeof(FormWidget *));
3304
3305 for (auto frmField : addedWidgets) {
3306 FormWidget *frmWidget = frmField->getWidget(i: 0);
3307 frmWidget->setID(FormWidget::encodeID(pageNum: page, fieldNum: numWidgets));
3308 widgets[numWidgets++] = frmWidget;
3309 }
3310}
3311
3312FormPageWidgets::~FormPageWidgets()
3313{
3314 gfree(p: widgets);
3315}
3316

source code of poppler/poppler/Form.cc