Widget painting

This commit is contained in:
Jakub Melka
2020-05-10 19:23:18 +02:00
parent 713a7079f8
commit ce585262c2
5 changed files with 245 additions and 11 deletions

View File

@ -36,6 +36,7 @@
#include <QTextEdit>
#include <QVBoxLayout>
#include <QLabel>
#include <QStyleOptionButton>
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::Key> PDFAppeareanceStreams::getAppearanceKeys() const
{
std::vector<Key> 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<PDFAppeareanceStreams::Key> PDFAnnotation::getDrawKeys() const
std::vector<PDFAppeareanceStreams::Key> 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<PDFAppeareanceStreams::Key> PDFTextAnnotation::getDrawKeys() const
std::vector<PDFAppeareanceStreams::Key> 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<PDFAppeareanceStreams::Key> PDFLinkAnnotation::getDrawKeys() const
std::vector<PDFAppeareanceStreams::Key> 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<const PDFFormFieldButton*>(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<pdf::PDFAppeareanceStreams::Key> PDFWidgetAnnotation::getDrawKeys(const PDFFormManager* formManager) const
{
if (!formManager)
{
return PDFAnnotation::getDrawKeys(formManager);
}
std::vector<pdf::PDFAppeareanceStreams::Key> 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<const PDFFormFieldButton*>(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

View File

@ -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<Key> getAppearanceKeys() const;
private:
std::map<Key, PDFObject> 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<PDFAppeareanceStreams::Key> getDrawKeys() const;
virtual std::vector<PDFAppeareanceStreams::Key> 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<PDFAppeareanceStreams::Key> getDrawKeys() const override;
virtual std::vector<PDFAppeareanceStreams::Key> 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<PDFAppeareanceStreams::Key> getDrawKeys() const;
virtual std::vector<PDFAppeareanceStreams::Key> 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<PDFAppeareanceStreams::Key> getDrawKeys(const PDFFormManager* formManager) const override;
HighlightMode getHighlightMode() const { return m_highlightMode; }
const PDFAnnotationAppearanceCharacteristics& getAppearanceCharacteristics() const { return m_appearanceCharacteristics; }

View File

@ -755,7 +755,7 @@ void PDFDocumentBuilder::updateAnnotationAppearanceStreams(PDFObjectReference an
return;
}
std::vector<PDFAppeareanceStreams::Key> keys = annotation->getDrawKeys();
std::vector<PDFAppeareanceStreams::Key> keys = annotation->getDrawKeys(m_formManager);
std::map<PDFAppeareanceStreams::Key, PDFObjectReference> appearanceStreams;
QRectF boundingRectangle;
@ -1080,6 +1080,16 @@ std::vector<PDFObject> PDFDocumentBuilder::copyFrom(const std::vector<PDFObject>
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),

View File

@ -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,

View File

@ -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);
}