diff --git a/PdfForQtLib/sources/pdfannotation.cpp b/PdfForQtLib/sources/pdfannotation.cpp index d874f6d..9bedc64 100644 --- a/PdfForQtLib/sources/pdfannotation.cpp +++ b/PdfForQtLib/sources/pdfannotation.cpp @@ -36,6 +36,7 @@ #include #include #include +#include namespace pdf { @@ -165,7 +166,7 @@ PDFObject PDFAppeareanceStreams::getAppearance(Appearance appearance, const QByt return PDFObject(); } -QByteArrayList PDFAppeareanceStreams::getAppearanceStates(Appearance appearance) +QByteArrayList PDFAppeareanceStreams::getAppearanceStates(Appearance appearance) const { QByteArrayList result; @@ -182,6 +183,13 @@ QByteArrayList PDFAppeareanceStreams::getAppearanceStates(Appearance appearance) return result; } +std::vector PDFAppeareanceStreams::getAppearanceKeys() const +{ + std::vector result; + std::transform(m_appearanceStreams.cbegin(), m_appearanceStreams.cend(), std::back_inserter(result), [](const auto& item) { return item.first; }); + return result; +} + PDFAnnotation::PDFAnnotation() : m_flags(), m_structParent(0) @@ -194,8 +202,10 @@ void PDFAnnotation::draw(AnnotationDrawParameters& parameters) const Q_UNUSED(parameters); } -std::vector PDFAnnotation::getDrawKeys() const +std::vector PDFAnnotation::getDrawKeys(const PDFFormManager* formManager) const { + Q_UNUSED(formManager); + return { PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Normal, QByteArray() } }; } @@ -1749,8 +1759,10 @@ QColor PDFMarkupAnnotation::getFillColor() const return color; } -std::vector PDFTextAnnotation::getDrawKeys() const +std::vector PDFTextAnnotation::getDrawKeys(const PDFFormManager* formManager) const { + Q_UNUSED(formManager); + return { PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Normal, QByteArray() }, PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Rollover, QByteArray() }, PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Down, QByteArray() } }; @@ -2328,8 +2340,10 @@ void PDFHighlightAnnotation::draw(AnnotationDrawParameters& parameters) const parameters.boundingRectangle.adjust(-penWidth, -penWidth, penWidth, penWidth); } -std::vector PDFLinkAnnotation::getDrawKeys() const +std::vector PDFLinkAnnotation::getDrawKeys(const PDFFormManager* formManager) const { + Q_UNUSED(formManager); + return { PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Down, QByteArray() } }; } @@ -2899,6 +2913,8 @@ void PDFWidgetAnnotation::draw(AnnotationDrawParameters& parameters) const return; } + PDFPainterStateGuard guard(parameters.painter); + const PDFFormFieldWidgetEditor* editor = parameters.formManager->getEditor(formField); if (editor && editor->isEditorDrawEnabled()) { @@ -2921,8 +2937,112 @@ void PDFWidgetAnnotation::draw(AnnotationDrawParameters& parameters) const } case PDFFormField::FieldType::Button: - case PDFFormField::FieldType::Invalid: + { + const PDFFormFieldButton* button = dynamic_cast(formField); + switch (button->getButtonType()) + { + case PDFFormFieldButton::ButtonType::PushButton: + { + QRectF rectangle = getRectangle(); + + QByteArray defaultAppearance = parameters.formManager->getForm()->getDefaultAppearance().value_or(QByteArray()); + PDFAnnotationDefaultAppearance appearance = PDFAnnotationDefaultAppearance::parse(defaultAppearance); + + qreal fontSize = appearance.getFontSize(); + if (qFuzzyIsNull(fontSize)) + { + fontSize = rectangle.height() * 0.6; + } + + QFont font(appearance.getFontName()); + font.setHintingPreference(QFont::PreferNoHinting); + font.setPixelSize(qCeil(fontSize)); + font.setStyleStrategy(QFont::ForceOutline); + + QFontMetrics fontMetrics(font); + + QPainter* painter = parameters.painter; + painter->translate(rectangle.bottomLeft()); + painter->scale(1.0, -1.0); + painter->setFont(font); + + QStyleOptionButton option; + option.state = QStyle::State_Enabled; + option.rect = QRect(0, 0, qFloor(rectangle.width()), qFloor(rectangle.height())); + option.palette = QApplication::palette(); + + if (parameters.key.first == PDFAppeareanceStreams::Appearance::Rollover) + { + option.state |= QStyle::State_MouseOver; + } + + if (parameters.key.first == PDFAppeareanceStreams::Appearance::Down) + { + option.state |= QStyle::State_Sunken; + } + + option.features = QStyleOptionButton::None; + option.text = getContents(); + option.fontMetrics = fontMetrics; + + QApplication::style()->drawControl(QStyle::CE_PushButton, &option, painter, nullptr); + break; + } + + case PDFFormFieldButton::ButtonType::RadioButton: + case PDFFormFieldButton::ButtonType::CheckBox: + { + QRectF rectangle = getRectangle(); + QPainter* painter = parameters.painter; + painter->translate(rectangle.bottomLeft()); + painter->scale(1.0, -1.0); + + QStyleOptionButton option; + option.state = QStyle::State_Enabled; + option.rect = QRect(0, 0, qFloor(rectangle.width()), qFloor(rectangle.height())); + option.palette = QApplication::palette(); + + if (parameters.key.first == PDFAppeareanceStreams::Appearance::Rollover) + { + option.state |= QStyle::State_MouseOver; + } + + if (parameters.key.first == PDFAppeareanceStreams::Appearance::Down) + { + option.state |= QStyle::State_Sunken; + } + + if (parameters.key.second != "Off") + { + option.state |= QStyle::State_On; + } + else + { + option.state |= QStyle::State_Off; + } + + option.features = QStyleOptionButton::None; + option.text = QString(); + + QStyle::PrimitiveElement element = (button->getButtonType() == PDFFormFieldButton::ButtonType::CheckBox) ? QStyle::PE_IndicatorCheckBox : QStyle::PE_IndicatorRadioButton; + QApplication::style()->drawPrimitive(element, &option, painter, nullptr); + break; + } + + default: + { + Q_ASSERT(false); + break; + } + } + + break; + } + case PDFFormField::FieldType::Choice: + // TODO: Draw choice field + + case PDFFormField::FieldType::Invalid: case PDFFormField::FieldType::Signature: break; @@ -2935,4 +3055,82 @@ void PDFWidgetAnnotation::draw(AnnotationDrawParameters& parameters) const } } +std::vector PDFWidgetAnnotation::getDrawKeys(const PDFFormManager* formManager) const +{ + if (!formManager) + { + return PDFAnnotation::getDrawKeys(formManager); + } + + std::vector result; + + // Try get the form field, if we find it, then determine from form field type + // the list of appearance states. + const PDFFormField* formField = formManager->getFormFieldForWidget(getSelfReference()); + if (!formField) + { + return PDFAnnotation::getDrawKeys(formManager); + } + + switch (formField->getFieldType()) + { + case PDFFormField::FieldType::Invalid: + break; + + case PDFFormField::FieldType::Button: + { + const PDFFormFieldButton* button = dynamic_cast(formField); + switch (button->getButtonType()) + { + case PDFFormFieldButton::ButtonType::PushButton: + { + result = { PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Normal, QByteArray() }, + PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Rollover, QByteArray() }, + PDFAppeareanceStreams::Key{ PDFAppeareanceStreams::Appearance::Down, QByteArray() } }; + break; + } + + case PDFFormFieldButton::ButtonType::RadioButton: + case PDFFormFieldButton::ButtonType::CheckBox: + { + result = getAppearanceStreams().getAppearanceKeys(); + PDFAppeareanceStreams::Key offKey{ PDFAppeareanceStreams::Appearance::Normal, QByteArray() }; + if (std::find(result.cbegin(), result.cend(), offKey) == result.cend()) + { + result.push_back(qMove(offKey)); + } + break; + } + + default: + { + Q_ASSERT(false); + break; + } + } + + break; + } + + case PDFFormField::FieldType::Text: + // Text has only default appearance + break; + + case PDFFormField::FieldType::Choice: + // TODO: Implement choice appearance + break; + + case PDFFormField::FieldType::Signature: + // Signatures have always default appearance + break; + } + + if (result.empty()) + { + result = PDFAnnotation::getDrawKeys(formManager); + } + + return result; +} + } // namespace pdf diff --git a/PdfForQtLib/sources/pdfannotation.h b/PdfForQtLib/sources/pdfannotation.h index 316436f..eb2de49 100644 --- a/PdfForQtLib/sources/pdfannotation.h +++ b/PdfForQtLib/sources/pdfannotation.h @@ -212,7 +212,10 @@ public: /// Returns list of appearance states for given appearance /// \param appearance Appearance - QByteArrayList getAppearanceStates(Appearance appearance); + QByteArrayList getAppearanceStates(Appearance appearance) const; + + /// Returns list of appearance keys + std::vector getAppearanceKeys() const; private: std::map m_appearanceStreams; @@ -504,7 +507,7 @@ public: virtual void draw(AnnotationDrawParameters& parameters) const; /// Returns a list of appearance states, which must be created for this annotation - virtual std::vector getDrawKeys() const; + virtual std::vector getDrawKeys(const PDFFormManager* formManager) const; /// Returns effective flags (some annotations can behave as they have always /// set some flags, such as NoZoom and NoRotate) @@ -695,7 +698,7 @@ public: inline explicit PDFTextAnnotation() = default; virtual AnnotationType getType() const override { return AnnotationType::Text; } - virtual std::vector getDrawKeys() const override; + virtual std::vector getDrawKeys(const PDFFormManager* formManager) const override; virtual void draw(AnnotationDrawParameters& parameters) const override; virtual Flags getEffectiveFlags() const override; @@ -729,7 +732,7 @@ public: inline explicit PDFLinkAnnotation() = default; virtual AnnotationType getType() const override { return AnnotationType::Link; } - virtual std::vector getDrawKeys() const; + virtual std::vector getDrawKeys(const PDFFormManager* formManager) const override; virtual void draw(AnnotationDrawParameters& parameters) const override; const PDFAction* getAction() const { return m_action.data(); } @@ -1168,6 +1171,7 @@ public: virtual AnnotationType getType() const override { return AnnotationType::Widget; } virtual void draw(AnnotationDrawParameters& parameters) const override; + virtual std::vector getDrawKeys(const PDFFormManager* formManager) const override; HighlightMode getHighlightMode() const { return m_highlightMode; } const PDFAnnotationAppearanceCharacteristics& getAppearanceCharacteristics() const { return m_appearanceCharacteristics; } diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.cpp b/PdfForQtLib/sources/pdfdocumentbuilder.cpp index 6728012..5180663 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.cpp +++ b/PdfForQtLib/sources/pdfdocumentbuilder.cpp @@ -755,7 +755,7 @@ void PDFDocumentBuilder::updateAnnotationAppearanceStreams(PDFObjectReference an return; } - std::vector keys = annotation->getDrawKeys(); + std::vector keys = annotation->getDrawKeys(m_formManager); std::map appearanceStreams; QRectF boundingRectangle; @@ -1080,6 +1080,16 @@ std::vector PDFDocumentBuilder::copyFrom(const std::vector return result; } +const PDFFormManager* PDFDocumentBuilder::getFormManager() const +{ + return m_formManager; +} + +void PDFDocumentBuilder::setFormManager(const PDFFormManager* formManager) +{ + m_formManager = formManager; +} + PDFContentStreamBuilder::PDFContentStreamBuilder(QSizeF size, CoordinateSystem coordinateSystem) : m_size(size), m_coordinateSystem(coordinateSystem), diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.h b/PdfForQtLib/sources/pdfdocumentbuilder.h index aedee6e..9f540d6 100644 --- a/PdfForQtLib/sources/pdfdocumentbuilder.h +++ b/PdfForQtLib/sources/pdfdocumentbuilder.h @@ -279,6 +279,9 @@ public: /// \param annotationReference Reference to the annotation void updateAnnotationAppearanceStreams(PDFObjectReference annotationReference); + const PDFFormManager* getFormManager() const; + void setFormManager(const PDFFormManager* formManager); + /* START GENERATED CODE */ /// Appends a new page after last page. @@ -973,6 +976,7 @@ private: PDFObjectStorage m_storage; PDFVersion m_version; + const PDFFormManager* m_formManager = nullptr; }; /// This class serves for document modification. While document is modified, diff --git a/PdfForQtLib/sources/pdfform.cpp b/PdfForQtLib/sources/pdfform.cpp index 9d4687a..dc4d687 100644 --- a/PdfForQtLib/sources/pdfform.cpp +++ b/PdfForQtLib/sources/pdfform.cpp @@ -780,6 +780,7 @@ void PDFFormManager::setFormFieldValue(PDFFormField::SetValueParameters paramete parameters.scope = PDFFormField::SetValueParameters::Scope::User; PDFDocumentModifier modifier(m_document); + modifier.getBuilder()->setFormManager(this); parameters.modifier = &modifier; if (parameters.invokingFormField->setValue(parameters)) @@ -2102,6 +2103,23 @@ QMatrix PDFTextEditPseudowidget::createTextBoxTransformMatrix(bool edit) const } } + if (!isMultiline() && !isComb()) + { + // If text is single line, then adjust text position to the vertical center + QTextLine textLine = m_textLayout.lineAt(0); + if (textLine.isValid()) + { + const qreal lineSpacing = textLine.leadingIncluded() ? textLine.height() : textLine.leading() + textLine.height(); + const qreal textBoxHeight = m_widgetRect.height(); + + if (lineSpacing < textBoxHeight) + { + const qreal delta = (textBoxHeight - lineSpacing) * 0.5; + matrix.translate(0.0, delta); + } + } + } + return matrix; } @@ -2228,7 +2246,7 @@ void PDFTextEditPseudowidget::draw(AnnotationDrawParameters& parameters, bool ed m_textLayout.draw(painter, QPointF(0.0, 0.0), selections, QRectF()); // If we are editing, also draw text - if (edit) + if (edit && !isReadonly()) { m_textLayout.drawCursor(painter, QPointF(0.0, 0.0), m_positionCursor); }