PDF4QT/Pdf4QtLib/sources/pdfform.h
2022-03-26 19:26:32 +01:00

750 lines
30 KiB
C++

// Copyright (C) 2020-2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFFORM_H
#define PDFFORM_H
#include "pdfobject.h"
#include "pdfdocument.h"
#include "pdfannotation.h"
#include "pdfdocumentdrawinterface.h"
#include "pdfsignaturehandler.h"
#include "pdfxfaengine.h"
#include <QTextLayout>
#include <optional>
namespace pdf
{
class PDFFormField;
class PDFFormManager;
class PDFObjectStorage;
class PDFModifiedDocument;
class PDFDocumentModifier;
using PDFFormFieldPointer = QSharedPointer<PDFFormField>;
using PDFFormFields = std::vector<PDFFormFieldPointer>;
using PDFWidgetToFormFieldMapping = std::map<PDFObjectReference, PDFFormField*>;
/// A simple proxy to the widget annotation
class PDFFormWidget
{
public:
explicit inline PDFFormWidget() = default;
explicit inline PDFFormWidget(PDFObjectReference page, PDFObjectReference widget, PDFFormField* parentField, PDFAnnotationAdditionalActions actions);
PDFObjectReference getPage() const { return m_page; }
PDFObjectReference getWidget() const { return m_widget; }
PDFFormField* getParent() const { return m_parentField; }
const PDFAction* getAction(PDFAnnotationAdditionalActions::Action action) const { return m_actions.getAction(action); }
/// Parses form widget from the object reference. If some error occurs
/// then empty object is returned, no exception is thrown.
/// \param storage Storage
/// \param reference Widget reference
/// \param parentField Parent field
static PDFFormWidget parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
private:
PDFObjectReference m_page;
PDFObjectReference m_widget;
PDFFormField* m_parentField;
PDFAnnotationAdditionalActions m_actions;
};
using PDFFormWidgets = std::vector<PDFFormWidget>;
/// Form field is terminal or non-terminal field (non-terminal fields
/// have children), fields represents various interactive widgets, such as
/// checks, radio buttons, text edits etc., which are editable and user
/// can interact with them.
class PDFFormField
{
public:
explicit inline PDFFormField() = default;
virtual ~PDFFormField() = default;
enum class FieldType
{
Invalid,
Button,
Text,
Choice,
Signature
};
enum NameType
{
Partial, ///< Partial name for this field
UserCaption, ///< Name used in GUI (for example, in message boxes)
FullyQualified, ///< Fully qualified name (according to the PDF specification 1.7)
Export, ///< Name for export
NameTypeEnd
};
using FieldNames = std::array<QString, NameTypeEnd>;
enum FieldFlag
{
None = 0,
/// Field is read only, it doesn't respond on mouse clicks (if it is a button),
/// associated widget annotation will not interact with the user. Field can't
/// change it's value. Mainly used for calculable fields.
ReadOnly = 1 << 0,
/// If set, field is required, when submitting form by submit action. If submit
/// action is triggered, then all fields with this flag must have a value.
Required = 1 << 1,
/// If set, field value must not be exported by submit form action.
NoExport = 1 << 2,
/// Text fields only. If set, then text can span multiple lines. Otherwise,
/// text is restricted to single line.
Multiline = 1 << 12,
/// Text fields only. If set, field is intended to display text edit, which
/// can edit passwords. Password characters should not be echoed to the screen
/// and value of this field should not be stored in PDF file.
Password = 1 << 13,
/// Only for radio buttons. If set, at least one radio button is checked.
/// If user clicks on checked radio button, it is not checked off. Otherwise
/// user can uncheck checked radio button (so no button is selected).
NoToggleToOff = 1 << 14,
/// Valid only for buttons which have PushButton flag unset. If Radio flag is set,
/// then widget is radio button, otherwise widget is push button.
Radio = 1 << 15,
/// Valid only for buttons. If set, button is push button.
PushButton = 1 << 16,
/// For choice fields only. If set, choice field is combo box.
/// If clear, it is a list box.
Combo = 1 << 17,
/// For choice fields combo boxes only. If set, combo box is editable,
/// i.e. user can enter custom text. If this flag is cleared, then combo box
/// is not editable and user can only select items from the drop down list.
Edit = 1 << 18,
/// For choice fields only. If set, the field option's items should be sorted
/// alphabetically, but not by the viewer application, but by author of the form.
/// Viewer application should respect Opt array and display items in that order.
Sort = 1 << 19,
/// Text fields only. Text field is used to select file path, whose contents
/// should be submitted as the value of the field.
FileSelect = 1 << 20,
/// For choice fields only. If set, then user can select multiple choices
/// simultaneously, if clear, then only one item should be selected at the time.
MultiSelect = 1 << 21,
/// Text fields only. If set, texts entered in this field should not be spell checked.
DoNotSpellcheck = 1 << 22,
/// Text fields only. Allow only so much text, which fits field's area. If field's area is filled,
/// then do not allow the user to store more text in the field.
DoNotScroll = 1 << 23,
/// Text fields only. If set, then MaxLen entry and annotation rectangle is used
/// to divide space equally for each character. Text is laid out into those spaces.
Comb = 1 << 24,
/// Valid only for radio buttons. Radio buttons in a group of radio buttons,
/// which have same value for 'On' state, will turn On and Off in unison, if one
/// is checked, all are checked. If clear, radio buttons are mutually exclusive.
RadiosInUnison = 1 << 25,
/// Text fields only. Value of this field should be a rich text.
RichText = 1 << 25,
/// Choice fields only. If set, then when user selects choice by mouse,
/// data is immediately commited. Otherwise data are commited only, when
/// widget lose focus.
CommitOnSelectionChange = 1 << 26
};
Q_DECLARE_FLAGS(FieldFlags, FieldFlag)
PDFObjectReference getSelfReference() const { return m_selfReference; }
FieldType getFieldType() const { return m_fieldType; }
const PDFFormField* getParentField() const { return m_parentField; }
const PDFFormFields& getChildFields() const { return m_childFields; }
const PDFFormWidgets& getWidgets() const { return m_widgets; }
const QString& getName(NameType nameType) const { return m_fieldNames.at(nameType); }
FieldFlags getFlags() const { return m_fieldFlags; }
const PDFObject& getValue() const { return m_value; }
const PDFObject& getDefaultValue() const { return m_defaultValue; }
/// Fills widget to form field mapping
/// \param mapping Form field mapping
void fillWidgetToFormFieldMapping(PDFWidgetToFormFieldMapping& mapping);
/// Reloads value from object storage. Actually stored value is lost.
virtual void reloadValue(const PDFObjectStorage* storage, PDFObject parentValue);
/// Applies function to this form field and all its descendants,
/// in pre-order (first application is to the parent, following
/// calls to apply for children).
/// \param functor Functor to apply
void apply(const std::function<void(const PDFFormField*)>& functor) const;
/// Applies function to this form field and all its descendants,
/// in pre-order (first application is to the parent, following
/// calls to apply for children).
/// \param functor Functor to apply
void modify(const std::function<void(PDFFormField*)>& functor);
/// Returns action by type. If action is not found, nullptr is returned
/// \param action Action type
const PDFAction* getAction(PDFAnnotationAdditionalActions::Action action) const { return m_additionalActions.getAction(action); }
/// Returns container of actions
const PDFAnnotationAdditionalActions& getActions() const { return m_additionalActions; }
/// Parses form field from the object reference. If some error occurs
/// then null pointer is returned, no exception is thrown.
/// \param storage Storage
/// \param reference Field reference
/// \param parentField Parent field (or nullptr, if it is root field)
static PDFFormFieldPointer parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
struct SetValueParameters
{
enum class Scope
{
User, ///< Changed value comes from user input
Internal ///< Value is changed by some program operation (for example, calculation)
};
PDFObject value;
PDFObjectReference invokingWidget;
PDFFormField* invokingFormField = nullptr;
PDFDocumentModifier* modifier = nullptr;
PDFFormManager* formManager = nullptr;
Scope scope = Scope::User;
// Choice list box field only
PDFInteger listboxTopIndex= 0;
std::vector<PDFInteger> listboxChoices;
};
/// Sets value to the form field. If value has been correctly
/// set, then true is returned, otherwise false is returned.
/// This function also verifies, if value can be set (i.e. form field
/// is editable, and value is valid).
/// \param parameters Parameters
virtual bool setValue(const SetValueParameters& parameters);
struct ResetValueParameters
{
PDFDocumentModifier* modifier = nullptr;
PDFFormManager* formManager = nullptr;
};
/// Resets value to the field's default value. Widget annotation
/// appearances are also updated.
/// \param parameters Parameters
virtual void resetValue(const ResetValueParameters& parameters);
protected:
PDFObjectReference m_selfReference;
FieldType m_fieldType = FieldType::Invalid;
PDFFormField* m_parentField = nullptr;
PDFFormFields m_childFields;
PDFFormWidgets m_widgets;
FieldNames m_fieldNames;
FieldFlags m_fieldFlags = None;
PDFObject m_value;
PDFObject m_defaultValue;
PDFAnnotationAdditionalActions m_additionalActions;
};
/// Represents pushbutton, checkbox and radio button (which is distinguished
/// by flags).
class PDFFormFieldButton : public PDFFormField
{
public:
explicit inline PDFFormFieldButton() = default;
enum class ButtonType
{
PushButton,
RadioButton,
CheckBox
};
/// Determines button type from form field's flags
ButtonType getButtonType() const;
const QStringList& getOptions() const { return m_options; }
/// Returns appearance state, which corresponds to the checked
/// state of checkbox or radio button. If error occurs, then
/// empty byte array is returned.
/// \param formManager Form manager
/// \param widget Widget
static QByteArray getOnAppearanceState(const PDFFormManager* formManager, const PDFFormWidget* widget);
/// Returns appearance state, which corresponds to the unchecked
/// state of checkbox or radio button. If error occurs, then
/// empty byte array is returned.
/// \param formManager Form manager
/// \param widget Widget
static QByteArray getOffAppearanceState(const PDFFormManager* formManager, const PDFFormWidget* widget);
virtual bool setValue(const SetValueParameters& parameters) override;
virtual void resetValue(const ResetValueParameters& parameters) override;
private:
friend PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
/// List of export names of 'On' state for radio buttons. In widget annotation's appearance
/// dictionaries, state names are computer generated numbers (for example /1, /3, ...),
/// which are indices to this string list. This allows to distinguish between
/// different widget annotations, even if they have same value in m_options array.
QStringList m_options;
};
/// Represents single line, or multiline text field
class PDFFormFieldText : public PDFFormField
{
public:
explicit inline PDFFormFieldText() = default;
PDFInteger getTextMaximalLength() const { return m_maxLength; }
const QByteArray& getDefaultAppearance() const { return m_defaultAppearance; }
Qt::Alignment getAlignment() const { return m_alignment; }
const QString& getRichTextDefaultStyle() const { return m_defaultStyle; }
const QString& getRichTextValue() const { return m_richTextValue; }
virtual bool setValue(const SetValueParameters& parameters) override;
virtual void resetValue(const ResetValueParameters& parameters) override;
private:
friend PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
/// Maximal length of text in the field. If zero,
/// no maximal length is specified.
PDFInteger m_maxLength = 0;
/// Default appearance
QByteArray m_defaultAppearance;
/// Text field alignment
Qt::Alignment m_alignment = Qt::Alignment();
/// Default style
QString m_defaultStyle;
/// Rich text value
QString m_richTextValue;
};
class PDFFormFieldChoice : public PDFFormField
{
using BaseClass = PDFFormField;
public:
explicit inline PDFFormFieldChoice() = default;
bool isComboBox() const { return m_fieldFlags.testFlag(Combo); }
bool isEditableComboBox() const { return m_fieldFlags.testFlag(Edit); }
bool isListBox() const { return !isComboBox(); }
struct Option
{
QString exportString;
QString userString;
};
using Options = std::vector<Option>;
const Options& getOptions() const { return m_options; }
PDFInteger getTopIndex() const { return m_topIndex; }
const PDFObject& getSelection() const { return m_selection; }
virtual bool setValue(const SetValueParameters& parameters) override;
virtual void resetValue(const ResetValueParameters& parameters) override;
virtual void reloadValue(const PDFObjectStorage* storage, PDFObject parentValue) override;
private:
friend PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
Options m_options;
PDFInteger m_topIndex;
PDFObject m_selection;
};
class PDFFormFieldSignature : public PDFFormField
{
public:
explicit inline PDFFormFieldSignature() = default;
const PDFSignature& getSignature() const { return m_signature; }
private:
friend PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
PDFSignature m_signature;
};
/// This class represents interactive form. Interactive form fields can span multiple
/// document pages. So this object represents all interactive form fields in the document.
/// Fields forms tree-like structure, where leafs are usually widgets. Fields include
/// ordinary widgets, such as buttons, check boxes, combo boxes and text fields, and one
/// special - signature field, which represents digital signature.
class PDF4QTLIBSHARED_EXPORT PDFForm
{
public:
explicit inline PDFForm() = default;
enum class FormType
{
None,
AcroForm,
XFAForm
};
enum SignatureFlag
{
None = 0x0000,
SignatureExists = 0x0001, ///< If set, at least one signature exists in the document
AppendOnly = 0x0002, ///< If set, signature may be invalidated during rewrite
};
Q_DECLARE_FLAGS(SignatureFlags, SignatureFlag)
FormType getFormType() const { return m_formType; }
const PDFFormFields& getFormFields() const { return m_formFields; }
bool isAppearanceUpdateNeeded() const { return m_needAppearances; }
SignatureFlags getSignatureFlags() const { return m_signatureFlags; }
const std::vector<PDFObjectReference>& getCalculationOrder() const { return m_calculationOrder; }
const PDFObject& getResources() const { return m_resources; }
const std::optional<QByteArray>& getDefaultAppearance() const { return m_defaultAppearance; }
const std::optional<PDFInteger>& getQuadding() const { return m_quadding; }
const PDFObject& getXFA() const { return m_xfa; }
Qt::Alignment getDefaultAlignment() const;
bool isAcroForm() const { return getFormType() == PDFForm::FormType::AcroForm; }
bool isXFAForm() const { return getFormType() == PDFForm::FormType::XFAForm; }
/// Returns form field for widget. If widget doesn't have attached form field,
/// then nullptr is returned.
/// \param widget Widget annotation
const PDFFormField* getFormFieldForWidget(PDFObjectReference widget) const;
/// Returns form field for widget. If widget doesn't have attached form field,
/// then nullptr is returned.
/// \param widget Widget annotation
PDFFormField* getFormFieldForWidget(PDFObjectReference widget);
/// Applies function to all form fields present in the form,
/// in pre-order (first application is to the parent, following
/// calls to apply for children).
/// \param functor Functor to apply
void apply(const std::function<void(const PDFFormField*)>& functor) const;
/// Parses form from the object. If some error occurs
/// then empty form is returned, no exception is thrown.
/// \param document Document
/// \param reference Field reference
static PDFForm parse(const PDFDocument* document, PDFObject object);
private:
void updateWidgetToFormFieldMapping();
FormType m_formType = FormType::None;
PDFFormFields m_formFields;
bool m_needAppearances = false;
SignatureFlags m_signatureFlags = None;
std::vector<PDFObjectReference> m_calculationOrder;
PDFObject m_resources;
std::optional<QByteArray> m_defaultAppearance;
std::optional<PDFInteger> m_quadding;
PDFObject m_xfa;
PDFWidgetToFormFieldMapping m_widgetToFormField;
};
/// Base class for editors of form fields. It enables editation
/// of form fields, such as entering text, clicking on check box etc.
class PDFFormFieldWidgetEditor
{
public:
explicit PDFFormFieldWidgetEditor(PDFFormManager* formManager, PDFFormWidget formWidget);
virtual ~PDFFormFieldWidgetEditor() = default;
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event);
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event);
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event);
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event, const QPointF& mousePagePosition);
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event, const QPointF& mousePagePosition);
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event, const QPointF& mousePagePosition);
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event, const QPointF& mousePagePosition);
virtual void wheelEvent(QWidget* widget, QWheelEvent* event, const QPointF& mousePagePosition);
virtual void reloadValue() { }
virtual bool isEditorDrawEnabled() const { return false; }
virtual QRectF getActiveEditorRectangle() const { return QRectF(); }
const PDFFormWidget* getFormWidget() const { return &m_formWidget; }
PDFFormField* getFormField() const { return m_formWidget.getParent(); }
PDFObjectReference getWidgetAnnotation() const { return m_formWidget.getWidget(); }
void setFocus(bool hasFocus);
/// Draw form field widget using given parameters. It is used, when
/// we want to draw editor contents on the painter using parameters.
/// Parameter \p edit decides, if editor is drawn, or static contents
/// based on field value is drawn.
/// \param parameters Parameters
/// \param edit Draw editor or static contents
virtual void draw(AnnotationDrawParameters& parameters, bool edit) const;
protected:
/// This function is called every time, the focus state changes
/// \param focused If editor was focused, or not
virtual void setFocusImpl(bool focused) { Q_UNUSED(focused); }
void performKeypadNavigation(QWidget* widget, QKeyEvent* event);
PDFFormManager* m_formManager;
PDFFormWidget m_formWidget;
bool m_hasFocus;
};
/// Form manager. Manages all form widgets functionality - triggers actions,
/// edits fields, updates annotation appearances, etc. Valid pointer to annotation
/// manager is requirement.
class PDF4QTLIBSHARED_EXPORT PDFFormManager : public QObject, public IDrawWidgetInputInterface
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
explicit PDFFormManager(PDFDrawWidgetProxy* proxy, QObject* parent);
virtual ~PDFFormManager() override;
enum FormAppearanceFlag
{
None = 0x0000,
HighlightFields = 0x0001,
HighlightRequiredFields = 0x0002,
};
Q_DECLARE_FLAGS(FormAppearanceFlags, FormAppearanceFlag)
bool hasForm() const { return hasAcroForm() || hasXFAForm(); }
bool hasAcroForm() const { return m_form.getFormType() == PDFForm::FormType::AcroForm; }
bool hasXFAForm() const { return m_form.getFormType() == PDFForm::FormType::XFAForm; }
const PDFForm* getForm() const { return &m_form; }
/// Returns form field for widget. If widget doesn't have attached form field,
/// then nullptr is returned.
/// \param widget Widget annotation
const PDFFormField* getFormFieldForWidget(PDFObjectReference widget) const { return m_form.getFormFieldForWidget(widget); }
/// Returns form field for widget. If widget doesn't have attached form field,
/// then nullptr is returned.
/// \param widget Widget annotation
PDFFormField* getFormFieldForWidget(PDFObjectReference widget) { return m_form.getFormFieldForWidget(widget); }
PDFAnnotationManager* getAnnotationManager() const;
void setAnnotationManager(PDFAnnotationManager* annotationManager);
const PDFDocument* getDocument() const;
void setDocument(const PDFModifiedDocument& document);
FormAppearanceFlags getAppearanceFlags() const;
void setAppearanceFlags(FormAppearanceFlags flags);
/// Returns true, if form field has text (for example, it is a text box,
/// or editable combo box)
/// \param widgetAnnotation Widget annotation
bool hasFormFieldWidgetText(PDFObjectReference widgetAnnotation) const;
/// Returns all form field widgets. This function is not simple getter,
/// call can be expensive, because all form fields are accessed.
PDFFormWidgets getWidgets() const;
/// Applies function to all form fields present in the form,
/// in pre-order (first application is to the parent, following
/// calls to apply for children).
/// \param functor Functor to apply
void apply(const std::function<void(const PDFFormField*)>& functor) const;
/// Applies function to all form fields present in the form,
/// in pre-order (first application is to the parent, following
/// calls to apply for children).
/// \param functor Functor to apply
void modify(const std::function<void(PDFFormField*)>& functor) const;
/// Sets focus to the editor. Is is allowed to pass nullptr to this
/// function, it means that no editor is focused.
/// \param editor Editor to be focused
void setFocusToEditor(PDFFormFieldWidgetEditor* editor);
/// Tries to set focus on next/previous form field in the focus chain.
/// Function returns true, if focus has been successfully set.
/// \param next Focus next (true) or previous (false) widget
bool focusNextPrevFormField(bool next);
/// Returns true, if widget is focused.
/// \param widget Widget annotation reference
bool isFocused(PDFObjectReference widget) const;
/// Tries to find appropriate action and returns it. If action is not found,
/// then nullptr is returned.
/// \param actionType Action to be performed
/// \param widget Form field widget
const PDFAction* getAction(PDFAnnotationAdditionalActions::Action actionType, const PDFFormWidget* widget);
/// Returns default form apperance flags
static constexpr FormAppearanceFlags getDefaultApperanceFlags() { return FormAppearanceFlags(HighlightFields | HighlightRequiredFields); }
struct MouseEventInfo
{
/// Form field under mouse event, nullptr, if
/// no form field is under mouse button.
PDFFormField* formField = nullptr;
/// Form field widget editor, which is associated
/// with given form field.
PDFFormFieldWidgetEditor* editor = nullptr;
/// Mouse position in form field coordinate space
QPointF mousePosition;
/// Matrix, which maps from device space to widget space
QMatrix deviceToWidget;
/// Returns true, if mouse event info is valid, i.e.
/// mouse event occurs above some form field.
bool isValid() const { return editor != nullptr; }
};
/// Tries to set value to the form field
void setFormFieldValue(PDFFormField::SetValueParameters parameters);
/// Get widget rectangle (from annotation)
QRectF getWidgetRectangle(const PDFFormWidget& widget) const;
/// Returns editor for form field
PDFFormFieldWidgetEditor* getEditor(const PDFFormField* formField) const;
/// Is committing data disabled?
bool isCommitDisabled() const { return m_isCommitDisabled; }
/// Performs reset action. Action must be valid pointer. Fields are resetted
/// according to the criteria specified in reset action.
/// \param action Reset action
void performResetAction(const PDFActionResetForm* action);
// interface IDrawWidgetInputInterface
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
/// Returns tooltip generated from annotation
virtual QString getTooltip() const override { return QString(); }
/// Returns current cursor
virtual const std::optional<QCursor>& getCursor() const override;
/// Draws XFA form, or does nothing, if XFA form is not present
/// \param pagePointToDevicePointMatrix Page point to device point matrix
/// \param page Page
/// \param errors Error list (for reporting rendering errors)
/// \param painter Painter
void drawXFAForm(const QMatrix& pagePointToDevicePointMatrix,
const PDFPage* page,
QList<PDFRenderError>& errors,
QPainter* painter);
/// Performs paging, when XFA form needs to change page count and size.
/// If some change needs to be done, then signal \p documentModified
/// is emitted.
void performPaging();
virtual int getInputPriority() const override { return FormPriority; }
signals:
void actionTriggered(const pdf::PDFAction* action);
void documentModified(pdf::PDFModifiedDocument document);
private:
void updateFormWidgetEditors();
void updateFieldValues();
MouseEventInfo getMouseEventInfo(QWidget* widget, QPoint point);
struct MouseGrabInfo
{
MouseEventInfo info;
int mouseGrabNesting = 0;
bool isMouseGrabbed() const { return mouseGrabNesting > 0; }
};
bool isMouseGrabbed() const { return m_mouseGrabInfo.isMouseGrabbed(); }
/// Grabs mouse input, if mouse is already grabbed, or if event
/// is accepted. When mouse is grabbed, then mouse input events
/// are sent to active editor and are automatically accepted.
/// \param info Mouse event info
/// \param event Mouse event
void grabMouse(const MouseEventInfo& info, QMouseEvent* event);
/// Release mouse input
/// \param info Mouse event info
/// \param event Mouse event
void ungrabMouse(const MouseEventInfo& info, QMouseEvent* event);
/// Releases all form widget editors
void clearEditors();
PDFDrawWidgetProxy* m_proxy;
PDFAnnotationManager* m_annotationManager;
const PDFDocument* m_document;
FormAppearanceFlags m_flags;
PDFForm m_form;
bool m_isCommitDisabled;
PDFXFAEngine m_xfaEngine;
std::vector<PDFFormFieldWidgetEditor*> m_widgetEditors;
PDFFormFieldWidgetEditor* m_focusedEditor;
MouseGrabInfo m_mouseGrabInfo;
std::optional<QCursor> m_mouseCursor;
};
} // namespace pdf
#endif // PDFFORM_H