AcroForms implementation (data structures start)

This commit is contained in:
Jakub Melka
2020-04-18 19:01:49 +02:00
parent 9b71031ec3
commit 247c2c98f7
15 changed files with 724 additions and 105 deletions

View File

@ -46,6 +46,7 @@ SOURCES += \
sources/pdfdocumentwriter.cpp \ sources/pdfdocumentwriter.cpp \
sources/pdfexecutionpolicy.cpp \ sources/pdfexecutionpolicy.cpp \
sources/pdffile.cpp \ sources/pdffile.cpp \
sources/pdfform.cpp \
sources/pdfitemmodels.cpp \ sources/pdfitemmodels.cpp \
sources/pdfjbig2decoder.cpp \ sources/pdfjbig2decoder.cpp \
sources/pdfmultimedia.cpp \ sources/pdfmultimedia.cpp \
@ -94,6 +95,7 @@ HEADERS += \
sources/pdfdocumentwriter.h \ sources/pdfdocumentwriter.h \
sources/pdfexecutionpolicy.h \ sources/pdfexecutionpolicy.h \
sources/pdffile.h \ sources/pdffile.h \
sources/pdfform.h \
sources/pdfitemmodels.h \ sources/pdfitemmodels.h \
sources/pdfjbig2decoder.h \ sources/pdfjbig2decoder.h \
sources/pdfmeshqualitysettings.h \ sources/pdfmeshqualitysettings.h \

View File

@ -976,7 +976,8 @@ void PDFAnnotationManager::drawPage(QPainter* painter,
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{ {
Q_UNUSED(compiledPage); Q_UNUSED(compiledPage);
Q_UNUSED(layoutGetter); Q_UNUSED(layoutGetter);
@ -1003,82 +1004,95 @@ void PDFAnnotationManager::drawPage(QPainter* painter,
for (const PageAnnotation& annotation : annotations.annotations) for (const PageAnnotation& annotation : annotations.annotations)
{ {
const PDFAnnotation::Flags annotationFlags = annotation.annotation->getEffectiveFlags(); try
if (annotationFlags.testFlag(PDFAnnotation::Hidden) || // Annotation is completely hidden
(m_target == Target::Print && !annotationFlags.testFlag(PDFAnnotation::Print)) || // Target is print and annotation is marked as not printed
(m_target == Target::View && annotationFlags.testFlag(PDFAnnotation::NoView)) || // Target is view, and annotation is disabled for screen
annotation.annotation->isReplyTo()) // Annotation is reply to another annotation, display just the first annotation
{ {
const PDFAnnotation::Flags annotationFlags = annotation.annotation->getEffectiveFlags();
if (annotationFlags.testFlag(PDFAnnotation::Hidden) || // Annotation is completely hidden
(m_target == Target::Print && !annotationFlags.testFlag(PDFAnnotation::Print)) || // Target is print and annotation is marked as not printed
(m_target == Target::View && annotationFlags.testFlag(PDFAnnotation::NoView)) || // Target is view, and annotation is disabled for screen
annotation.annotation->isReplyTo()) // Annotation is reply to another annotation, display just the first annotation
{
continue;
}
PDFObject appearanceStreamObject = m_document->getObject(getAppearanceStream(annotation));
if (!appearanceStreamObject.isStream())
{
// Object is not valid appearance stream. We will try to draw default
// annotation appearance.
painter->save();
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);
painter->restore();
continue;
}
PDFDocumentDataLoaderDecorator loader(m_document);
const PDFStream* formStream = appearanceStreamObject.getStream();
const PDFDictionary* formDictionary = formStream->getDictionary();
QRectF annotationRectangle = annotation.annotation->getRectangle();
QRectF formBoundingBox = loader.readRectangle(formDictionary->get("BBox"), QRectF());
QMatrix formMatrix = loader.readMatrixFromDictionary(formDictionary, "Matrix", QMatrix());
QByteArray content = m_document->getDecodedStream(formStream);
PDFObject resources = m_document->getObject(formDictionary->get("Resources"));
PDFObject transparencyGroup = m_document->getObject(formDictionary->get("Group"));
if (formBoundingBox.isEmpty() || annotationRectangle.isEmpty())
{
// Form bounding box is empty
continue;
}
QMatrix userSpaceToDeviceSpace = prepareTransformations(pagePointToDevicePointMatrix, painter->device(), annotationFlags, page, annotationRectangle);
if (annotationFlags.testFlag(PDFAnnotation::NoZoom))
{
features.setFlag(PDFRenderer::ClipToCropBox, false);
}
// Jakub Melka: perform algorithm 8.1, defined in PDF 1.7 reference,
// chapter 8.4.4 Appearance streams.
// Step 1) - calculate transformed appearance box
QRectF transformedAppearanceBox = formMatrix.mapRect(formBoundingBox);
// Step 2) - calculate matrix A, which maps from form space to annotation space
// Matrix A transforms from transformed appearance box space to the
// annotation rectangle.
const PDFReal scaleX = annotationRectangle.width() / transformedAppearanceBox.width();
const PDFReal scaleY = annotationRectangle.height() / transformedAppearanceBox.height();
const PDFReal translateX = annotationRectangle.left() - transformedAppearanceBox.left() * scaleX;
const PDFReal translateY = annotationRectangle.bottom() - transformedAppearanceBox.bottom() * scaleY;
QMatrix A(scaleX, 0.0, 0.0, scaleY, translateX, translateY);
// Step 3) - compute final matrix AA
QMatrix AA = formMatrix * A;
PDFPainter pdfPainter(painter, features, userSpaceToDeviceSpace, page, m_document, m_fontCache, cms.get(), m_optionalActivity, m_meshQualitySettings);
pdfPainter.initializeProcessor();
// Jakub Melka: we must check, that we do not display annotation disabled by optional content
PDFObjectReference oc = annotation.annotation->getOptionalContent();
if (!oc.isValid() || !pdfPainter.isContentSuppressedByOC(oc))
{
pdfPainter.processForm(AA, formBoundingBox, resources, transparencyGroup, content);
}
}
catch (PDFException exception)
{
errors.push_back(PDFRenderError(RenderErrorType::Error, exception.getMessage()));
continue; continue;
} }
catch (PDFRendererException exception)
PDFObject appearanceStreamObject = m_document->getObject(getAppearanceStream(annotation));
if (!appearanceStreamObject.isStream())
{ {
// Object is not valid appearance stream. We will try to draw default errors.push_back(exception.getError());
// annotation appearance.
painter->save();
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);
painter->restore();
continue; continue;
} }
PDFDocumentDataLoaderDecorator loader(m_document);
const PDFStream* formStream = appearanceStreamObject.getStream();
const PDFDictionary* formDictionary = formStream->getDictionary();
QRectF annotationRectangle = annotation.annotation->getRectangle();
QRectF formBoundingBox = loader.readRectangle(formDictionary->get("BBox"), QRectF());
QMatrix formMatrix = loader.readMatrixFromDictionary(formDictionary, "Matrix", QMatrix());
QByteArray content = m_document->getDecodedStream(formStream);
PDFObject resources = m_document->getObject(formDictionary->get("Resources"));
PDFObject transparencyGroup = m_document->getObject(formDictionary->get("Group"));
if (formBoundingBox.isEmpty() || annotationRectangle.isEmpty())
{
// Form bounding box is empty
continue;
}
QMatrix userSpaceToDeviceSpace = prepareTransformations(pagePointToDevicePointMatrix, painter->device(), annotationFlags, page, annotationRectangle);
if (annotationFlags.testFlag(PDFAnnotation::NoZoom))
{
features.setFlag(PDFRenderer::ClipToCropBox, false);
}
// Jakub Melka: perform algorithm 8.1, defined in PDF 1.7 reference,
// chapter 8.4.4 Appearance streams.
// Step 1) - calculate transformed appearance box
QRectF transformedAppearanceBox = formMatrix.mapRect(formBoundingBox);
// Step 2) - calculate matrix A, which maps from form space to annotation space
// Matrix A transforms from transformed appearance box space to the
// annotation rectangle.
const PDFReal scaleX = annotationRectangle.width() / transformedAppearanceBox.width();
const PDFReal scaleY = annotationRectangle.height() / transformedAppearanceBox.height();
const PDFReal translateX = annotationRectangle.left() - transformedAppearanceBox.left() * scaleX;
const PDFReal translateY = annotationRectangle.bottom() - transformedAppearanceBox.bottom() * scaleY;
QMatrix A(scaleX, 0.0, 0.0, scaleY, translateX, translateY);
// Step 3) - compute final matrix AA
QMatrix AA = formMatrix * A;
PDFPainter pdfPainter(painter, features, userSpaceToDeviceSpace, page, m_document, m_fontCache, cms.get(), m_optionalActivity, m_meshQualitySettings);
pdfPainter.initializeProcessor();
// Jakub Melka: we must check, that we do not display annotation disabled by optional content
PDFObjectReference oc = annotation.annotation->getOptionalContent();
if (!oc.isValid() || !pdfPainter.isContentSuppressedByOC(oc))
{
pdfPainter.processForm(AA, formBoundingBox, resources, transparencyGroup, content);
}
} }
m_fontCache->setCacheShrinkEnabled(this, true); m_fontCache->setCacheShrinkEnabled(this, true);

View File

@ -1129,7 +1129,7 @@ private:
PDFAnnotationAdditionalActions m_additionalActions; PDFAnnotationAdditionalActions m_additionalActions;
}; };
/// Widget annotation represents form fileds for interactive forms. /// Widget annotation represents form fields for interactive forms.
/// Annotation's dictionary is merged with form field dictionary, /// Annotation's dictionary is merged with form field dictionary,
/// it can be done, because dictionaries doesn't overlap. /// it can be done, because dictionaries doesn't overlap.
class PDFWidgetAnnotation : public PDFAnnotation class PDFWidgetAnnotation : public PDFAnnotation
@ -1281,7 +1281,8 @@ public:
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const override; const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
void setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity); void setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity);

