blob: b6ec9140abba25cf77d90d841584f746f69306ba [file] [log] [blame]
//========================================================================
//
// Form.h
//
// This file is licensed under the GPLv2 or later
//
// Copyright 2006 Julien Rebetez <julienr@svn.gnome.org>
// Copyright 2007, 2008, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
// Copyright 2007-2010, 2012, 2015-2019 Albert Astals Cid <aacid@kde.org>
// Copyright 2010 Mark Riedesel <mark@klowner.com>
// Copyright 2011 Pino Toscano <pino@kde.org>
// Copyright 2012 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright 2013 Adrian Johnson <ajohnson@redneon.com>
// Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
// Copyright 2015 André Esser <bepandre@hotmail.com>
// Copyright 2017 Roland Hieber <r.hieber@pengutronix.de>
// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
// Copyright 2018 Andre Heinecke <aheinecke@intevation.de>
// Copyright 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
// Copyright 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@protonmail.com>
// Copyright 2019 Oliver Sander <oliver.sander@tu-dresden.de>
// Copyright 2019 João Netto <joaonetto901@gmail.com>
//
//========================================================================
#ifndef FORM_H
#define FORM_H
#include "Object.h"
#include "Annot.h"
#include <time.h>
#include <set>
#include <vector>
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 *dict, unsigned num, Ref ref, FormField *p);
~FormWidgetButton ();
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 *dict, 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 *dict, unsigned num, Ref ref, FormField *p);
~FormWidgetChoice();
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 *dict, 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<Goffset> 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<int> *usedParents, FormFieldType t=formUndef);
virtual ~FormField();
// Accessors.
FormFieldType getType() const { return type; }
Object* getObj() { return &obj; }
Ref getRef() { return ref; }
void setReadOnly (bool b);
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<int> *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<int> *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();
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 &&dict, const Ref ref, FormField *parent, std::set<int> *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();
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<GooString*>* daToks, const char* searchTok);
protected:
int parseDA(std::vector<GooString*>* 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<int> *usedParents);
~FormFieldChoice();
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<int> *usedParents);
// Use -1 for now as validationTime
SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime);
~FormFieldSignature();
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<int> *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<Ref> &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<Ref> 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