Form field highlighting

This commit is contained in:
Jakub Melka
2020-04-23 19:25:57 +02:00
parent d16e2a2c02
commit 76af397b07
6 changed files with 164 additions and 29 deletions

View File

@ -105,6 +105,7 @@ HEADERS += \
sources/pdfoptionalcontent.h \ sources/pdfoptionalcontent.h \
sources/pdfoutline.h \ sources/pdfoutline.h \
sources/pdfpagetransition.h \ sources/pdfpagetransition.h \
sources/pdfpainterutils.h \
sources/pdfparser.h \ sources/pdfparser.h \
sources/pdfglobal.h \ sources/pdfglobal.h \
sources/pdfconstants.h \ sources/pdfconstants.h \

View File

@ -26,6 +26,7 @@
#include "pdfparser.h" #include "pdfparser.h"
#include "pdfdrawwidget.h" #include "pdfdrawwidget.h"
#include "pdfform.h" #include "pdfform.h"
#include "pdfpainterutils.h"
#include <QDialog> #include <QDialog>
#include <QApplication> #include <QApplication>
@ -974,6 +975,48 @@ QMatrix PDFAnnotationManager::prepareTransformations(const QMatrix& pagePointToD
return userSpaceToDeviceSpace; return userSpaceToDeviceSpace;
} }
void PDFAnnotationManager::drawWidgetAnnotationHighlight(QRectF annotationRectangle,
const PDFAnnotation* annotation,
QPainter* painter,
QMatrix userSpaceToDeviceSpace) const
{
const bool isWidget = annotation->getType() == AnnotationType::Widget;
if (m_formManager && isWidget)
{
// Is it a form field?
const PDFFormManager::FormAppearanceFlags flags = m_formManager->getAppearanceFlags();
if (flags.testFlag(PDFFormManager::HighlightFields) || flags.testFlag(PDFFormManager::HighlightRequiredFields))
{
const PDFFormField* formField = m_formManager->getFormFieldForWidget(annotation->getSelfReference());
if (!formField)
{
return;
}
QColor color;
if (flags.testFlag(PDFFormManager::HighlightFields))
{
color = Qt::blue;
}
if (flags.testFlag(PDFFormManager::HighlightRequiredFields) && formField->getFlags().testFlag(PDFFormField::Required))
{
color = Qt::red;
}
if (color.isValid())
{
color.setAlphaF(0.2);
// Draw annotation rectangle by highlight color
QPainterPath highlightArea;
highlightArea.addRect(annotationRectangle);
highlightArea = userSpaceToDeviceSpace.map(highlightArea);
painter->fillPath(highlightArea, color);
}
}
}
}
void PDFAnnotationManager::drawPage(QPainter* painter, void PDFAnnotationManager::drawPage(QPainter* painter,
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
@ -1021,15 +1064,40 @@ void PDFAnnotationManager::drawPage(QPainter* painter,
if (!appearanceStreamObject.isStream()) if (!appearanceStreamObject.isStream())
{ {
// Object is not valid appearance stream. We will try to draw default // Object is not valid appearance stream. We will try to draw default
// annotation appearance. // annotation appearance, but we must consider also optional content.
painter->save(); // We do not draw annotation, if it is not ignored and annotation
painter->setRenderHint(QPainter::Antialiasing, true); // has reference to optional content.
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
AnnotationDrawParameters parameters; if (!m_features.testFlag(PDFRenderer::IgnoreOptionalContent) &&
parameters.painter = painter; annotation.annotation->getOptionalContent().isValid())
parameters.key = std::make_pair(annotation.appearance, annotation.annotation->getAppearanceState()); {
annotation.annotation->draw(parameters); continue;
painter->restore(); }
QRectF annotationRectangle = annotation.annotation->getRectangle();
{
PDFPainterStateGuard guard(painter);
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
AnnotationDrawParameters parameters;
parameters.painter = painter;
parameters.key = std::make_pair(annotation.appearance, annotation.annotation->getAppearanceState());
annotation.annotation->draw(parameters);
if (parameters.boundingRectangle.isValid())
{
annotationRectangle = parameters.boundingRectangle;
}
}
// Draw highlighting of fields, but only, if target is View,
// we do not want to render form field highlight, when we are
// printing to the printer.
if (m_target == Target::View)
{
PDFPainterStateGuard guard(painter);
drawWidgetAnnotationHighlight(annotationRectangle, annotation.annotation.get(), painter, pagePointToDevicePointMatrix);
}
continue; continue;
} }
@ -1075,31 +1143,31 @@ void PDFAnnotationManager::drawPage(QPainter* painter,
// Step 3) - compute final matrix AA // Step 3) - compute final matrix AA
QMatrix AA = formMatrix * A; QMatrix AA = formMatrix * A;
PDFPainter pdfPainter(painter, features, userSpaceToDeviceSpace, page, m_document, m_fontCache, cms.get(), m_optionalActivity, m_meshQualitySettings); bool isContentVisible = false;
pdfPainter.initializeProcessor();
// Jakub Melka: we must check, that we do not display annotation disabled by optional content // Draw annotation
PDFObjectReference oc = annotation.annotation->getOptionalContent();
if (!oc.isValid() || !pdfPainter.isContentSuppressedByOC(oc))
{ {
pdfPainter.processForm(AA, formBoundingBox, resources, transparencyGroup, content); PDFPainterStateGuard guard(painter);
PDFPainter pdfPainter(painter, features, userSpaceToDeviceSpace, page, m_document, m_fontCache, cms.get(), m_optionalActivity, m_meshQualitySettings);
pdfPainter.initializeProcessor();
// Is it a form field? // Jakub Melka: we must check, that we do not display annotation disabled by optional content
if (m_formManager && annotation.annotation->getType() == AnnotationType::Widget) PDFObjectReference oc = annotation.annotation->getOptionalContent();
isContentVisible = !oc.isValid() || !pdfPainter.isContentSuppressedByOC(oc);
if (isContentVisible)
{ {
const PDFFormManager::FormAppearanceFlags flags = m_formManager->getAppearanceFlags(); pdfPainter.processForm(AA, formBoundingBox, resources, transparencyGroup, content);
if (flags.testFlag(PDFFormManager::HighlightFields) || flags.testFlag(PDFFormManager::HighlightRequiredFields))
{
const PDFFormField* formField = m_formManager->getFormFieldForWidget(annotation.annotation->getSelfReference());
if (!formField)
{
continue;
}
s
}
} }
} }
// Draw highlighting of fields, but only, if target is View,
// we do not want to render form field highlight, when we are
// printing to the printer.
if (isContentVisible && m_target == Target::View)
{
drawWidgetAnnotationHighlight(annotationRectangle, annotation.annotation.get(), painter, userSpaceToDeviceSpace);
}
} }
catch (PDFException exception) catch (PDFException exception)
{ {

View File

@ -1261,6 +1261,8 @@ class PDFFORQTLIBSHARED_EXPORT PDFAnnotationManager : public QObject, public IDo
private: private:
using BaseClass = QObject; using BaseClass = QObject;
void drawWidgetAnnotationHighlight(QRectF annotationRectangle, PDFObjectReference annotationReference, QPainter* painter, QMatrix userSpaceToDeviceSpace) const;
public: public:
enum class Target enum class Target
@ -1367,6 +1369,11 @@ protected:
/// Returns true, if any page in the given indices has annotation /// Returns true, if any page in the given indices has annotation
bool hasAnyPageAnnotation(const std::vector<PDFInteger>& pageIndices) const; bool hasAnyPageAnnotation(const std::vector<PDFInteger>& pageIndices) const;
void drawWidgetAnnotationHighlight(QRectF annotationRectangle,
const PDFAnnotation* annotation,
QPainter* painter,
QMatrix userSpaceToDeviceSpace) const;
const PDFDocument* m_document; const PDFDocument* m_document;
PDFFontCache* m_fontCache; PDFFontCache* m_fontCache;

View File

@ -282,6 +282,17 @@ PDFFormManager::~PDFFormManager()
} }
const PDFFormField* PDFFormManager::getFormFieldForWidget(PDFObjectReference widget) const
{
auto it = m_widgetToFormField.find(widget);
if (it != m_widgetToFormField.cend())
{
return it->second;
}
return nullptr;
}
PDFAnnotationManager* PDFFormManager::getAnnotationManager() const PDFAnnotationManager* PDFFormManager::getAnnotationManager() const
{ {
return m_annotationManager; return m_annotationManager;
@ -331,7 +342,7 @@ void PDFFormManager::updateWidgetToFormFieldMapping()
{ {
m_widgetToFormField.clear(); m_widgetToFormField.clear();
if (hasAcroForm()) if (hasAcroForm() || hasXFAForm())
{ {
for (const PDFFormFieldPointer& formFieldPtr : m_form.getFormFields()) for (const PDFFormFieldPointer& formFieldPtr : m_form.getFormFields())
{ {

View File

@ -367,6 +367,7 @@ public:
Q_DECLARE_FLAGS(FormAppearanceFlags, FormAppearanceFlag) Q_DECLARE_FLAGS(FormAppearanceFlags, FormAppearanceFlag)
bool hasAcroForm() const { return m_form.getFormType() == PDFForm::FormType::AcroForm; } bool hasAcroForm() const { return m_form.getFormType() == PDFForm::FormType::AcroForm; }
bool hasXFAForm() const { return m_form.getFormType() == PDFForm::FormType::XFAForm; }
/// Returns form field for widget. If widget doesn't have attached form field, /// Returns form field for widget. If widget doesn't have attached form field,
/// then nullptr is returned. /// then nullptr is returned.

View File

@ -0,0 +1,47 @@
// Copyright (C) 2020 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt 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
// (at your option) any later version.
//
// PdfForQt 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 PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFPAINTERUTILS_H
#define PDFPAINTERUTILS_H
#include <QPainter>
namespace pdf
{
/// RAII wrapper for painter save/restore
class PDFPainterStateGuard
{
public:
explicit inline PDFPainterStateGuard(QPainter* painter) :
m_painter(painter)
{
m_painter->save();
}
inline ~PDFPainterStateGuard()
{
m_painter->restore();
}
private:
QPainter* m_painter;
};
} // namespace pdf
#endif // PDFPAINTERUTILS_H