View File

@ -253,7 +253,7 @@ PDFDocumentDataLoaderDecorator::PDFDocumentDataLoaderDecorator(const PDFDocument
} }
QByteArray PDFDocumentDataLoaderDecorator::readName(const PDFObject& object) QByteArray PDFDocumentDataLoaderDecorator::readName(const PDFObject& object) const
{ {
const PDFObject& dereferencedObject = m_storage->getObject(object); const PDFObject& dereferencedObject = m_storage->getObject(object);
if (dereferencedObject.isName()) if (dereferencedObject.isName())
@ -264,7 +264,7 @@ QByteArray PDFDocumentDataLoaderDecorator::readName(const PDFObject& object)
return QByteArray(); return QByteArray();
} }
QByteArray PDFDocumentDataLoaderDecorator::readString(const PDFObject& object) QByteArray PDFDocumentDataLoaderDecorator::readString(const PDFObject& object) const
{ {
const PDFObject& dereferencedObject = m_storage->getObject(object); const PDFObject& dereferencedObject = m_storage->getObject(object);
if (dereferencedObject.isString()) if (dereferencedObject.isString())
@ -362,7 +362,7 @@ QRectF PDFDocumentDataLoaderDecorator::readRectangle(const PDFObject& object, co
return defaultValue; return defaultValue;
} }
QMatrix PDFDocumentDataLoaderDecorator::readMatrixFromDictionary(const PDFDictionary* dictionary, const char* key, QMatrix defaultValue) QMatrix PDFDocumentDataLoaderDecorator::readMatrixFromDictionary(const PDFDictionary* dictionary, const char* key, QMatrix defaultValue) const
{ {
if (dictionary->hasKey(key)) if (dictionary->hasKey(key))
{ {
@ -380,7 +380,7 @@ QMatrix PDFDocumentDataLoaderDecorator::readMatrixFromDictionary(const PDFDictio
std::vector<PDFReal> PDFDocumentDataLoaderDecorator::readNumberArrayFromDictionary(const PDFDictionary* dictionary, std::vector<PDFReal> PDFDocumentDataLoaderDecorator::readNumberArrayFromDictionary(const PDFDictionary* dictionary,
const char* key, const char* key,
std::vector<PDFReal> defaultValue) std::vector<PDFReal> defaultValue) const
{ {
if (dictionary->hasKey(key)) if (dictionary->hasKey(key))
{ {
@ -390,7 +390,7 @@ std::vector<PDFReal> PDFDocumentDataLoaderDecorator::readNumberArrayFromDictiona
return defaultValue; return defaultValue;
} }
std::vector<PDFInteger> PDFDocumentDataLoaderDecorator::readIntegerArrayFromDictionary(const PDFDictionary* dictionary, const char* key) std::vector<PDFInteger> PDFDocumentDataLoaderDecorator::readIntegerArrayFromDictionary(const PDFDictionary* dictionary, const char* key) const
{ {
if (dictionary->hasKey(key)) if (dictionary->hasKey(key))
{ {
@ -440,7 +440,7 @@ QString PDFDocumentDataLoaderDecorator::readTextStringFromDictionary(const PDFDi
return defaultValue; return defaultValue;
} }
std::vector<PDFObjectReference> PDFDocumentDataLoaderDecorator::readReferenceArrayFromDictionary(const PDFDictionary* dictionary, const char* key) std::vector<PDFObjectReference> PDFDocumentDataLoaderDecorator::readReferenceArrayFromDictionary(const PDFDictionary* dictionary, const char* key) const
{ {
if (dictionary->hasKey(key)) if (dictionary->hasKey(key))
{ {
@ -605,7 +605,7 @@ bool PDFDocumentDataLoaderDecorator::readBooleanFromDictionary(const PDFDictiona
return defaultValue; return defaultValue;
} }
QByteArray PDFDocumentDataLoaderDecorator::readNameFromDictionary(const PDFDictionary* dictionary, const char* key) QByteArray PDFDocumentDataLoaderDecorator::readNameFromDictionary(const PDFDictionary* dictionary, const char* key) const
{ {
if (dictionary->hasKey(key)) if (dictionary->hasKey(key))
{ {
@ -615,7 +615,7 @@ QByteArray PDFDocumentDataLoaderDecorator::readNameFromDictionary(const PDFDicti
return QByteArray(); return QByteArray();
} }
QByteArray PDFDocumentDataLoaderDecorator::readStringFromDictionary(const PDFDictionary* dictionary, const char* key) QByteArray PDFDocumentDataLoaderDecorator::readStringFromDictionary(const PDFDictionary* dictionary, const char* key) const
{ {
if (dictionary->hasKey(key)) if (dictionary->hasKey(key))
{ {
@ -655,6 +655,28 @@ QStringList PDFDocumentDataLoaderDecorator::readTextStringList(const PDFObject&
return result; return result;
} }
std::optional<QByteArray> PDFDocumentDataLoaderDecorator::readOptionalStringFromDictionary(const PDFDictionary* dictionary, const char* key) const
{
if (dictionary->hasKey(key))
{
return readStringFromDictionary(dictionary, key);
}
return std::nullopt;
}
std::optional<PDFInteger> PDFDocumentDataLoaderDecorator::readOptionalIntegerFromDictionary(const PDFDictionary* dictionary, const char* key) const
{
if (dictionary->hasKey(key))
{
PDFInteger integer = readIntegerFromDictionary(dictionary, key, std::numeric_limits<PDFInteger>::max());
if (integer != std::numeric_limits<PDFInteger>::max())
{
return integer;
}
}
return std::nullopt;
}
std::vector<QByteArray> PDFDocumentDataLoaderDecorator::readStringArray(const PDFObject& object) const std::vector<QByteArray> PDFDocumentDataLoaderDecorator::readStringArray(const PDFObject& object) const
{ {
const PDFObject& dereferencedObject = m_storage->getObject(object); const PDFObject& dereferencedObject = m_storage->getObject(object);

View File

@ -28,6 +28,8 @@
#include <QMatrix> #include <QMatrix>
#include <QDateTime> #include <QDateTime>
#include <optional>
namespace pdf namespace pdf
{ {
class PDFDocument; class PDFDocument;
@ -138,12 +140,12 @@ public:
/// Reads a name from the object, if it is possible. If object is not a name, /// Reads a name from the object, if it is possible. If object is not a name,
/// then empty byte array is returned. /// then empty byte array is returned.
/// \param object Object, can be an indirect reference to object (it is dereferenced) /// \param object Object, can be an indirect reference to object (it is dereferenced)
QByteArray readName(const PDFObject& object); QByteArray readName(const PDFObject& object) const;
/// Reads a string from the object, if it is possible. If object is not a string, /// Reads a string from the object, if it is possible. If object is not a string,
/// then empty byte array is returned. /// then empty byte array is returned.
/// \param object Object, can be an indirect reference to object (it is dereferenced) /// \param object Object, can be an indirect reference to object (it is dereferenced)
QByteArray readString(const PDFObject& object); QByteArray readString(const PDFObject& object) const;
/// Reads an integer from the object, if it is possible. /// Reads an integer from the object, if it is possible.
/// \param object Object, can be an indirect reference to object (it is dereferenced) /// \param object Object, can be an indirect reference to object (it is dereferenced)
@ -239,15 +241,15 @@ public:
/// Tries to read matrix from the dictionary. If matrix entry is not present, default value is returned. /// Tries to read matrix from the dictionary. If matrix entry is not present, default value is returned.
/// If it is present and invalid, exception is thrown. /// If it is present and invalid, exception is thrown.
QMatrix readMatrixFromDictionary(const PDFDictionary* dictionary, const char* key, QMatrix defaultValue); QMatrix readMatrixFromDictionary(const PDFDictionary* dictionary, const char* key, QMatrix defaultValue) const;
/// Tries to read array of real values from dictionary. If entry dictionary doesn't exist, /// Tries to read array of real values from dictionary. If entry dictionary doesn't exist,
/// or error occurs, default value is returned. /// or error occurs, default value is returned.
std::vector<PDFReal> readNumberArrayFromDictionary(const PDFDictionary* dictionary, const char* key, std::vector<PDFReal> defaultValue = std::vector<PDFReal>()); std::vector<PDFReal> readNumberArrayFromDictionary(const PDFDictionary* dictionary, const char* key, std::vector<PDFReal> defaultValue = std::vector<PDFReal>()) const;
/// Tries to read array of integer values from dictionary. If entry dictionary doesn't exist, /// Tries to read array of integer values from dictionary. If entry dictionary doesn't exist,
/// or error occurs, empty array is returned. /// or error occurs, empty array is returned.
std::vector<PDFInteger> readIntegerArrayFromDictionary(const PDFDictionary* dictionary, const char* key); std::vector<PDFInteger> readIntegerArrayFromDictionary(const PDFDictionary* dictionary, const char* key) const;
/// Reads number from dictionary. If dictionary entry doesn't exist, or error occurs, default value is returned. /// Reads number from dictionary. If dictionary entry doesn't exist, or error occurs, default value is returned.
/// \param dictionary Dictionary containing desired data /// \param dictionary Dictionary containing desired data
@ -275,7 +277,7 @@ public:
/// Tries to read array of references from dictionary. If entry dictionary doesn't exist, /// Tries to read array of references from dictionary. If entry dictionary doesn't exist,
/// or error occurs, empty array is returned. /// or error occurs, empty array is returned.
std::vector<PDFObjectReference> readReferenceArrayFromDictionary(const PDFDictionary* dictionary, const char* key); std::vector<PDFObjectReference> readReferenceArrayFromDictionary(const PDFDictionary* dictionary, const char* key) const;
/// Reads number array from dictionary. Reads all values. If some value is not /// Reads number array from dictionary. Reads all values. If some value is not
/// real number (or integer number), default value is returned. Default value is also returned, /// real number (or integer number), default value is returned. Default value is also returned,
@ -324,12 +326,12 @@ public:
/// Reads a name from dictionary. If dictionary entry doesn't exist, or error occurs, empty byte array is returned. /// Reads a name from dictionary. If dictionary entry doesn't exist, or error occurs, empty byte array is returned.
/// \param dictionary Dictionary containing desired data /// \param dictionary Dictionary containing desired data
/// \param key Entry key /// \param key Entry key
QByteArray readNameFromDictionary(const PDFDictionary* dictionary, const char* key); QByteArray readNameFromDictionary(const PDFDictionary* dictionary, const char* key) const;
/// Reads a string from dictionary. If dictionary entry doesn't exist, or error occurs, empty byte array is returned. /// Reads a string from dictionary. If dictionary entry doesn't exist, or error occurs, empty byte array is returned.
/// \param dictionary Dictionary containing desired data /// \param dictionary Dictionary containing desired data
/// \param key Entry key /// \param key Entry key
QByteArray readStringFromDictionary(const PDFDictionary* dictionary, const char* key); QByteArray readStringFromDictionary(const PDFDictionary* dictionary, const char* key) const;
/// Reads string array from dictionary. Reads all values. If error occurs, /// Reads string array from dictionary. Reads all values. If error occurs,
/// then empty array is returned. /// then empty array is returned.
@ -361,6 +363,17 @@ public:
return result; return result;
} }
/// Reads optional string from dictionary. If key is not in dictionary,
/// then empty optional is returned.
/// \param dictionary Dictionary containing desired data
/// \param key Entry key
std::optional<QByteArray> readOptionalStringFromDictionary(const PDFDictionary* dictionary, const char* key) const;
/// Reads optionalinteger from dictionary. If dictionary entry doesn't exist, or error occurs, empty optional is returned.
/// \param dictionary Dictionary containing desired data
/// \param key Entry key
std::optional<PDFInteger> readOptionalIntegerFromDictionary(const PDFDictionary* dictionary, const char* key) const;
private: private:
const PDFObjectStorage* m_storage; const PDFObjectStorage* m_storage;
}; };

View File

@ -19,6 +19,7 @@
#define PDFDOCUMENTDRAWINTERFACE_H #define PDFDOCUMENTDRAWINTERFACE_H
#include "pdfglobal.h" #include "pdfglobal.h"
#include "pdfexception.h"
class QPainter; class QPainter;
@ -40,11 +41,13 @@ public:
/// \param compiledPage Compiled page /// \param compiledPage Compiled page
/// \param layoutGetter Layout getter /// \param layoutGetter Layout getter
/// \param pagePointToDevicePointMatrix Matrix mapping page space to device point space /// \param pagePointToDevicePointMatrix Matrix mapping page space to device point space
/// \param[out] errors Output parameter - rendering errors
virtual void drawPage(QPainter* painter, virtual void drawPage(QPainter* painter,
pdf::PDFInteger pageIndex, pdf::PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const; const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const;
/// Performs drawing of additional graphics after all pages are drawn onto the painter. /// Performs drawing of additional graphics after all pages are drawn onto the painter.
/// \param painter Painter /// \param painter Painter

View File

@ -752,12 +752,13 @@ void PDFDrawWidgetProxy::drawPages(QPainter* painter, QRect rect, PDFRenderer::F
painter->restore(); painter->restore();
} }
QList<PDFRenderError> drawInterfaceErrors;
if (!features.testFlag(PDFRenderer::DenyExtraGraphics)) if (!features.testFlag(PDFRenderer::DenyExtraGraphics))
{ {
for (IDocumentDrawInterface* drawInterface : m_drawInterfaces) for (IDocumentDrawInterface* drawInterface : m_drawInterfaces)
{ {
painter->save(); painter->save();
drawInterface->drawPage(painter, item.pageIndex, compiledPage, layoutGetter, matrix); drawInterface->drawPage(painter, item.pageIndex, compiledPage, layoutGetter, matrix, drawInterfaceErrors);
painter->restore(); painter->restore();
} }
} }
@ -794,10 +795,15 @@ void PDFDrawWidgetProxy::drawPages(QPainter* painter, QRect rect, PDFRenderer::F
painter->restore(); painter->restore();
} }
const QList<PDFRenderError>& errors = compiledPage->getErrors(); const QList<PDFRenderError>& pageErrors = compiledPage->getErrors();
if (!errors.empty()) if (!pageErrors.empty() || !drawInterfaceErrors.empty())
{ {
emit renderingError(item.pageIndex, errors); QList<PDFRenderError> errors = pageErrors;
if (!drawInterfaceErrors.isEmpty())
{
errors.append(drawInterfaceErrors);
}
emit renderingError(item.pageIndex, qMove(errors));
} }
} }
} }
@ -1371,13 +1377,15 @@ void IDocumentDrawInterface::drawPage(QPainter* painter,
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{ {
Q_UNUSED(painter); Q_UNUSED(painter);
Q_UNUSED(pageIndex); Q_UNUSED(pageIndex);
Q_UNUSED(compiledPage); Q_UNUSED(compiledPage);
Q_UNUSED(layoutGetter); Q_UNUSED(layoutGetter);
Q_UNUSED(pagePointToDevicePointMatrix); Q_UNUSED(pagePointToDevicePointMatrix);
Q_UNUSED(errors);
} }
void IDocumentDrawInterface::drawPostRendering(QPainter* painter, QRect rect) const void IDocumentDrawInterface::drawPostRendering(QPainter* painter, QRect rect) const

View File

@ -0,0 +1,213 @@
// 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/>.
#include "pdfform.h"
#include "pdfdocument.h"
namespace pdf
{
PDFForm PDFForm::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDFForm form;
if (const PDFDictionary* formDictionary = storage->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(storage);
std::vector<PDFObjectReference> fieldRoots = loader.readReferenceArrayFromDictionary(formDictionary, "Fields");
form.m_formFields.reserve(fieldRoots.size());
for (const PDFObjectReference& fieldRootReference : fieldRoots)
{
form.m_formFields.emplace_back(PDFFormField::parse(storage, fieldRootReference, nullptr));
}
form.m_needAppearances = loader.readBooleanFromDictionary(formDictionary, "NeedAppearances", false);
form.m_signatureFlags = static_cast<SignatureFlags>(loader.readIntegerFromDictionary(formDictionary, "SigFlags", 0));
form.m_calculationOrder = loader.readReferenceArrayFromDictionary(formDictionary, "CO");
form.m_resources = formDictionary->get("DR");
form.m_defaultAppearance = loader.readOptionalStringFromDictionary(formDictionary, "DA");
form.m_quadding = loader.readOptionalIntegerFromDictionary(formDictionary, "Q");
form.m_xfa = formDictionary->get("XFA");
}
return form;
}
PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField)
{
PDFFormFieldPointer result;
if (const PDFDictionary* fieldDictionary = storage->getDictionaryFromObject(storage->getObjectByReference(reference)))
{
PDFFormField* formField = nullptr;
PDFFormFieldButton* formFieldButton = nullptr;
PDFFormFieldText* formFieldText = nullptr;
PDFFormFieldChoice* formFieldChoice = nullptr;
PDFFormFieldSignature* formFieldSignature = nullptr;
constexpr const std::array<std::pair<const char*, FieldType>, 4> fieldTypes = {
std::pair<const char*, FieldType>{ "Btn", FieldType::Button },
std::pair<const char*, FieldType>{ "Tx", FieldType::Text },
std::pair<const char*, FieldType>{ "Ch", FieldType::Choice },
std::pair<const char*, FieldType>{ "Sig", FieldType::Signature }
};
PDFDocumentDataLoaderDecorator loader(storage);
FieldType formFieldType = parentField ? parentField->getFieldType() : FieldType::Invalid;
if (fieldDictionary->hasKey(FT))
{
formFieldType = loader.readEnumByName(fieldDictionary->get("FT"), fieldTypes.begin(), fieldTypes.end(), FieldType::Invalid);
}
switch (formFieldType)
{
case FieldType::Invalid:
formField = new PDFFormField();
break;
case FieldType::Button:
formFieldButton = new PDFFormFieldButton();
formField = formFieldButton;
break;
case FieldType::Text:
formFieldText = new PDFFormFieldText();
formField = formFieldText;
break;
case FieldType::Choice:
formFieldChoice = new PDFFormFieldChoice();
formField = formFieldChoice;
break;
case FieldType::Signature:
formFieldSignature = new PDFFormFieldSignature();
formField = formFieldSignature;
break;
default:
Q_ASSERT(false);
break;
}
result.reset(formField);
std::vector<PDFObjectReference> kids = loader.readReferenceArrayFromDictionary(fieldDictionary, "Kids");
if (kids.empty())
{
// This means, that field's dictionary is merged with annotation's dictionary,
// so, we will add pointer to self to the form widgets. But we must test, if we
// really have merged annotation's dictionary - we test it by checking for 'Subtype'
// presence.
if (loader.readNameFromDictionary(fieldDictionary, "Subtype") == "Widget")
{
formField->m_widgets.emplace_back(PDFFormWidget::parse(storage, reference, formField));
}
}
else
{
// Otherwise we must scan all references, and determine, if kid is another form field,
// or it is a widget annotation. Widget annotations has required field 'Subtype', which
// has value 'Widget', form field has required field 'Parent' (for non-root fields).
for (const PDFObjectReference& kid : kids)
{
if (const PDFDictionary* childDictionary = storage->getDictionaryFromObject(storage->getObjectByReference(kid)))
{
const bool isWidget = loader.readNameFromDictionary(childDictionary, "Subtype") == "Widget";
const bool isField = loader.readReferenceFromDictionary(childDictionary, "Parent").isValid();
if (isField)
{
// This is form field (potentially merged with widget)
formField->m_childFields.emplace_back(PDFFormField::parse(storage, kid, formField));
}
else if (isWidget)
{
// This is pure widget (with no form field)
formField->m_widgets.emplace_back(PDFFormWidget::parse(storage, kid, formField));
}
}
}
}
PDFObject parentV = parentField ? parentField->getValue() : PDFObject();
PDFObject parentDV = parentField ? parentField->getDefaultValue() : PDFObject();
FieldFlags parentFlags = parentField ? parentField->getFlags() : None;
formField->m_selfReference = reference;
formField->m_fieldType = formFieldType;
formField->m_parentField = parentField;
formField->m_fieldNames[Partial] = loader.readTextStringFromDictionary(fieldDictionary, "T", QString());
formField->m_fieldNames[UserCaption] = loader.readTextStringFromDictionary(fieldDictionary, "TU", QString());
formField->m_fieldNames[Export] = loader.readTextStringFromDictionary(fieldDictionary, "TM", QString());
formField->m_fieldNames[FullyQualified] = parentField ? QString("%1.%2").arg(parentField->getName(FullyQualified), formField->m_fieldNames[Partial]) : formField->m_fieldNames[Partial];
formField->m_fieldFlags = fieldDictionary->hasKey("Ff") ? static_cast<FieldFlags>(loader.readIntegerFromDictionary(fieldDictionary, "Ff", 0)) : parentFlags;
formField->m_value = fieldDictionary->hasKey("V") ? fieldDictionary->get("V") : parentV;
formField->m_defaultValue = fieldDictionary->hasKey("DV") ? fieldDictionary->get("DV") : parentDV;
formField->m_additionalActions = PDFAnnotationAdditionalActions::parse(storage, fieldDictionary->get("AA"));
if (formFieldButton)
{
formFieldButton->m_options = loader.readTextStringList(fieldDictionary->get("Opt"));
}
if (formFieldText)
{
PDFInteger maxLengthDefault = 0;
if (PDFFormFieldText* parentTextField = dynamic_cast<PDFFormFieldText*>(parentField))
{
maxLengthDefault = parentTextField->getTextMaximalLength();
}
formFieldText->m_maxLength = loader.readIntegerFromDictionary(fieldDictionary, "MaxLen", maxLengthDefault);
}
}
return result;
}
PDFFormWidget::PDFFormWidget(PDFObjectReference widget, PDFFormField* parentField) :
m_widget(widget),
m_parentField(parentField)
{
}
PDFFormWidget PDFFormWidget::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField)
{
Q_UNUSED(storage);
return PDFFormWidget(reference, parentField);
}
PDFFormFieldButton::ButtonType PDFFormFieldButton::getButtonType() const
{
if (m_fieldFlags.testFlag(PushButton))
{
return ButtonType::PushButton;
}
else if (m_fieldFlags.testFlag(Radio))
{
return ButtonType::RadioButton;
}
return ButtonType::CheckBox;
}
} // namespace pdf

View File

@ -0,0 +1,322 @@
// 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 PDFFORM_H
#define PDFFORM_H
#include "pdfobject.h"
#include "pdfannotation.h"
#include <optional>
namespace pdf
{
class PDFObjectStorage;
class PDFFormField;
using PDFFormFieldPointer = QSharedPointer<PDFFormField>;
using PDFFormFields = std::vector<PDFFormFieldPointer>;
/// A simple proxy to the widget annotation
class PDFFormWidget
{
public:
explicit inline PDFFormWidget() = default;
explicit inline PDFFormWidget(PDFObjectReference widget, PDFFormField* parentField);
PDFObjectReference getWidget() const { return m_widget; }
PDFFormField* getParent() const { return m_parentField; }
/// Parses form widget from the object reference. If some error occurs
/// then empty object is returned, no exception is thrown.
/// \param storage Storage
/// \param reference Widget reference
/// \param parentField Parent field
static PDFFormWidget parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
private:
PDFObjectReference m_widget;
PDFFormField* m_parentField;
};
using PDFFormWidgets = std::vector<PDFFormWidget>;
/// Form field is terminal or non-terminal field (non-terminal fields
/// have children), fields represents various interactive widgets, such as
/// checks, radio buttons, text edits etc., which are editable and user
/// can interact with them.
class PDFFormField
{
public:
explicit inline PDFFormField() = default;
enum class FieldType
{
Invalid,
Button,
Text,
Choice,
Signature
};
enum NameType
{
Partial, ///< Partial name for this field
UserCaption, ///< Name used in GUI (for example, in message boxes)
FullyQualified, ///< Fully qualified name (according to the PDF specification 1.7)
Export, ///< Name for export
NameTypeEnd
};
using FieldNames = std::array<QString, NameTypeEnd>;
enum FieldFlag
{
None = 0,
/// Field is read only, it doesn't respond on mouse clicks (if it is a button),
/// associated widget annotation will not interact with the user. Field can't
/// change it's value. Mainly used for calculable fields.
ReadOnly = 1 << 0,
/// If set, field is required, when submitting form by submit action. If submit
/// action is triggered, then all fields with this flag must have a value.
Required = 1 << 1,
/// If set, field value must not be exported by submit form action.
NoExport = 1 << 2,
/// Text fields only. If set, then text can span multiple lines. Otherwise,
/// text is restricted to single line.
Multiline = 1 << 12,
/// Text fields only. If set, field is intended to display text edit, which
/// can edit passwords. Password characters should not be echoed to the screen
/// and value of this field should not be stored in PDF file.
Password = 1 << 13,
/// Only for radio buttons. If set, at least one radio button is checked.
/// If user clicks on checked radio button, it is not checked off. Otherwise
/// user can uncheck checked radio button (so no button is selected).
NoToggleToOff = 1 << 14,
/// Valid only for buttons which have PushButton flag unset. If Radio flag is set,
/// then widget is radio button, otherwise widget is push button.
Radio = 1 << 15,
/// Valid only for buttons. If set, button is push button.
PushButton = 1 << 16,
/// For choice fields only. If set, choice field is combo box.
/// If clear, it is a list box.
Combo = 1 << 17,
/// For choice fields combo boxes only. If set, combo box is editable,
/// i.e. user can enter custom text. If this flag is cleared, then combo box
/// is not editable and user can only select items from the drop down list.
Edit = 1 << 18,
/// For choice fields only. If set, the field option's items should be sorted
/// alphabetically, but not by the viewer application, but by author of the form.
/// Viewer application should respect Opt array and display items in that order.
Sort = 1 << 19,
/// Text fields only. Text field is used to select file path, whose contents
/// should be submitted as the value of the field.
FileSelect = 1 << 20,
/// For choice fields only. If set, then user can select multiple choices
/// simultaneously, if clear, then only one item should be selected at the time.
MultiSelect = 1 << 21,
/// Text fields only. If set, texts entered in this field should not be spell checked.
DoNotSpellcheck = 1 << 22,
/// Text fields only. Allow only so much text, which fits field's area. If field's area is filled,
/// then do not allow the user to store more text in the field.
DoNotScroll = 1 << 23,
/// Text fields only. If set, then MaxLen entry and annotation rectangle is used
/// to divide space equally for each character. Text is laid out into those spaces.
Comb = 1 << 24,
/// Valid only for radio buttons. Radio buttons in a group of radio buttons,
/// which have same value for 'On' state, will turn On and Off in unison, if one
/// is checked, all are checked. If clear, radio buttons are mutually exclusive.
RadiosInUnison = 1 << 25,
/// Text fields only. Value of this field should be a rich text.
RichText = 1 << 25,
/// Choice fields only. If set, then when user selects choice by mouse,
/// data is immediately commited. Otherwise data are commited only, when
/// widget lose focus.
CommitOnSelectionChange = 1 << 26
};
Q_DECLARE_FLAGS(FieldFlags, FieldFlag)
PDFObjectReference getSelfReference() const { return m_selfReference; }
FieldType getFieldType() const { return m_fieldType; }
const PDFFormField* getParentField() const { return m_parentField; }
const PDFFormFields& getChildFields() const { return m_childFields; }
const PDFFormWidgets& getWidgets() const { return m_widgets; }
const QString& getName(NameType nameType) const { return m_fieldNames.at(nameType); }
FieldFlags getFlags() const { return m_fieldFlags; }
const PDFObject& getValue() const { return m_value; }
const PDFObject& getDefaultValue() const { return m_defaultValue; }
/// Parses form field from the object reference. If some error occurs
/// then null pointer is returned, no exception is thrown.
/// \param storage Storage
/// \param reference Field reference
/// \param parentField Parent field (or nullptr, if it is root field)
static PDFFormFieldPointer parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
protected:
PDFObjectReference m_selfReference;
FieldType m_fieldType = FieldType::Invalid;
PDFFormField* m_parentField = nullptr;
PDFFormFields m_childFields;
PDFFormWidgets m_widgets;
FieldNames m_fieldNames;
FieldFlags m_fieldFlags = None;
PDFObject m_value;
PDFObject m_defaultValue;
PDFAnnotationAdditionalActions m_additionalActions;
};
/// Represents pushbutton, checkbox and radio button (which is distinguished
/// by flags).
class PDFFormFieldButton : public PDFFormField
{
public:
explicit inline PDFFormFieldButton() = default;
enum class ButtonType
{
PushButton,
RadioButton,
CheckBox
};
/// Determines button type from form field's flags
ButtonType getButtonType() const;
const QStringList& getOptions() const { return m_options; }
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
/// 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.
QStringList m_options;
};
class PDFFormFieldText : public PDFFormField
{
public:
explicit inline PDFFormFieldText() = default;
PDFInteger getTextMaximalLength() const { return m_maxLength; }
private:
friend static PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
/// Maximal length of text in the field. If zero,
/// no maximal length is specified.
PDFInteger m_maxLength = 0;
};
class PDFFormFieldChoice : public PDFFormField
{
public:
explicit inline PDFFormFieldChoice() = default;
private:
friend static PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
};
class PDFFormFieldSignature : public PDFFormField
{
public:
explicit inline PDFFormFieldSignature() = default;
private:
friend static PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField);
};
/// This class represents interactive form. Interactive form fields can span multiple
/// document pages. So this object represents all interactive form fields in the document.
/// Fields forms tree-like structure, where leafs are usually widgets. Fields include
/// ordinary widgets, such as buttons, check boxes, combo boxes and text fields, and one
/// special - signature field, which represents digital signature.
class PDFForm
{
public:
explicit inline PDFForm() = default;
enum class FormType
{
None,
AcroForm,
XFAForm
};
enum SignatureFlag
{
None = 0x0000,
SignatureExists = 0x0001, ///< If set, at least one signature exists in the document
AppendOnly = 0x0002, ///< If set, signature may be invalidated during rewrite
};
Q_DECLARE_FLAGS(SignatureFlags, SignatureFlag)
const PDFFormFields& getFormFields() const { return m_formFields; }
bool isAppearanceUpdateNeeded() const { return m_needAppearances; }
SignatureFlags getSignatureFlags() const { return m_signatureFlags; }
const std::vector<PDFObjectReference>& getCalculationOrder() const { return m_calculationOrder; }
const PDFObject& getResources() const { return m_resources; }
const std::optional<QByteArray>& getDefaultAppearance() const { return m_defaultAppearance; }
const std::optional<PDFInteger>& getQuadding() const { return m_quadding; }
const PDFObject& getXFA() const { return m_xfa; }
/// Parses form from the object. If some error occurs
/// then empty form is returned, no exception is thrown.
/// \param storage Storage
/// \param reference Field reference
static PDFForm parse(const PDFObjectStorage* storage, PDFObject object);
private:
FormType m_formType = FormType::None;
PDFFormFields m_formFields;
bool m_needAppearances = false;
SignatureFlags m_signatureFlags = None;
std::vector<PDFObjectReference> m_calculationOrder;
PDFObject m_resources;
std::optional<QByteArray> m_defaultAppearance;
std::optional<PDFInteger> m_quadding;
PDFObject m_xfa;
};
} // namespace pdf
#endif // PDFFORM_H

View File

@ -237,8 +237,9 @@ QImage PDFRasterizer::render(PDFInteger pageIndex,
if (annotationManager) if (annotationManager)
{ {
QList<PDFRenderError> errors;
PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex); PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex);
annotationManager->drawPage(&painter, pageIndex, compiledPage, textLayoutGetter, matrix); annotationManager->drawPage(&painter, pageIndex, compiledPage, textLayoutGetter, matrix, errors);
} }
} }
@ -268,8 +269,9 @@ QImage PDFRasterizer::render(PDFInteger pageIndex,
if (annotationManager) if (annotationManager)
{ {
QList<PDFRenderError> errors;
PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex); PDFTextLayoutGetter textLayoutGetter(nullptr, pageIndex);
annotationManager->drawPage(&painter, pageIndex, compiledPage, textLayoutGetter, matrix); annotationManager->drawPage(&painter, pageIndex, compiledPage, textLayoutGetter, matrix, errors);
} }
} }

View File

@ -443,7 +443,14 @@ QByteArray PDFFlateDecodeFilter::uncompress(const QByteArray& data)
break; // No error, normal behaviour break; // No error, normal behaviour
default: default:
{
if (errorMessage.isEmpty())
{
errorMessage = PDFTranslationContext::tr("zlib code: %1").arg(error);
}
throw PDFException(PDFTranslationContext::tr("Error decompressing by flate method: %1").arg(errorMessage)); throw PDFException(PDFTranslationContext::tr("Error decompressing by flate method: %1").arg(errorMessage));
}
} }
return result; return result;

View File

@ -213,9 +213,11 @@ void PDFFindTextTool::drawPage(QPainter* painter,
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{ {
Q_UNUSED(compiledPage); Q_UNUSED(compiledPage);
Q_UNUSED(errors);
const pdf::PDFTextSelection& textSelection = getTextSelection(); const pdf::PDFTextSelection& textSelection = getTextSelection();
pdf::PDFTextSelectionPainter textSelectionPainter(&textSelection); pdf::PDFTextSelectionPainter textSelectionPainter(&textSelection);
@ -507,9 +509,11 @@ void PDFSelectTextTool::drawPage(QPainter* painter,
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{ {
Q_UNUSED(compiledPage); Q_UNUSED(compiledPage);
Q_UNUSED(errors);
pdf::PDFTextSelectionPainter textSelectionPainter(&m_textSelection); pdf::PDFTextSelectionPainter textSelectionPainter(&m_textSelection);
textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix); textSelectionPainter.draw(painter, pageIndex, layoutGetter, pagePointToDevicePointMatrix);
@ -966,10 +970,12 @@ void PDFPickTool::drawPage(QPainter* painter,
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{ {
Q_UNUSED(compiledPage); Q_UNUSED(compiledPage);
Q_UNUSED(layoutGetter); Q_UNUSED(layoutGetter);
Q_UNUSED(errors);
// If we are picking rectangles, then draw current selection rectangle // If we are picking rectangles, then draw current selection rectangle
if (m_mode == Mode::Rectangles && m_pageIndex == pageIndex && !m_pickedPoints.empty()) if (m_mode == Mode::Rectangles && m_pageIndex == pageIndex && !m_pickedPoints.empty())

View File

@ -150,7 +150,8 @@ public:
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const override; const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
protected: protected:
virtual void setActiveImpl(bool active) override; virtual void setActiveImpl(bool active) override;
@ -216,7 +217,8 @@ public:
PDFInteger pageIndex, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const override; const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override; virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override; virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
@ -308,7 +310,8 @@ public:
virtual void drawPage(QPainter* painter, PDFInteger pageIndex, virtual void drawPage(QPainter* painter, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage, const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter, PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const override; const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void drawPostRendering(QPainter* painter, QRect rect) const override; virtual void drawPostRendering(QPainter* painter, QRect rect) const override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override; virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override; virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;

View File

@ -163,9 +163,11 @@ void PDFAdvancedFindWidget::drawPage(QPainter* painter,
pdf::PDFInteger pageIndex, pdf::PDFInteger pageIndex,
const pdf::PDFPrecompiledPage* compiledPage, const pdf::PDFPrecompiledPage* compiledPage,
pdf::PDFTextLayoutGetter& layoutGetter, pdf::PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const const QMatrix& pagePointToDevicePointMatrix,
QList<pdf::PDFRenderError>& errors) const
{ {
Q_UNUSED(compiledPage); Q_UNUSED(compiledPage);
Q_UNUSED(errors);
const pdf::PDFTextSelection& textSelection = getTextSelection(); const pdf::PDFTextSelection& textSelection = getTextSelection();
pdf::PDFTextSelectionPainter textSelectionPainter(&textSelection); pdf::PDFTextSelectionPainter textSelectionPainter(&textSelection);

View File

@ -53,7 +53,8 @@ public:
pdf::PDFInteger pageIndex, pdf::PDFInteger pageIndex,
const pdf::PDFPrecompiledPage* compiledPage, const pdf::PDFPrecompiledPage* compiledPage,
pdf::PDFTextLayoutGetter& layoutGetter, pdf::PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const override; const QMatrix& pagePointToDevicePointMatrix,
QList<pdf::PDFRenderError>& errors) const override;
void setDocument(const pdf::PDFDocument* document); void setDocument(const pdf::PDFDocument* document);