//======================================================================== // // Form.h // // This file is licensed under the GPLv2 or later // // Copyright 2006 Julien Rebetez // Copyright 2007, 2008, 2011 Carlos Garcia Campos // Copyright 2007-2010, 2012, 2015-2023 Albert Astals Cid // Copyright 2010 Mark Riedesel // Copyright 2011 Pino Toscano // Copyright 2012 Fabio D'Urso // Copyright 2013 Adrian Johnson // Copyright 2015 André Guerreiro // Copyright 2015 André Esser // Copyright 2017 Roland Hieber // Copyright 2017 Hans-Ulrich Jüttner // Copyright 2018 Andre Heinecke // Copyright 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2019, 2020 Oliver Sander // Copyright 2019 João Netto // Copyright 2020, 2021 Nelson Benítez León // Copyright 2020 Marek Kasik // Copyright 2020 Thorsten Behrens // Copyright 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden // Copyright 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright 2021 Theofilos Intzoglou // Copyright 2022 Alexander Sulfrian // Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== #ifndef FORM_H #define FORM_H #include "Annot.h" #include "CharTypes.h" #include "Object.h" #include "poppler_private_export.h" #include "SignatureInfo.h" #include #include #include #include #include class GooString; class Array; class Dict; class Annot; class AnnotWidget; class Annots; class LinkAction; class GfxResources; class PDFDoc; class X509CertificateInfo; namespace CryptoSign { class VerificationInterface; } enum FormFieldType { formButton, formText, formChoice, formSignature, formUndef }; enum FormButtonType { formButtonCheck, formButtonPush, formButtonRadio }; enum FormSignatureType { adbe_pkcs7_sha1, adbe_pkcs7_detached, ETSI_CAdES_detached, unknown_signature_type, unsigned_signature_field }; enum FillValueType { fillValue, fillDefaultValue }; class Form; class FormField; class FormFieldButton; class FormFieldText; class FormFieldSignature; class FormFieldChoice; //------------------------------------------------------------------------ // FormWidget // A FormWidget represents the graphical part of a field and is "attached" // to a page. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidget { public: virtual ~FormWidget(); // Check if point is inside the field bounding rect bool inRect(double x, double y) const; // Get the field bounding rect void getRect(double *x1, double *y1, double *x2, double *y2) const; unsigned getID() { return ID; } void setID(unsigned int i) { ID = i; } FormField *getField() { return field; } FormFieldType getType() { return type; } Object *getObj() { return &obj; } Ref getRef() { return ref; } void setChildNum(unsigned i) { childNum = i; } unsigned getChildNum() { return childNum; } const GooString *getPartialName() const; void setPartialName(const GooString &name); const GooString *getAlternateUiName() const; const GooString *getMappingName() const; GooString *getFullyQualifiedName(); bool isModified() const; bool isReadOnly() const; void setReadOnly(bool value); LinkAction *getActivationAction(); // The caller should not delete the result std::unique_ptr getAdditionalAction(Annot::FormAdditionalActionsType type); bool setAdditionalAction(Annot::FormAdditionalActionsType t, const std::string &js); // return the unique ID corresponding to pageNum/fieldNum static int encodeID(unsigned pageNum, unsigned fieldNum); // decode id and retrieve pageNum and fieldNum static void decodeID(unsigned id, unsigned *pageNum, unsigned *fieldNum); void createWidgetAnnotation(); AnnotWidget *getWidgetAnnotation() const { return widget; } void setWidgetAnnotation(AnnotWidget *_widget) { widget = _widget; } virtual void updateWidgetAppearance() = 0; void print(int indent = 0); protected: FormWidget(PDFDoc *docA, Object *aobj, unsigned num, Ref aref, FormField *fieldA); AnnotWidget *widget; FormField *field; FormFieldType type; Object obj; Ref ref; PDFDoc *doc; XRef *xref; // index of this field in the parent's child list unsigned childNum; /* Field ID is an (unsigned) integer, calculated as follow : the first sizeof/2 bits are the field number, relative to the page the last sizeof/2 bits are the page number [page number | field number] (encoding) id = (pageNum << 4*sizeof(unsigned)) + fieldNum; (decoding) pageNum = id >> 4*sizeof(unsigned); fieldNum = (id << 4*sizeof(unsigned)) >> 4*sizeof(unsigned); */ unsigned ID; }; //------------------------------------------------------------------------ // FormWidgetButton //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidgetButton : public FormWidget { public: FormWidgetButton(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); ~FormWidgetButton() override; FormButtonType getButtonType() const; void setState(bool state); bool getState() const; const char *getOnStr() const; void setAppearanceState(const char *state); void updateWidgetAppearance() override; protected: FormFieldButton *parent() const; GooString *onStr; }; //------------------------------------------------------------------------ // FormWidgetText //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidgetText : public FormWidget { public: FormWidgetText(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); // return the field's content (UTF16BE) const GooString *getContent() const; // expects a UTF16BE string void setContent(const GooString *new_content); // sets the text inside the field appearance stream void setAppearanceContent(const GooString *new_content); void updateWidgetAppearance() override; bool isMultiline() const; bool isPassword() const; bool isFileSelect() const; bool noSpellCheck() const; bool noScroll() const; bool isComb() const; bool isRichText() const; int getMaxLen() const; // return the font size of the field's text double getTextFontSize(); // set the font size of the field's text (currently only integer values) void setTextFontSize(int fontSize); protected: FormFieldText *parent() const; }; //------------------------------------------------------------------------ // FormWidgetChoice //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidgetChoice : public FormWidget { public: FormWidgetChoice(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); ~FormWidgetChoice() override; int getNumChoices() const; // return the display name of the i-th choice (UTF16BE) const GooString *getChoice(int i) const; const GooString *getExportVal(int i) const; // select the i-th choice void select(int i); // toggle selection of the i-th choice void toggle(int i); // deselect everything void deselectAll(); // except a UTF16BE string // only work for editable combo box, set the user-entered text as the current choice void setEditChoice(const GooString *new_content); const GooString *getEditChoice() const; void updateWidgetAppearance() override; bool isSelected(int i) const; bool isCombo() const; bool hasEdit() const; bool isMultiSelect() const; bool noSpellCheck() const; bool commitOnSelChange() const; bool isListBox() const; protected: bool _checkRange(int i) const; FormFieldChoice *parent() const; }; //------------------------------------------------------------------------ // FormWidgetSignature //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormWidgetSignature : public FormWidget { public: FormWidgetSignature(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); void updateWidgetAppearance() override; FormSignatureType signatureType() const; void setSignatureType(FormSignatureType fst); // Use -1 for now as validationTime // ocspRevocation and aiafetch might happen async in the Background // doneCallback will be invoked once there is a result // Note: Validation callback will likely happen from an auxillary // thread and it is the caller of this method who is responsible // for moving back to the main thread // For synchronous code, don't provide validation callback // and just call validateSignatureResult afterwards // The returned SignatureInfo from this method does // not have validated the certificate. SignatureInfo *validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function &doneCallback); /// Waits, if needed, on validation callback and /// returns a signatureinfo with validated certificates CertificateValidationStatus validateSignatureResult(); // returns a list with the boundaries of the signed ranges // the elements of the list are of type Goffset std::vector getSignedRangeBounds() const; // Creates or replaces the dictionary name "V" in the signature dictionary and // fills it with the fields of the signature; the field "Contents" is the signature // in PKCS#7 format, which is calculated over the byte range encompassing the whole // document except for the signature itself; this byte range is specified in the // field "ByteRange" in the dictionary "V". // Arguments reason and location are UTF-16 big endian strings with BOM. An empty string and nullptr are acceptable too. // Returns success. bool signDocument(const std::string &filename, const std::string &certNickname, const std::string &password, const GooString *reason = nullptr, const GooString *location = nullptr, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}); // Same as above but adds text, font color, etc. bool signDocumentWithAppearance(const std::string &filename, const std::string &certNickname, const std::string &password, const GooString *reason = nullptr, const GooString *location = nullptr, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, const GooString &signatureText = {}, const GooString &signatureTextLeft = {}, double fontSize = {}, double leftFontSize = {}, std::unique_ptr &&fontColor = {}, double borderWidth = {}, std::unique_ptr &&borderColor = {}, std::unique_ptr &&backgroundColor = {}); // checks the length encoding of the signature and returns the hex encoded signature // if the check passed (and the checked file size as output parameter in checkedFileSize) // otherwise a nullptr is returned std::optional getCheckedSignature(Goffset *checkedFileSize); const GooString *getSignature() const; private: bool createSignature(Object &vObj, Ref vRef, const GooString &name, int placeholderLength, const GooString *reason = nullptr, const GooString *location = nullptr); bool getObjectStartEnd(const GooString &filename, int objNum, Goffset *objStart, Goffset *objEnd, const std::optional &ownerPassword, const std::optional &userPassword); bool updateOffsets(FILE *f, Goffset objStart, Goffset objEnd, Goffset *sigStart, Goffset *sigEnd, Goffset *fileSize); bool updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString &signature); }; //------------------------------------------------------------------------ // FormField // A FormField implements the logical side of a field and is "attached" to // the Catalog. This is an internal class and client applications should // only interact with FormWidgets. //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormField { public: FormField(PDFDoc *docA, Object &&aobj, const Ref aref, FormField *parent, std::set *usedParents, FormFieldType t = formUndef); virtual ~FormField(); // Accessors. FormFieldType getType() const { return type; } Object *getObj() { return &obj; } Ref getRef() { return ref; } void setReadOnly(bool value); bool isReadOnly() const { return readOnly; } void setStandAlone(bool value) { standAlone = value; } bool isStandAlone() const { return standAlone; } GooString *getDefaultAppearance() const { return defaultAppearance; } void setDefaultAppearance(const std::string &appearance); bool hasTextQuadding() const { return hasQuadding; } VariableTextQuadding getTextQuadding() const { return quadding; } const GooString *getPartialName() const { return partialName; } void setPartialName(const GooString &name); const GooString *getAlternateUiName() const { return alternateUiName; } const GooString *getMappingName() const { return mappingName; } GooString *getFullyQualifiedName(); FormWidget *findWidgetByRef(Ref aref); int getNumWidgets() const { return terminal ? numChildren : 0; } FormWidget *getWidget(int i) const { return terminal ? widgets[i] : nullptr; } int getNumChildren() const { return !terminal ? numChildren : 0; } FormField *getChildren(int i) const { return children[i]; } // only implemented in FormFieldButton virtual void fillChildrenSiblingsID(); void createWidgetAnnotations(); void printTree(int indent = 0); virtual void print(int indent = 0); virtual void reset(const std::vector &excludedFields); void resetChildren(const std::vector &excludedFields); FormField *findFieldByRef(Ref aref); FormField *findFieldByFullyQualifiedName(const std::string &name); protected: void _createWidget(Object *obj, Ref aref); void createChildren(std::set *usedParents); void updateChildrenAppearance(); bool isAmongExcludedFields(const std::vector &excludedFields); FormFieldType type; // field type Ref ref; bool terminal; Object obj; PDFDoc *doc; XRef *xref; FormField **children; FormField *parent; int numChildren; FormWidget **widgets; bool readOnly; GooString *partialName; // T field GooString *alternateUiName; // TU field GooString *mappingName; // TM field GooString *fullyQualifiedName; // Variable Text GooString *defaultAppearance; bool hasQuadding; VariableTextQuadding quadding; // True when FormField is not part of Catalog's Field array (or there isn't one). bool standAlone; private: FormField() { } }; //------------------------------------------------------------------------ // FormFieldButton //------------------------------------------------------------------------ class FormFieldButton : public FormField { public: FormFieldButton(PDFDoc *docA, Object &&dict, const Ref ref, FormField *parent, std::set *usedParents); FormButtonType getButtonType() const { return btype; } bool noToggleToOff() const { return noAllOff; } // returns true if the state modification is accepted bool setState(const char *state, bool ignoreToggleOff = false); bool getState(const char *state) const; const char *getAppearanceState() const { return appearanceState.isName() ? appearanceState.getName() : nullptr; } const char *getDefaultAppearanceState() const { return defaultAppearanceState.isName() ? defaultAppearanceState.getName() : nullptr; } void fillChildrenSiblingsID() override; void setNumSiblings(int num); void setSibling(int i, FormFieldButton *id) { siblings[i] = id; } // For radio buttons, return the fields of the other radio buttons in the same group FormFieldButton *getSibling(int i) const { return siblings[i]; } int getNumSiblings() const { return numSiblings; } void print(int indent) override; void reset(const std::vector &excludedFields) override; ~FormFieldButton() override; protected: void updateState(const char *state); FormFieldButton **siblings; // IDs of dependent buttons (each button of a radio field has all the others buttons // of the same field in this array) int numSiblings; FormButtonType btype; int size; int active_child; // only used for combo box bool noAllOff; Object appearanceState; // V Object defaultAppearanceState; // DV }; //------------------------------------------------------------------------ // FormFieldText //------------------------------------------------------------------------ class FormFieldText : public FormField { public: FormFieldText(PDFDoc *docA, Object &&dictObj, const Ref ref, FormField *parent, std::set *usedParents); const GooString *getContent() const { return content; } const GooString *getAppearanceContent() const { return internalContent ? internalContent : content; } void setContentCopy(const GooString *new_content); void setAppearanceContentCopy(const GooString *new_content); ~FormFieldText() override; bool isMultiline() const { return multiline; } bool isPassword() const { return password; } bool isFileSelect() const { return fileSelect; } bool noSpellCheck() const { return doNotSpellCheck; } bool noScroll() const { return doNotScroll; } bool isComb() const { return comb; } bool isRichText() const { return richText; } int getMaxLen() const { return maxLen; } // return the font size of the field's text double getTextFontSize(); // set the font size of the field's text (currently only integer values) void setTextFontSize(int fontSize); void print(int indent) override; void reset(const std::vector &excludedFields) override; static int tokenizeDA(const std::string &daString, std::vector *daToks, const char *searchTok); protected: int parseDA(std::vector *daToks); void fillContent(FillValueType fillType); GooString *content; GooString *internalContent; GooString *defaultContent; bool multiline; bool password; bool fileSelect; bool doNotSpellCheck; bool doNotScroll; bool comb; bool richText; int maxLen; }; //------------------------------------------------------------------------ // FormFieldChoice //------------------------------------------------------------------------ class FormFieldChoice : public FormField { public: FormFieldChoice(PDFDoc *docA, Object &&aobj, const Ref ref, FormField *parent, std::set *usedParents); ~FormFieldChoice() override; int getNumChoices() const { return numChoices; } const GooString *getChoice(int i) const { return choices ? choices[i].optionName : nullptr; } const GooString *getExportVal(int i) const { return choices ? choices[i].exportVal : nullptr; } // For multi-select choices it returns the first one const GooString *getSelectedChoice() const; // select the i-th choice void select(int i); // toggle selection of the i-th choice void toggle(int i); // deselect everything void deselectAll(); // only work for editable combo box, set the user-entered text as the current choice void setEditChoice(const GooString *new_content); const GooString *getEditChoice() const; bool isSelected(int i) const { return choices[i].selected; } int getNumSelected(); bool isCombo() const { return combo; } bool hasEdit() const { return edit; } bool isMultiSelect() const { return multiselect; } bool noSpellCheck() const { return doNotSpellCheck; } bool commitOnSelChange() const { return doCommitOnSelChange; } bool isListBox() const { return !combo; } int getTopIndex() const { return topIdx; } void print(int indent) override; void reset(const std::vector &excludedFields) override; protected: void unselectAll(); void updateSelection(); void fillChoices(FillValueType fillType); bool combo; bool edit; bool multiselect; bool doNotSpellCheck; bool doCommitOnSelChange; struct ChoiceOpt { GooString *exportVal; // the export value ("internal" name) GooString *optionName; // displayed name bool selected; // if this choice is selected }; int numChoices; ChoiceOpt *choices; bool *defaultChoices; GooString *editedChoice; int topIdx; // TI }; //------------------------------------------------------------------------ // FormFieldSignature //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormFieldSignature : public FormField { public: FormFieldSignature(PDFDoc *docA, Object &&dict, const Ref ref, FormField *parent, std::set *usedParents); // Use -1 for now as validationTime SignatureInfo *validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function &doneCallback); CertificateValidationStatus validateSignatureResult(); // returns a list with the boundaries of the signed ranges // the elements of the list are of type Goffset std::vector getSignedRangeBounds() const; // checks the length encoding of the signature and returns the hex encoded signature // if the check passed (and the checked file size as output parameter in checkedFileSize) // otherwise a nullptr is returned std::optional getCheckedSignature(Goffset *checkedFileSize); ~FormFieldSignature() override; Object *getByteRange() { return &byte_range; } const GooString *getSignature() const { return signature; } void setSignature(const GooString &sig); FormSignatureType getSignatureType() const { return signature_type; } void setSignatureType(FormSignatureType t) { signature_type = t; } const GooString &getCustomAppearanceContent() const; void setCustomAppearanceContent(const GooString &s); const GooString &getCustomAppearanceLeftContent() const; void setCustomAppearanceLeftContent(const GooString &s); double getCustomAppearanceLeftFontSize() const; void setCustomAppearanceLeftFontSize(double size); // Background image (ref to an object of type XObject). Invalid ref if not required. Ref getImageResource() const; void setImageResource(const Ref imageResourceA); void setCertificateInfo(std::unique_ptr &); FormWidget *getCreateWidget(); private: void parseInfo(); void hashSignedDataBlock(CryptoSign::VerificationInterface *handler, Goffset block_len); FormSignatureType signature_type; Object byte_range; GooString *signature; SignatureInfo *signature_info; GooString customAppearanceContent; GooString customAppearanceLeftContent; double customAppearanceLeftFontSize = 20; Ref imageResource = Ref::INVALID(); std::unique_ptr certificate_info; std::unique_ptr signature_handler; void print(int indent) override; }; //------------------------------------------------------------------------ // Form // This class handle the document-wide part of Form (things in the acroForm // Catalog entry). //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT Form { public: explicit Form(PDFDoc *doc); ~Form(); Form(const Form &) = delete; Form &operator=(const Form &) = delete; // Look up an inheritable field dictionary entry. static Object fieldLookup(Dict *field, const char *key); /* Creates a new Field of the type specified in obj's dict. used in Form::Form , FormField::FormField and Page::loadStandaloneFields */ static FormField *createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref, FormField *parent, std::set *usedParents); // Finds in the default resources dictionary a font named popplerfontXXX that // has the given fontFamily and fontStyle. This makes us relatively sure that we added that font ourselves std::string findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle) const; // Finds in the default resources a font that is suitable to create a signature annotation. // If none is found then it is added to the default resources. std::string findPdfFontNameToUseForSigning(); struct AddFontResult { std::string fontName; Ref ref; }; // Finds in the system a font name matching the given fontFamily and fontStyle // And adds it to the default resources dictionary, font name there will be popplerfontXXX except if forceName is true, // in that case the font name will be fontFamily + " " + fontStyle (if fontStyle is empty just fontFamily) AddFontResult addFontToDefaultResources(const std::string &fontFamily, const std::string &fontStyle, bool forceName = false); // Finds in the default resources dictionary a font named popplerfontXXX that // emulates fontToEmulate and can draw the given char std::string getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate) const; // Makes sure the default resources has fonts to draw all the given chars and as close as possible to the given pdfFontNameToEmulate // If needed adds fonts to the default resources dictionary, font names will be popplerfontXXX // If fieldResources is not nullptr, it is used instead of the to query the font to emulate instead of the default resources // Returns a list of all the added fonts (if any) std::vector ensureFontsForAllCharacters(const GooString *unicodeText, const std::string &pdfFontNameToEmulate, GfxResources *fieldResources = nullptr); bool getNeedAppearances() const { return needAppearances; } int getNumFields() const { return numFields; } FormField *getRootField(int i) const { return rootFields[i]; } const GooString *getDefaultAppearance() const { return defaultAppearance; } VariableTextQuadding getTextQuadding() const { return quadding; } GfxResources *getDefaultResources() const { return defaultResources; } Object *getDefaultResourcesObj() { return &resDict; } FormWidget *findWidgetByRef(Ref aref); FormField *findFieldByRef(Ref aref) const; FormField *findFieldByFullyQualifiedName(const std::string &name) const; void postWidgetsLoad(); const std::vector &getCalculateOrder() const { return calculateOrder; } void reset(const std::vector &fields, bool excludeFields); private: // Finds in the system a font name matching the given fontFamily and fontStyle // And adds it to the default resources dictionary, font name there will be popplerfontXXX except if forceName is true, // in that case the font name will be fontFamily + " " + fontStyle (if fontStyle is empty just fontFamily) AddFontResult addFontToDefaultResources(const std::string &filepath, int faceIndex, const std::string &fontFamily, const std::string &fontStyle, bool forceName = false); AddFontResult doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate); FormField **rootFields; int numFields; int size; PDFDoc *const doc; bool needAppearances; GfxResources *defaultResources; Object resDict; std::vector calculateOrder; // Variable Text GooString *defaultAppearance; VariableTextQuadding quadding; }; //------------------------------------------------------------------------ // FormPageWidgets //------------------------------------------------------------------------ class POPPLER_PRIVATE_EXPORT FormPageWidgets { public: FormPageWidgets(Annots *annots, unsigned int page, Form *form); ~FormPageWidgets(); FormPageWidgets(const FormPageWidgets &) = delete; FormPageWidgets &operator=(const FormPageWidgets &) = delete; int getNumWidgets() const { return numWidgets; } FormWidget *getWidget(int i) const { return widgets[i]; } void addWidgets(const std::vector &addedWidgets, unsigned int page); private: FormWidget **widgets; int numWidgets; int size; }; #endif