//======================================================================== // // 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-2019 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 Oliver Sander // Copyright 2019 João Netto // //======================================================================== #ifndef FORM_H #define FORM_H #include "Object.h" #include "Annot.h" #include #include #include class GooString; class Array; class Dict; class Annot; class AnnotWidget; class Annots; class LinkAction; class GfxResources; class PDFDoc; class SignatureInfo; class SignatureHandler; enum FormFieldType { formButton, formText, formChoice, formSignature, formUndef }; enum FormButtonType { formButtonCheck, formButtonPush, formButtonRadio }; enum VariableTextQuadding { quaddingLeftJustified, quaddingCentered, quaddingRightJustified }; enum FormSignatureType { adbe_pkcs7_sha1, adbe_pkcs7_detached, ETSI_CAdES_detached }; 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 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 LinkAction *getAdditionalAction(Annot::FormAdditionalActionsType type); // The caller should delete the result bool setAdditionalAction(Annot::FormAdditionalActionsType t, const GooString &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; } 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 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 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 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; //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 FormWidgetSignature: public FormWidget { public: FormWidgetSignature(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); void updateWidgetAppearance() override; FormSignatureType signatureType(); // Use -1 for now as validationTime SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime); // returns a list with the boundaries of the signed ranges // the elements of the list are of type Goffset std::vector getSignedRangeBounds(); // 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 GooString* getCheckedSignature(Goffset *checkedFileSize); const GooString *getSignature() const; }; //------------------------------------------------------------------------ // 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 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; } GooString* getDefaultAppearance() const { return defaultAppearance; } 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() { return terminal ? numChildren : 0; } FormWidget *getWidget(int i) { return terminal ? widgets[i] : nullptr; } // only implemented in FormFieldButton virtual void fillChildrenSiblingsID (); void createWidgetAnnotations(); void printTree(int indent = 0); virtual void print(int indent = 0); protected: void _createWidget (Object *obj, Ref aref); void createChildren(std::set *usedParents); void updateChildrenAppearance(); 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; 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 getState(const char *state) const; const char *getAppearanceState() const { return appearanceState.isName() ? appearanceState.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; ~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 }; //------------------------------------------------------------------------ // 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; static int tokenizeDA(const GooString* daString, std::vector* daToks, const char* searchTok); protected: int parseDA(std::vector* daToks); GooString* content; GooString* internalContent; 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; protected: void unselectAll(); void updateSelection(); 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; GooString* editedChoice; int topIdx; // TI }; //------------------------------------------------------------------------ // FormFieldSignature //------------------------------------------------------------------------ class FormFieldSignature: public FormField { friend class FormWidgetSignature; public: FormFieldSignature(PDFDoc *docA, Object &&dict, const Ref ref, FormField *parent, std::set *usedParents); // Use -1 for now as validationTime SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime); ~FormFieldSignature() override; Object* getByteRange() { return &byte_range; } const GooString* getSignature() const { return signature; } private: void parseInfo(); void hashSignedDataBlock(SignatureHandler *handler, Goffset block_len); FormSignatureType signature_type; Object byte_range; GooString *signature; SignatureInfo *signature_info; void print(int indent) override; }; //------------------------------------------------------------------------ // Form // This class handle the document-wide part of Form (things in the acroForm // Catalog entry). //------------------------------------------------------------------------ class Form { public: Form(PDFDoc *docA, Object* acroForm); ~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 and FormField::FormField */ static FormField *createFieldFromDict (Object &&obj, PDFDoc *docA, const Ref aref, FormField *parent, std::set *usedParents); Object *getObj () const { return acroForm; } 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); void postWidgetsLoad(); const std::vector &getCalculateOrder() const { return calculateOrder; } private: FormField** rootFields; int numFields; int size; PDFDoc *doc; XRef* xref; Object *acroForm; bool needAppearances; GfxResources *defaultResources; Object resDict; std::vector calculateOrder; // Variable Text GooString *defaultAppearance; VariableTextQuadding quadding; }; //------------------------------------------------------------------------ // FormPageWidgets //------------------------------------------------------------------------ class 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]; } private: FormWidget** widgets; int numWidgets; int size; }; #endif