mirror of https://github.com/JakubMelka/PDF4QT.git
405 lines
15 KiB
C++
405 lines
15 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 "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); }
|
|
|
|
private:
|
|
const PDFDocument* m_document;
|
|
const PDFFontCache* m_fontCache;
|
|
const PDFCMS* m_cms;
|
|
const PDFOptionalContentActivity* m_optionalContentActivity;
|
|
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
|