Checkbox and radio form fields

This commit is contained in:
Jakub Melka 2020-05-02 18:04:25 +02:00
parent bfb26c4807
commit f604dd77b2
14 changed files with 584 additions and 23 deletions

View File

@ -148,6 +148,7 @@ public:
_QPolygonF,
_QDateTime,
_QLocale,
_QByteArray,
_Polygons,
_TextAnnotationIcon,
_LinkHighlightMode,
@ -363,7 +364,8 @@ public:
Structure,
Annotations,
ColorSpace,
Actions
Actions,
Forms
};
Q_ENUM(FunctionType)

View File

@ -55,6 +55,9 @@ GeneratorMainWindow::GeneratorMainWindow(QWidget *parent) :
connect(ui->parameterValueEdit, &QLineEdit::editingFinished, this, &GeneratorMainWindow::saveGeneratedSettings);
connect(ui->parameterDescriptionEdit, &QTextBrowser::textChanged, this, &GeneratorMainWindow::saveGeneratedSettings);
ui->parameterItemTypeCombo->setMaxVisibleItems(30);
ui->parameterDataTypeCombo->setMaxVisibleItems(30);
setWindowState(Qt::WindowMaximized);
updateFunctionListUI();
}

View File

@ -165,6 +165,23 @@ PDFObject PDFAppeareanceStreams::getAppearance(Appearance appearance, const QByt
return PDFObject();
}
QByteArrayList PDFAppeareanceStreams::getAppearanceStates(Appearance appearance)
{
QByteArrayList result;
for (const auto& item : m_appearanceStreams)
{
if (item.first.first != appearance)
{
continue;
}
result << item.first.second;
}
return result;
}
PDFAnnotation::PDFAnnotation() :
m_flags(),
m_structParent(0)

View File

@ -209,6 +209,10 @@ public:
/// \param state State name
PDFObject getAppearance(Appearance appearance, const QByteArray& state) const;
/// Returns list of appearance states for given appearance
/// \param appearance Appearance
QByteArrayList getAppearanceStates(Appearance appearance);
private:
std::map<Key, PDFObject> m_appearanceStreams;
};

View File

