mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Form field highlighting
This commit is contained in:
@ -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 \
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -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())
|
||||||
{
|
{
|
||||||
|
@ -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.
|
||||||
|
47
PdfForQtLib/sources/pdfpainterutils.h
Normal file
47
PdfForQtLib/sources/pdfpainterutils.h
Normal 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
|
Reference in New Issue
Block a user