PDF4QT/Pdf4QtLib/sources/pdfrenderer.h
2022-02-04 20:03:23 +01:00

410 lines
16 KiB
C++

// Copyright (C) 2019-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT 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
// with the written consent of the copyright owner, any later version.
//
// PDF4QT 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 PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFRENDERER_H
#define PDFRENDERER_H
#include "pdfpage.h"
#include "pdfexception.h"
#include "pdfoperationcontrol.h"
#include "pdfmeshqualitysettings.h"
#include <QMutex>
#include <QSemaphore>
#include <QImageWriter>
#include <QSurfaceFormat>
#include <QImage>
class QPainter;
class QOpenGLContext;
class QOffscreenSurface;
class QOpenGLFramebufferObject;
namespace pdf
{
class PDFCMS;
class PDFProgress;
class PDFFontCache;
class PDFCMSManager;
class PDFPrecompiledPage;
class PDFAnnotationManager;
class PDFOptionalContentActivity;
/// Renders the PDF page on the painter, or onto an image.
class PDF4QTLIBSHARED_EXPORT PDFRenderer
{
public:
enum Feature
{
None = 0x0000,
Antialiasing = 0x0001, ///< Antialiasing for lines, shapes, etc.
TextAntialiasing = 0x0002, ///< Antialiasing for drawing text
SmoothImages = 0x0004, ///< Adjust images to the device space using smooth transformation (slower, but better image quality)
IgnoreOptionalContent = 0x0008, ///< Ignore optional content (so all is drawn ignoring settings of optional content)
ClipToCropBox = 0x0010, ///< Clip page content to crop box (items outside crop box will not be visible)
DisplayTimes = 0x0020, ///< Display page compile/draw time
DebugTextBlocks = 0x0040, ///< Debug text block layout algorithm
DebugTextLines = 0x0080, ///< Debug text line layout algorithm
InvertColors = 0x0100, ///< Invert colors
DenyExtraGraphics = 0x0200, ///< Do not display additional graphics, for example from tools
DisplayAnnotations = 0x0400, ///< Display annotations
};
Q_DECLARE_FLAGS(Features, Feature)
explicit PDFRenderer(const PDFDocument* document,
const PDFFontCache* fontCache,
const PDFCMS* cms,
const PDFOptionalContentActivity* optionalContentActivity,
Features features,
const PDFMeshQualitySettings& meshQualitySettings);
/// Paints desired page onto the painter. Page is painted in the rectangle using best-fit method.
/// If the page doesn't exist, then error is returned. No exception is thrown. Rendering errors
/// are reported and returned in the error list. If no error occured, empty list is returned.
/// \param painter Painter
/// \param rectangle Paint area for the page
/// \param pageIndex Index of the page to be painted
QList<PDFRenderError> render(QPainter* painter, const QRectF& rectangle, size_t pageIndex) const;
/// Paints desired page onto the painter. Page is painted using \p matrix, which maps page coordinates
/// to the device coordinates. If the page doesn't exist, then error is returned. No exception is thrown.
/// Rendering errors are reported and returned in the error list. If no error occured, empty list is returned.
QList<PDFRenderError> render(QPainter* painter, const QMatrix& matrix, size_t pageIndex) const;
/// Compiles page (i.e. prepares compiled page). \p page should be empty page, onto which
/// are graphics commands written. No exception is thrown. Rendering errors are reported and written
/// to the compiled page.
/// \param precompiledPage Precompiled page pointer
/// \param pageIndex Index of page to be compiled
void compile(PDFPrecompiledPage* precompiledPage, size_t pageIndex) const;
/// Creates page point to device point matrix for the given rectangle. It creates transformation
/// from page's media box to the target rectangle.
/// \param page Page, for which we want to create matrix
/// \param rectangle Page rectangle, to which is page media box transformed
/// \param extraRotation Extra rotation applied to the page rotation
static QMatrix createPagePointToDevicePointMatrix(const PDFPage* page,
const QRectF& rectangle,
PageRotation extraRotation = PageRotation::None);
/// Creates media box to device point matrix for the given media box.
/// \param mediaBox Media box
/// \param rectangle Page rectangle, to which is page media box transformed
/// \param rotation Rotation
static QMatrix createMediaBoxToDevicePointMatrix(const QRectF& mediaBox,
const QRectF& rectangle,
PageRotation rotation);
/// Returns default renderer features
static constexpr Features getDefaultFeatures() { return Features(Antialiasing | TextAntialiasing | ClipToCropBox | DisplayAnnotations); }
const PDFOperationControl* getOperationControl() const;
void setOperationControl(const PDFOperationControl* newOperationControl);
private:
const PDFDocument* m_document;
const PDFFontCache* m_fontCache;
const PDFCMS* m_cms;
const PDFOptionalContentActivity* m_optionalContentActivity;
const PDFOperationControl* m_operationControl;
Features m_features;
PDFMeshQualitySettings m_meshQualitySettings;
};
/// Renders PDF pages to bitmap images (QImage). It can use OpenGL for painting,
/// if it is enabled, if this is the case, offscreen rendering to framebuffer
/// is used.
/// \note Construct this object only in main GUI thread
class PDF4QTLIBSHARED_EXPORT PDFRasterizer : public QObject
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
explicit PDFRasterizer(QObject* parent);
virtual ~PDFRasterizer() override;
/// Resets the renderer. This function must be called from main GUI thread,
/// it cannot be called from deferred threads, because it can create hidden
/// window (offscreen surface).
/// \param useOpenGL Use OpenGL for rendering
/// \param surfaceFormat Surface format to render
void reset(bool useOpenGL, const QSurfaceFormat& surfaceFormat);
enum Feature
{
UseOpenGL = 0x0001, ///< Use OpenGL for rendering
ValidOpenGL = 0x0002, ///< OpenGL is initialized and valid
FailedOpenGL = 0x0004, ///< OpenGL creation has failed
};
Q_DECLARE_FLAGS(Features, Feature)
/// Renders page to the image of given size. If some error occurs, then
/// empty image is returned. Warning: this function can modify this object,
/// so it is not const and is not thread safe. We can also draw annotations,
/// if \p annotationManager is not nullptr and annotations are enabled.
/// \param pageIndex Page index
/// \param page Page
/// \param compiledPage Compiled page contents
/// \param size Size of the target image
/// \param features Renderer features
/// \param annotationManager Annotation manager (can be nullptr)
/// \param extraRotation Extra page rotation
QImage render(PDFInteger pageIndex,
const PDFPage* page,
const PDFPrecompiledPage* compiledPage,
QSize size,
PDFRenderer::Features features,
const PDFAnnotationManager* annotationManager,
PageRotation extraRotation);
private:
void initializeOpenGL();
void releaseOpenGL();
Features m_features;
QSurfaceFormat m_surfaceFormat;
QOffscreenSurface* m_surface;
QOpenGLContext* m_context;
QOpenGLFramebufferObject* m_fbo;
};
/// Simple structure for storing rendered page images
struct PDFRenderedPageImage
{
qint64 pageCompileTime = 0;
qint64 pageWaitTime = 0;
qint64 pageRenderTime = 0;
qint64 pageTotalTime = 0;
PDFInteger pageIndex;
QImage pageImage;
};
/// Pool of page image renderers. It can use predefined number of renderers to
/// render page images asynchronously. You can use this object in two ways -
/// first one is as standard object pool, second one is to directly render
/// page images asynchronously.
class PDF4QTLIBSHARED_EXPORT PDFRasterizerPool : public QObject
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
using PageImageSizeGetter = std::function<QSize(const PDFPage*)>;
using ProcessImageMethod = std::function<void(PDFRenderedPageImage&)>;
/// Creates new rasterizer pool
/// \param document Document
/// \param fontCache Font cache
/// \param cmsManager Color management system manager
/// \param optionalContentActivity Optional content activity
/// \param features Renderer features
/// \param meshQualitySettings Mesh quality settings
/// \param rasterizerCount Number of rasterizers
/// \param useOpenGL Use OpenGL for rendering?
/// \param surfaceFormat Surface format
/// \param parent Parent object
explicit PDFRasterizerPool(const PDFDocument* document,
PDFFontCache* fontCache,
const PDFCMSManager* cmsManager,
const PDFOptionalContentActivity* optionalContentActivity,
PDFRenderer::Features features,
const PDFMeshQualitySettings& meshQualitySettings,
int rasterizerCount,
bool useOpenGL,
const QSurfaceFormat& surfaceFormat,
QObject* parent);
/// Acquire rasterizer. This function is thread safe.
PDFRasterizer* acquire();
/// Return back (release) rasterizer into rasterizer pool
/// This function is thread safe.
/// \param rasterizer Rasterizer
void release(PDFRasterizer* rasterizer);
/// Renders pages asynchronously to images, using given page indices,
/// function which returns rendered size and process image function,
/// which processes rendered images.
/// \param pageIndices Page indices for rendered pages
/// \param imageSizeGetter Getter, which computes image size from page index
/// \param processImage Method, which processes rendered page images
/// \param progress Progress indicator
void render(const std::vector<PDFInteger>& pageIndices,
const PageImageSizeGetter& imageSizeGetter,
const ProcessImageMethod& processImage,
PDFProgress* progress);
/// Returns default rasterizer count
static int getDefaultRasterizerCount();
/// Returns corrected rasterizer count (so, if user
/// select too high or too low rasterizer count, this function
/// corrects it to acceptable number.
/// \param rasterizerCount Requested number of rasterizers
/// \returns Corrected number of rasterizers
static int getCorrectedRasterizerCount(int rasterizerCount);
signals:
void renderError(PDFInteger pageIndex, PDFRenderError error);
private:
const PDFDocument* m_document;
PDFFontCache* m_fontCache;
const PDFCMSManager* m_cmsManager;
const PDFOptionalContentActivity* m_optionalContentActivity;
PDFRenderer::Features m_features;
const PDFMeshQualitySettings& m_meshQualitySettings;
QSemaphore m_semaphore;
QMutex m_mutex;
std::vector<PDFRasterizer*> m_rasterizers;
};
/// Settings object for image writer
class PDF4QTLIBSHARED_EXPORT PDFImageWriterSettings
{
public:
explicit PDFImageWriterSettings();
/// Returns true, if image option is supported
bool isOptionSupported(QImageIOHandler::ImageOption option) const { return m_supportedOptions.count(option); }
/// Returns a list of available image formats
const QList<QByteArray>& getFormats() const { return m_formats; }
/// Returns a list of available subtypes
const QList<QByteArray>& getSubtypes() const { return m_subtypes; }
/// Selects image format (and initializes default values)
void selectFormat(const QByteArray& format);
int getCompression() const;
void setCompression(int compression);
int getQuality() const;
void setQuality(int quality);
float getGamma() const;
void setGamma(float gamma);
bool hasOptimizedWrite() const;
void setOptimizedWrite(bool optimizedWrite);
bool hasProgressiveScanWrite() const;
void setProgressiveScanWrite(bool progressiveScanWrite);
QByteArray getCurrentFormat() const;
QByteArray getCurrentSubtype() const;
void setCurrentSubtype(const QByteArray& currentSubtype);
private:
int m_compression = 9;
int m_quality = 100;
float m_gamma = 1.0;
bool m_optimizedWrite = false;
bool m_progressiveScanWrite = false;
QByteArray m_currentFormat;
QByteArray m_currentSubtype;
std::set<QImageIOHandler::ImageOption> m_supportedOptions;
QList<QByteArray> m_formats;
QList<QByteArray> m_subtypes;
};
/// This class is for setup of page image exporter
class PDF4QTLIBSHARED_EXPORT PDFPageImageExportSettings
{
public:
explicit PDFPageImageExportSettings() : PDFPageImageExportSettings(nullptr) { }
explicit PDFPageImageExportSettings(const PDFDocument* document);
enum class PageSelectionMode
{
All,
Selection
};
enum class ResolutionMode
{
DPI,
Pixels
};
ResolutionMode getResolutionMode() const;
void setResolutionMode(ResolutionMode resolution);
PageSelectionMode getPageSelectionMode() const;
void setPageSelectionMode(PageSelectionMode pageSelectionMode);
QString getDirectory() const;
void setDirectory(const QString& directory);
QString getFileTemplate() const;
void setFileTemplate(const QString& fileTemplate);
QString getPageSelection() const;
void setPageSelection(const QString& pageSelection);
int getDpiResolution() const;
void setDpiResolution(int dpiResolution);
int getPixelResolution() const;
void setPixelResolution(int pixelResolution);
/// Validates the settings, if they can be used for image generation
bool validate(QString* errorMessagePtr, bool validatePageSelection = true, bool validateFileSettings = true, bool validateResolution = true) const;
/// Returns list of selected pages
std::vector<PDFInteger> getPages() const;
/// Returns output file name for given page
QString getOutputFileName(PDFInteger pageIndex, const QByteArray& outputFormat) const;
static constexpr int getMinDPIResolution() { return 72; }
static constexpr int getMaxDPIResolution() { return 6000; }
static constexpr int getMinPixelResolution() { return 100; }
static constexpr int getMaxPixelResolution() { return 16384; }
private:
const PDFDocument* m_document;
ResolutionMode m_resolutionMode = ResolutionMode::DPI;
PageSelectionMode m_pageSelectionMode = PageSelectionMode::All;
QString m_directory;
QString m_fileTemplate;
QString m_pageSelection;
int m_dpiResolution = 300;
int m_pixelResolution = 100;
};
} // namespace pdf
Q_DECLARE_OPERATORS_FOR_FLAGS(pdf::PDFRenderer::Features)
#endif // PDFRENDERER_H