@ -45,6 +45,12 @@ QByteArray PDFObjectStorage::getDecodedStream(const PDFStream* stream) const
return PDFStreamFilterStorage::getDecodedStream(stream, std::bind(QOverload<const PDFObject&>::of(&PDFObjectStorage::getObject), this, std::placeholders::_1), getSecurityHandler());
}
bool PDFDocument::operator==(const PDFDocument& other) const
{
// Document is considered equal, if storage is equal
return m_pdfObjectStorage == other.m_pdfObjectStorage;
}
QByteArray PDFDocument::getDecodedStream(const PDFStream* stream) const
{
return m_pdfObjectStorage.getDecodedStream(stream);
@ -215,6 +221,13 @@ void PDFDocument::initInfo()
}
}
bool PDFObjectStorage::operator==(const PDFObjectStorage& other) const
{
// We compare just content. Security handler just defines encryption behavior.
return m_objects == other.m_objects &&
m_trailerDictionary == other.m_trailerDictionary;
}
const PDFObject& PDFObjectStorage::getObject(PDFObjectReference reference) const
{
if (reference.objectNumber >= 0 &&

View File

@ -48,11 +48,17 @@ public:
constexpr inline PDFObjectStorage& operator=(const PDFObjectStorage&) = default;
constexpr inline PDFObjectStorage& operator=(PDFObjectStorage&&) = default;
bool operator==(const PDFObjectStorage& other) const;
bool operator!=(const PDFObjectStorage& other) const { return !(*this == other); }
struct Entry
{
constexpr inline explicit Entry() = default;
inline explicit Entry(PDFInteger generation, PDFObject object) : generation(generation), object(std::move(object)) { }
inline bool operator==(const Entry& other) const { return generation == other.generation && object == other.object; }
inline bool operator!=(const Entry& other) const { return !(*this == other); }
PDFInteger generation = 0;
PDFObject object;
};
@ -386,6 +392,9 @@ class PDFFORQTLIBSHARED_EXPORT PDFDocument
public:
explicit PDFDocument() = default;
bool operator==(const PDFDocument& other) const;
bool operator!=(const PDFDocument& other) const { return !(*this == other); }
const PDFObjectStorage& getStorage() const { return m_pdfObjectStorage; }
/// Info about the document. Title, Author, Keywords... It also stores "extra"

View File

@ -1154,6 +1154,25 @@ PDFContentStreamBuilder::ContentStream PDFContentStreamBuilder::end(QPainter* pa
return result;
}
PDFDocumentModifier::PDFDocumentModifier(const PDFDocument* originalDocument) :
m_originalDocument(originalDocument),
m_builder(originalDocument)
{
}
bool PDFDocumentModifier::finalize()
{
PDFDocument document = m_builder.build();
if (document != *m_originalDocument)
{
m_modifiedDocument.reset(new PDFDocument(qMove(document)));
return true;
}
return false;
}
/* START GENERATED CODE */
PDFObjectReference PDFDocumentBuilder::appendPage(QRectF mediaBox)
@ -2898,6 +2917,21 @@ PDFObject PDFDocumentBuilder::createTrailerDictionary(PDFObjectReference catalog
}
void PDFDocumentBuilder::setAnnotationAppearanceState(PDFObjectReference annotation,
QByteArray appearanceState)
{
PDFObjectFactory objectBuilder;
objectBuilder.beginDictionary();
objectBuilder.beginDictionaryItem("AS");
objectBuilder << WrapName(appearanceState);
objectBuilder.endDictionaryItem();
objectBuilder.endDictionary();
PDFObject annotationObject = objectBuilder.takeObject();
mergeTo(annotation, annotationObject);
}
void PDFDocumentBuilder::setAnnotationBorder(PDFObjectReference annotation,
PDFReal hRadius,
PDFReal vRadius,
@ -3160,6 +3194,21 @@ void PDFDocumentBuilder::setDocumentTitle(QString title)
}
void PDFDocumentBuilder::setFormFieldValue(PDFObjectReference formField,
PDFObject value)
{
PDFObjectFactory objectBuilder;
objectBuilder.beginDictionary();
objectBuilder.beginDictionaryItem("V");
objectBuilder << value;
objectBuilder.endDictionaryItem();
objectBuilder.endDictionary();
PDFObject formFieldObject = objectBuilder.takeObject();
mergeTo(formField, formFieldObject);
}
void PDFDocumentBuilder::setLanguage(QLocale locale)
{
PDFObjectFactory objectBuilder;

View File

@ -242,7 +242,7 @@ public:
/// Creates a new blank document (with no pages)
explicit PDFDocumentBuilder();
///
/// Creates a new document as modification of old document
explicit PDFDocumentBuilder(const PDFDocument* document);
/// Resets the object to the initial state.
@ -800,6 +800,13 @@ public:
PDFObject createTrailerDictionary(PDFObjectReference catalog);
/// Sets annotation appearance state.
/// \param annotation Annotation
/// \param appearanceState Appearance state
void setAnnotationAppearanceState(PDFObjectReference annotation,
QByteArray appearanceState);
/// Sets annotation border.
/// \param annotation Annotation
/// \param hRadius Horizontal corner radius
@ -913,6 +920,15 @@ public:
void setDocumentTitle(QString title);
/// Sets form field value. Value must be correct for this form field, no checking is performed. Also, if
/// you use this function, annotation widgets, which are attached to this form field, should also be
/// updated (for example, appearance state and sometimes appearance streams).
/// \param formField Form field
/// \param value Value
void setFormFieldValue(PDFObjectReference formField,
PDFObject value);
/// Set document language.
/// \param locale Locale, from which is language determined
void setLanguage(QLocale locale);
@ -959,6 +975,38 @@ private:
PDFVersion m_version;
};
/// This class serves for document modification. While document is modified,
/// modification flags are gathered. At the end of the modification, it is checked,
/// if document was really changed.
class PDFFORQTLIBSHARED_EXPORT PDFDocumentModifier
{
public:
explicit PDFDocumentModifier(const PDFDocument* originalDocument);
/// Returns builder, which can modify document
PDFDocumentBuilder* getBuilder() { return &m_builder; }
/// Finalizes document modification and prepares new changed document.
/// If document content is equal to the original, then false is returned,
/// otherwise true is returned. If document was not modified,
/// then new document is not created and function \p getDocument
/// will return nullptr.
bool finalize();
PDFDocumentPointer getDocument() const { return m_modifiedDocument; }
PDFModifiedDocument::ModificationFlags getFlags() const { return m_modificationFlags; }
void markReset() { m_modificationFlags.setFlag(PDFModifiedDocument::Reset); }
void markAnnotationsChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::Annotation); }
void markFormFieldChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::FormField); }
private:
const PDFDocument* m_originalDocument;
PDFDocumentBuilder m_builder;
PDFDocumentPointer m_modifiedDocument;
PDFModifiedDocument::ModificationFlags m_modificationFlags;
};
// Implementation
inline

