mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
AcroForms implementation (data structures start)
This commit is contained in:
@ -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 \
|
||||||
|
@ -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);
|
||||||
@ -1002,6 +1003,8 @@ void PDFAnnotationManager::drawPage(QPainter* painter,
|
|||||||
m_fontCache->setCacheShrinkEnabled(this, false);
|
m_fontCache->setCacheShrinkEnabled(this, false);
|
||||||
|
|
||||||
for (const PageAnnotation& annotation : annotations.annotations)
|
for (const PageAnnotation& annotation : annotations.annotations)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
const PDFAnnotation::Flags annotationFlags = annotation.annotation->getEffectiveFlags();
|
const PDFAnnotation::Flags annotationFlags = annotation.annotation->getEffectiveFlags();
|
||||||
if (annotationFlags.testFlag(PDFAnnotation::Hidden) || // Annotation is completely hidden
|
if (annotationFlags.testFlag(PDFAnnotation::Hidden) || // Annotation is completely hidden
|
||||||
@ -1080,6 +1083,17 @@ void PDFAnnotationManager::drawPage(QPainter* painter,
|
|||||||
pdfPainter.processForm(AA, formBoundingBox, resources, transparencyGroup, content);
|
pdfPainter.processForm(AA, formBoundingBox, resources, transparencyGroup, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (PDFException exception)
|
||||||
|
{
|
||||||
|
errors.push_back(PDFRenderError(RenderErrorType::Error, exception.getMessage()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
catch (PDFRendererException exception)
|
||||||
|
{
|
||||||
|
errors.push_back(exception.getError());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_fontCache->setCacheShrinkEnabled(this, true);
|
m_fontCache->setCacheShrinkEnabled(this, true);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
213
PdfForQtLib/sources/pdfform.cpp
Normal file
213
PdfForQtLib/sources/pdfform.cpp
Normal 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
|
322
PdfForQtLib/sources/pdfform.h
Normal file
322
PdfForQtLib/sources/pdfform.h
Normal 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
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,8 +443,15 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user