mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Checkbox and radio form fields
This commit is contained in:
@@ -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)
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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 &&
|
||||
|
@@ -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"
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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 = [¶meters, &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)
|
||||
{
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user