View File

@ -83,6 +83,7 @@ void PDFWidget::setDocument(const PDFModifiedDocument& document)
{
m_proxy->setDocument(document);
m_pageRenderingErrors.clear();
m_drawWidget->getWidget()->update();
}
void PDFWidget::updateRenderer(RendererEngine engine, int samplesCount)

View File

@ -19,10 +19,12 @@
#include "pdfdocument.h"
#include "pdfdrawspacecontroller.h"
#include "pdfdrawwidget.h"
#include "pdfdocumentbuilder.h"
#include <QKeyEvent>
#include <QMouseEvent>
#include <QApplication>
#include <QByteArray>
namespace pdf
{
@ -173,6 +175,16 @@ void PDFFormField::apply(const std::function<void (const PDFFormField*)>& functo
}
}
void PDFFormField::modify(const std::function<void (PDFFormField*)>& functor)
{
functor(this);
for (const PDFFormFieldPointer& childField : m_childFields)
{
childField->modify(functor);
}
}
PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField)
{
PDFFormFieldPointer result;
@ -356,7 +368,16 @@ PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObje
return result;
}
PDFFormWidget::PDFFormWidget(PDFObjectReference widget, PDFFormField* parentField, PDFAnnotationAdditionalActions actions) :
bool PDFFormField::setValue(const SetValueParameters& parameters)
{
Q_UNUSED(parameters);
// Default behaviour: return false, value cannot be set
return false;
}
PDFFormWidget::PDFFormWidget(PDFObjectReference page, PDFObjectReference widget, PDFFormField* parentField, PDFAnnotationAdditionalActions actions) :
m_page(page),
m_widget(widget),
m_parentField(parentField),
m_actions(qMove(actions))
@ -366,13 +387,16 @@ PDFFormWidget::PDFFormWidget(PDFObjectReference widget, PDFFormField* parentFiel
PDFFormWidget PDFFormWidget::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField)
{
PDFObjectReference pageReference;
PDFAnnotationAdditionalActions actions;
if (const PDFDictionary* annotationDictionary = storage->getDictionaryFromObject(storage->getObjectByReference(reference)))
{
PDFDocumentDataLoaderDecorator loader(storage);
pageReference = loader.readReferenceFromDictionary(annotationDictionary, "P");
actions = PDFAnnotationAdditionalActions::parse(storage, annotationDictionary->get("AA"), annotationDictionary->get("A"));
}
return PDFFormWidget(reference, parentField, qMove(actions));
return PDFFormWidget(pageReference, reference, parentField, qMove(actions));
}
PDFFormFieldButton::ButtonType PDFFormFieldButton::getButtonType() const
@ -389,6 +413,105 @@ PDFFormFieldButton::ButtonType PDFFormFieldButton::getButtonType() const
return ButtonType::CheckBox;
}
QByteArray PDFFormFieldButton::getOnAppearanceState(const PDFFormManager* formManager, const PDFFormWidget* widget)
{
Q_ASSERT(formManager);
Q_ASSERT(widget);
const PDFDocument* document = formManager->getDocument();
Q_ASSERT(document);
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(document->getObjectByReference(widget->getWidget())))
{
PDFAppeareanceStreams streams = PDFAppeareanceStreams::parse(&document->getStorage(), dictionary->get("AP"));
QByteArrayList states = streams.getAppearanceStates(PDFAppeareanceStreams::Appearance::Normal);
for (const QByteArray& state : states)
{
if (!state.isEmpty() && state != "Off")
{
return state;
}
}
}
return QByteArray();
}
QByteArray PDFFormFieldButton::getOffAppearanceState(const PDFFormManager* formManager, const PDFFormWidget* widget)
{
Q_UNUSED(formManager);
Q_UNUSED(widget);
// 'Off' value is specified by PDF 1.7 specification. It has always value 'Off'.
// 'On' values can have different appearance states.
return "Off";
}
bool PDFFormFieldButton::setValue(const SetValueParameters& parameters)
{
// Do not allow to set value to push buttons
if (getFlags().testFlag(PushButton))
{
return false;
}
// If form field is readonly, and scope is user (form field is changed by user,
// not by calculated value), then we must not allow value change.
if (getFlags().testFlag(ReadOnly) && parameters.scope == SetValueParameters::Scope::User)
{
return false;
}
Q_ASSERT(parameters.formManager);
Q_ASSERT(parameters.modifier);
Q_ASSERT(parameters.value.isName());
PDFDocumentBuilder* builder = parameters.modifier->getBuilder();
QByteArray state = parameters.value.getString();
parameters.modifier->markFormFieldChanged();
builder->setFormFieldValue(getSelfReference(), parameters.value);
// Change widget appearance states
const bool isRadio = getFlags().testFlag(Radio);
const bool isRadioInUnison = getFlags().testFlag(RadiosInUnison);
const bool isSameValueForAllWidgets = !isRadio || isRadioInUnison;
const bool isAllowedToCheckAllOff = !getFlags().testFlag(NoToggleToOff);
bool hasWidgets = !m_widgets.empty();
bool isAnyWidgetToggledOn = false;
for (const PDFFormWidget& formWidget : getWidgets())
{
QByteArray onState = PDFFormFieldButton::getOnAppearanceState(parameters.formManager, &formWidget);
// We set appearance to 'On' if following two conditions both hold:
// 1) State equals to widget's "On" state
// 2) Either we are setting value to invoking widget, or setting of same
// value to other widgets is allowed (it is a checkbox, or radio in unison)
if (state == onState && (isSameValueForAllWidgets || formWidget.getWidget() == parameters.invokingWidget))
{
isAnyWidgetToggledOn = true;
builder->setAnnotationAppearanceState(formWidget.getWidget(), onState);
}
else
{
QByteArray offState = PDFFormFieldButton::getOffAppearanceState(parameters.formManager, &formWidget);
builder->setAnnotationAppearanceState(formWidget.getWidget(), offState);
}
parameters.modifier->markAnnotationsChanged();
}
// We must check, if correct value has been set. If form field has no widgets,
// but has same qualified name, then no check is performed (just form field value is set
// to same value in all form fields with same qualified name, according to the PDF specification.
if (hasWidgets && !isAnyWidgetToggledOn && !isAllowedToCheckAllOff)
{
return false;
}
return true;
}
PDFFormManager::PDFFormManager(PDFDrawWidgetProxy* proxy, QObject* parent) :
BaseClass(parent),
m_proxy(proxy),
@ -503,6 +626,14 @@ void PDFFormManager::apply(const std::function<void (const PDFFormField*)>& func
}
}
void PDFFormManager::modify(const std::function<void (PDFFormField*)>& functor) const
{
for (const PDFFormFieldPointer& childField : m_form.getFormFields())
{
childField->modify(functor);
}
}
void PDFFormManager::setFocusToEditor(PDFFormFieldWidgetEditor* editor)
{
if (m_focusedEditor != editor)
@ -603,6 +734,47 @@ const PDFAction* PDFFormManager::getAction(PDFAnnotationAdditionalActions::Actio
return nullptr;
}
void PDFFormManager::setFormFieldValue(PDFFormField::SetValueParameters parameters)
{
Q_ASSERT(parameters.invokingFormField);
Q_ASSERT(parameters.invokingWidget.isValid());
parameters.formManager = this;
parameters.scope = PDFFormField::SetValueParameters::Scope::User;
PDFDocumentModifier modifier(m_document);
parameters.modifier = &modifier;
if (parameters.invokingFormField->setValue(parameters))
{
// We must also set dependent fields with same name
QString qualifiedFormFieldName = parameters.invokingFormField->getName(PDFFormField::NameType::FullyQualified);
if (!qualifiedFormFieldName.isEmpty())
{
parameters.scope = PDFFormField::SetValueParameters::Scope::Internal;
auto updateDependentField = [&parameters, &qualifiedFormFieldName](PDFFormField* formField)
{
if (parameters.invokingFormField == formField)
{
// Do not update self
return;
}
if (qualifiedFormFieldName == formField->getName(PDFFormField::NameType::FullyQualified))
{
formField->setValue(parameters);
}
};
modify(updateDependentField);
}
if (modifier.finalize())
{
emit documentModified(modifier.getDocument(), modifier.getFlags());
}
}
}
void PDFFormManager::keyPressEvent(QWidget* widget, QKeyEvent* event)
{
if (m_focusedEditor)
@ -966,7 +1138,6 @@ void PDFFormFieldWidgetEditor::performKeypadNavigation(QWidget* widget, QKeyEven
const bool isLeft = key == Qt::Key_Left;
const bool isRight = key == Qt::Key_Right;
const bool isUp = key == Qt::Key_Up;
const bool isDown = key == Qt::Key_Down;
const bool isHorizontal = isLeft || isRight;
@ -1032,7 +1203,13 @@ PDFFormFieldPushButtonEditor::PDFFormFieldPushButtonEditor(PDFFormManager* formM
}
void PDFFormFieldPushButtonEditor::keyPressEvent(QWidget* widget, QKeyEvent* event)
PDFFormFieldAbstractButtonEditor::PDFFormFieldAbstractButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) :
BaseClass(formManager, formWidget, parent)
{
}
void PDFFormFieldAbstractButtonEditor::keyPressEvent(QWidget* widget, QKeyEvent* event)
{
switch (event->key())
{
@ -1058,7 +1235,7 @@ void PDFFormFieldPushButtonEditor::keyPressEvent(QWidget* widget, QKeyEvent* eve
}
}
void PDFFormFieldPushButtonEditor::keyReleaseEvent(QWidget* widget, QKeyEvent* event)
void PDFFormFieldAbstractButtonEditor::keyReleaseEvent(QWidget* widget, QKeyEvent* event)
{
Q_UNUSED(widget);
@ -1077,7 +1254,7 @@ void PDFFormFieldPushButtonEditor::keyReleaseEvent(QWidget* widget, QKeyEvent* e
}
}
void PDFFormFieldPushButtonEditor::mousePressEvent(QWidget* widget, QMouseEvent* event)
void PDFFormFieldAbstractButtonEditor::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
@ -1106,6 +1283,33 @@ PDFFormFieldCheckableButtonEditor::PDFFormFieldCheckableButtonEditor(PDFFormMana
}
void PDFFormFieldCheckableButtonEditor::click()
{
QByteArray newState;
// First, check current state of form field
PDFDocumentDataLoaderDecorator loader(m_formManager->getDocument());
QByteArray state = loader.readName(m_formWidget.getParent()->getValue());
QByteArray onState = PDFFormFieldButton::getOnAppearanceState(m_formManager, &m_formWidget);
if (state != onState)
{
newState = onState;
}
else
{
newState = PDFFormFieldButton::getOffAppearanceState(m_formManager, &m_formWidget);
}
// We have a new state, try to apply it to form field
PDFFormField::SetValueParameters parameters;
parameters.formManager = m_formManager;
parameters.invokingWidget = m_formWidget.getWidget();
parameters.invokingFormField = m_formWidget.getParent();
parameters.scope = PDFFormField::SetValueParameters::Scope::User;
parameters.value = PDFObject::createName(std::make_shared<PDFString>(qMove(newState)));
m_formManager->setFormFieldValue(parameters);
}
PDFFormFieldComboBoxEditor::PDFFormFieldComboBoxEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent) :
BaseClass(formManager, formWidget, parent)
{

View File

@ -19,6 +19,7 @@
#define PDFFORM_H
#include "pdfobject.h"
#include "pdfdocument.h"
#include "pdfannotation.h"
#include "pdfdocumentdrawinterface.h"
@ -30,6 +31,7 @@ class PDFFormField;
class PDFFormManager;
class PDFObjectStorage;
class PDFModifiedDocument;
class PDFDocumentModifier;
using PDFFormFieldPointer = QSharedPointer<PDFFormField>;
using PDFFormFields = std::vector<PDFFormFieldPointer>;
@ -40,8 +42,9 @@ class PDFFormWidget
{
public:
explicit inline PDFFormWidget() = default;
explicit inline PDFFormWidget(PDFObjectReference widget, PDFFormField* parentField, PDFAnnotationAdditionalActions actions);
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); }
@ -54,6 +57,7 @@ public:
static PDFFormWidget parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
private:
PDFObjectReference m_page;
PDFObjectReference m_widget;
PDFFormField* m_parentField;
PDFAnnotationAdditionalActions m_actions;
@ -200,6 +204,12 @@ public:
/// \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); }
@ -211,6 +221,29 @@ public:
/// \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;
};
/// 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);
protected:
PDFObjectReference m_selfReference;
FieldType m_fieldType = FieldType::Invalid;
@ -243,10 +276,26 @@ public:
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;
private:
friend static PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
/// List of names of 'On' state for radio buttons. In widget annotation's appearance
/// 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.
@ -410,8 +459,8 @@ protected:
bool m_hasFocus;
};
/// Editor for push buttons
class PDFFormFieldPushButtonEditor : public PDFFormFieldWidgetEditor
/// Editor for button-like editors
class PDFFormFieldAbstractButtonEditor : public PDFFormFieldWidgetEditor
{
Q_OBJECT
@ -419,28 +468,47 @@ private:
using BaseClass = PDFFormFieldWidgetEditor;
public:
explicit PDFFormFieldPushButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldPushButtonEditor() = default;
explicit PDFFormFieldAbstractButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldAbstractButtonEditor() = default;
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
private:
void click();
protected:
virtual void click() = 0;
};
/// Editor for check boxes or radio buttons
class PDFFormFieldCheckableButtonEditor : public PDFFormFieldWidgetEditor
/// Editor for push buttons
class PDFFormFieldPushButtonEditor : public PDFFormFieldAbstractButtonEditor
{
Q_OBJECT
private:
using BaseClass = PDFFormFieldWidgetEditor;
using BaseClass = PDFFormFieldAbstractButtonEditor;
public:
explicit PDFFormFieldPushButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldPushButtonEditor() = default;
protected:
virtual void click() override;
};
/// Editor for check boxes or radio buttons
class PDFFormFieldCheckableButtonEditor : public PDFFormFieldAbstractButtonEditor
{
Q_OBJECT
private:
using BaseClass = PDFFormFieldAbstractButtonEditor;
public:
explicit PDFFormFieldCheckableButtonEditor(PDFFormManager* formManager, PDFFormWidget formWidget, QObject* parent);
virtual ~PDFFormFieldCheckableButtonEditor() = default;
protected:
virtual void click() override;
};
/// Editor for text fields
@ -542,6 +610,12 @@ public:
/// \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
@ -586,6 +660,9 @@ public:
bool isValid() const { return editor != nullptr; }
};
/// Tries to set value to the form field
void setFormFieldValue(PDFFormField::SetValueParameters parameters);
// interface IDrawWidgetInputInterface
/// Handles key press event from widget
@ -628,6 +705,7 @@ public:
signals:
void actionTriggered(const PDFAction* action);
void documentModified(PDFDocumentPointer document, PDFModifiedDocument::ModificationFlags flags);
private:
void updateFormWidgetEditors();

View File

@ -277,6 +277,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
m_annotationManager->setFormManager(m_formManager);
m_pdfWidget->setFormManager(m_formManager);
connect(m_formManager, &pdf::PDFFormManager::actionTriggered, this, &PDFViewerMainWindow::onActionTriggered);
connect(m_formManager, &pdf::PDFFormManager::documentModified, this, &PDFViewerMainWindow::onDocumentModified);
connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::drawSpaceChanged, this, &PDFViewerMainWindow::onDrawSpaceChanged);
connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::pageLayoutChanged, this, &PDFViewerMainWindow::onPageLayoutChanged);
@ -982,6 +983,13 @@ void PDFViewerMainWindow::onDocumentReadingFinished()
updateActionsAvailability();
}
void PDFViewerMainWindow::onDocumentModified(pdf::PDFDocumentPointer document, pdf::PDFModifiedDocument::ModificationFlags flags)
{
m_pdfDocument = document;
pdf::PDFModifiedDocument modifiedDocument(m_pdfDocument.data(), m_optionalContentActivity, flags);
setDocument(modifiedDocument);
}
void PDFViewerMainWindow::setDocument(pdf::PDFModifiedDocument document)
{
if (document.hasReset())

View File

@ -93,13 +93,9 @@ private slots:
void on_actionFitHeight_triggered();
void on_actionProperties_triggered();
void on_actionSend_by_E_Mail_triggered();
void on_actionRotateRight_triggered();
void on_actionRotateLeft_triggered();
void on_actionPrint_triggered();
void on_actionRender_to_Images_triggered();
private:
@ -118,6 +114,7 @@ private:
void onProgressFinished();
void onDocumentReadingFinished();
void onDocumentModified(pdf::PDFDocumentPointer document, pdf::PDFModifiedDocument::ModificationFlags flags);
void readSettings();
void readActionSettings();

View File

@ -5763,6 +5763,70 @@ return annotationObject;</property>
<property name="functionDescription">This function is used to create a new trailer dictionary, when blank document is created. Do not call this function manually.</property>
<property name="returnType">_PDFObject</property>
</QObject>
<QObject class="codegen::GeneratedFunction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedParameter">
<property name="objectName"></property>
<property name="items"/>
<property name="parameterName">annotation</property>
<property name="parameterType">_PDFObjectReference</property>
<property name="parameterDescription">Annotation</property>
</QObject>
<QObject class="codegen::GeneratedParameter">
<property name="objectName"></property>
<property name="items"/>
<property name="parameterName">appearanceState</property>
<property name="parameterType">_QByteArray</property>
<property name="parameterDescription">Appearance state</property>
</QObject>
</property>
<property name="actionType">Parameters</property>
<property name="variableName"></property>
<property name="variableType">_void</property>
<property name="code"></property>
</QObject>
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">AS</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">WrapName(appearanceState)</property>
</QObject>
</property>
<property name="dictionaryItemName"></property>
<property name="objectType">Dictionary</property>
<property name="value"></property>
</QObject>
</property>
<property name="actionType">CreateObject</property>
<property name="variableName">annotationObject</property>
<property name="variableType">_PDFObject</property>
<property name="code"></property>
</QObject>
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items"/>
<property name="actionType">Code</property>
<property name="variableName"></property>
<property name="variableType">_void</property>
<property name="code">mergeTo(annotation, annotationObject);</property>
</QObject>
</property>
<property name="functionType">Annotations</property>
<property name="functionName">setAnnotationAppearanceState</property>
<property name="functionDescription">Sets annotation appearance state.</property>
<property name="returnType">_void</property>
</QObject>
<QObject class="codegen::GeneratedFunction">
<property name="objectName"></property>
<property name="items">
@ -6854,6 +6918,70 @@ return annotationObject;</property>
<property name="functionDescription">Set document title.</property>
<property name="returnType">_void</property>
</QObject>
<QObject class="codegen::GeneratedFunction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedParameter">
<property name="objectName"></property>
<property name="items"/>
<property name="parameterName">formField</property>
<property name="parameterType">_PDFObjectReference</property>
<property name="parameterDescription">Form field</property>
</QObject>
<QObject class="codegen::GeneratedParameter">
<property name="objectName"></property>
<property name="items"/>
<property name="parameterName">value</property>
<property name="parameterType">_PDFObject</property>
<property name="parameterDescription">Value</property>
</QObject>
</property>
<property name="actionType">Parameters</property>
<property name="variableName"></property>
<property name="variableType">_void</property>
<property name="code"></property>
</QObject>
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items">
<QObject class="codegen::GeneratedPDFObject">
<property name="objectName"></property>
<property name="items"/>
<property name="dictionaryItemName">V</property>
<property name="objectType">DictionaryItemSimple</property>
<property name="value">value</property>
</QObject>
</property>
<property name="dictionaryItemName"></property>
<property name="objectType">Dictionary</property>
<property name="value"></property>
</QObject>
</property>
<property name="actionType">CreateObject</property>
<property name="variableName">formFieldObject</property>
<property name="variableType">_PDFObject</property>
<property name="code"></property>
</QObject>
<QObject class="codegen::GeneratedAction">
<property name="objectName"></property>
<property name="items"/>
<property name="actionType">Code</property>
<property name="variableName"></property>
<property name="variableType">_void</property>
<property name="code">mergeTo(formField, formFieldObject);</property>
</QObject>
</property>
<property name="functionType">Forms</property>
<property name="functionName">setFormFieldValue</property>
<property name="functionDescription">Sets form field value. Value must be correct for this form field, no checking is performed. Also, if you use this function, annotation widgets, which are attached to this form field, should also be updated (for example, appearance state and sometimes appearance streams).</property>
<property name="returnType">_void</property>
</QObject>
<QObject class="codegen::GeneratedFunction">
<property name="objectName"></property>
<property name="items">