Rendering pdf to images (first part)

This commit is contained in:
Jakub Melka
2020-02-09 16:06:29 +01:00
parent a1d270e2ab
commit 15805d80af
10 changed files with 510 additions and 18 deletions

View File

@ -393,7 +393,8 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
m_compiler(new PDFAsynchronousPageCompiler(this)), m_compiler(new PDFAsynchronousPageCompiler(this)),
m_textLayoutCompiler(new PDFAsynchronousTextLayoutCompiler(this)), m_textLayoutCompiler(new PDFAsynchronousTextLayoutCompiler(this)),
m_rasterizer(new PDFRasterizer(this)), m_rasterizer(new PDFRasterizer(this)),
m_progress(nullptr) m_progress(nullptr),
m_useOpenGL(false)
{ {
m_controller = new PDFDrawSpaceController(this); m_controller = new PDFDrawSpaceController(this);
connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update); connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update);
@ -1117,6 +1118,8 @@ bool PDFDrawWidgetProxy::isBlockMode() const
void PDFDrawWidgetProxy::updateRenderer(bool useOpenGL, const QSurfaceFormat& surfaceFormat) void PDFDrawWidgetProxy::updateRenderer(bool useOpenGL, const QSurfaceFormat& surfaceFormat)
{ {
m_useOpenGL = useOpenGL;
m_surfaceFormat = surfaceFormat;
m_rasterizer->reset(useOpenGL, surfaceFormat); m_rasterizer->reset(useOpenGL, surfaceFormat);
} }

View File

@ -320,6 +320,8 @@ public:
void setProgress(PDFProgress* progress) { m_progress = progress; } void setProgress(PDFProgress* progress) { m_progress = progress; }
PDFAsynchronousTextLayoutCompiler* getTextLayoutCompiler() const { return m_textLayoutCompiler; } PDFAsynchronousTextLayoutCompiler* getTextLayoutCompiler() const { return m_textLayoutCompiler; }
PDFWidget* getWidget() const { return m_widget; } PDFWidget* getWidget() const { return m_widget; }
bool isUsingOpenGL() const { return m_useOpenGL; }
const QSurfaceFormat& getSurfaceFormat() const { return m_surfaceFormat; }
void setFeatures(PDFRenderer::Features features); void setFeatures(PDFRenderer::Features features);
void setPreferredMeshResolutionRatio(PDFReal ratio); void setPreferredMeshResolutionRatio(PDFReal ratio);
@ -470,6 +472,12 @@ private:
/// Additional drawing interfaces /// Additional drawing interfaces
std::set<IDocumentDrawInterface*> m_drawInterfaces; std::set<IDocumentDrawInterface*> m_drawInterfaces;
/// Use OpenGL for rendering?
bool m_useOpenGL;
/// Surface format for OpenGL
QSurfaceFormat m_surfaceFormat;
}; };
} // namespace pdf } // namespace pdf

View File

@ -44,7 +44,8 @@ enum RenderErrorType
Error, Error,
Warning, Warning,
NotImplemented, NotImplemented,
NotSupported NotSupported,
Information
}; };
struct PDFRenderError struct PDFRenderError

View File

@ -18,7 +18,10 @@
#include "pdfrenderer.h" #include "pdfrenderer.h"
#include "pdfpainter.h" #include "pdfpainter.h"
#include "pdfdocument.h" #include "pdfdocument.h"
#include "pdfexecutionpolicy.h"
#include "pdfprogress.h"
#include <QDir>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QOffscreenSurface> #include <QOffscreenSurface>
@ -336,10 +339,120 @@ void PDFRasterizer::releaseOpenGL()
} }
} }
PDFRasterizer* PDFRasterizerPool::acquire()
{
m_semaphore.acquire();
QMutexLocker guard(&m_mutex);
Q_ASSERT(!m_rasterizers.empty());
PDFRasterizer* rasterizer = m_rasterizers.back();
m_rasterizers.pop_back();
return rasterizer;
}
void PDFRasterizerPool::release(pdf::PDFRasterizer* rasterizer)
{
QMutexLocker guard(&m_mutex);
Q_ASSERT(std::find(m_rasterizers.cbegin(), m_rasterizers.cend(), rasterizer) == m_rasterizers.cend());
m_rasterizers.push_back(rasterizer);
// Jakub Melka: we must release it at the end, to ensure rasterizer is in the array before
// semaphore is released, to avoid race condition.
m_semaphore.release();
}
void PDFRasterizerPool::render(const std::vector<PDFInteger>& pageIndices,
const PDFRasterizerPool::PageImageSizeGetter& imageSizeGetter,
const PDFRasterizerPool::ProcessImageMethod& processImage,
PDFProgress* progress)
{
if (pageIndices.empty())
{
return;
}
Q_ASSERT(imageSizeGetter);
Q_ASSERT(processImage);
QElapsedTimer timer;
timer.start();
emit renderError(PDFRenderError(RenderErrorType::Information, PDFTranslationContext::tr("Start at %1...").arg(QTime::currentTime().toString(Qt::TextDate))));
if (progress)
{
ProgressStartupInfo info;
info.showDialog = true;
info.text = PDFTranslationContext::tr("Rendering document into images.");
progress->start(pageIndices.size(), qMove(info));
}
auto processPage = [this, progress, &imageSizeGetter, &processImage](const PDFInteger pageIndex)
{
const PDFPage* page = m_document->getCatalog()->getPage(pageIndex);
if (!page)
{
if (progress)
{
progress->step();
}
emit renderError(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Page %1 not found.").arg(pageIndex)));
return;
}
// Precompile the page
PDFPrecompiledPage precompiledPage;
PDFRenderer renderer(m_document, m_fontCache, m_cms, m_optionalContentActivity, m_features, m_meshQualitySettings);
renderer.compile(&precompiledPage, pageIndex);
for (const PDFRenderError error : precompiledPage.getErrors())
{
emit renderError(PDFRenderError(error.type, PDFTranslationContext::tr("Page %1: %2").arg(pageIndex + 1).arg(error.message)));
}
// Render page to image
PDFRasterizer* rasterizer = acquire();
QImage image = rasterizer->render(page, &precompiledPage, imageSizeGetter(page), m_features);
release(rasterizer);
// Now, process the image
processImage(pageIndex, qMove(image));
if (progress)
{
progress->step();
}
};
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, pageIndices.cbegin(), pageIndices.cend(), processPage);
if (progress)
{
progress->finish();
}
emit renderError(PDFRenderError(RenderErrorType::Information, PDFTranslationContext::tr("Finished at %1...").arg(QTime::currentTime().toString(Qt::TextDate))));
emit renderError(PDFRenderError(RenderErrorType::Information, PDFTranslationContext::tr("%1 miliseconds elapsed to render %2 pages...").arg(timer.nsecsElapsed() / 1000000).arg(pageIndices.size())));
}
int PDFRasterizerPool::getDefaultRasterizerCount()
{
int hint = QThread::idealThreadCount() / 2;
return qBound(1, hint, 16);
}
PDFImageWriterSettings::PDFImageWriterSettings() PDFImageWriterSettings::PDFImageWriterSettings()
{ {
m_formats = QImageWriter::supportedImageFormats(); m_formats = QImageWriter::supportedImageFormats();
selectFormat(m_formats.front());
constexpr const char* DEFAULT_FORMAT = "png";
if (m_formats.count(DEFAULT_FORMAT))
{
selectFormat(DEFAULT_FORMAT);
}
else
{
selectFormat(m_formats.front());
}
} }
void PDFImageWriterSettings::selectFormat(const QByteArray& format) void PDFImageWriterSettings::selectFormat(const QByteArray& format)
@ -460,7 +573,8 @@ void PDFImageWriterSettings::setCurrentSubtype(const QByteArray& currentSubtype)
m_currentSubtype = currentSubtype; m_currentSubtype = currentSubtype;
} }
PDFPageImageExportSettings::PDFPageImageExportSettings() PDFPageImageExportSettings::PDFPageImageExportSettings(const PDFDocument* document) :
m_document(document)
{ {
m_fileTemplate = PDFTranslationContext::tr("Image_%"); m_fileTemplate = PDFTranslationContext::tr("Image_%");
} }
@ -535,4 +649,174 @@ void PDFPageImageExportSettings::setPixelResolution(int pixelResolution)
m_pixelResolution = pixelResolution; m_pixelResolution = pixelResolution;
} }
bool PDFPageImageExportSettings::validate(QString* errorMessagePtr)
{
QString dummy;
QString& errorMessage = errorMessagePtr ? *errorMessagePtr : dummy;
if (m_directory.isEmpty())
{
errorMessage = PDFTranslationContext::tr("Target directory is empty.");
return false;
}
// Check, if target directory exists
QDir directory(m_directory);
if (!directory.exists())
{
errorMessage = PDFTranslationContext::tr("Target directory '%1' doesn't exist.").arg(m_directory);
return false;
}
if (m_fileTemplate.isEmpty())
{
errorMessage = PDFTranslationContext::tr("File template is empty.");
return false;
}
if (!m_fileTemplate.contains("%"))
{
errorMessage = PDFTranslationContext::tr("File template must contain character '%' for page number.");
return false;
}
// Check page selection
if (m_pageSelectionMode == PageSelectionMode::Selection)
{
std::vector<PDFInteger> pages = getPages();
if (pages.empty())
{
errorMessage = PDFTranslationContext::tr("Page list is invalid. It should have form such as '1-12,17,24,27-29'.");
return false;
}
if (pages.back() >= PDFInteger(m_document->getCatalog()->getPageCount()))
{
errorMessage = PDFTranslationContext::tr("Page list contains page, which is not in the document (%1).").arg(pages.back());
return false;
}
}
if (m_resolutionMode == ResolutionMode::DPI && (m_dpiResolution < getMinDPIResolution() || m_dpiResolution > getMaxDPIResolution()))
{
errorMessage = PDFTranslationContext::tr("DPI resolution should be in range %1 to %2.").arg(getMinDPIResolution()).arg(getMaxDPIResolution());
return false;
}
if (m_resolutionMode == ResolutionMode::Pixels && (m_pixelResolution < getMinPixelResolution() || m_pixelResolution > getMaxPixelResolution()))
{
errorMessage = PDFTranslationContext::tr("Pixel resolution should be in range %1 to %2.").arg(getMinPixelResolution()).arg(getMaxPixelResolution());
return false;
}
return true;
}
std::vector<PDFInteger> PDFPageImageExportSettings::getPages() const
{
std::vector<PDFInteger> result;
switch (m_pageSelectionMode)
{
case PageSelectionMode::All:
{
result.resize(m_document->getCatalog()->getPageCount(), 0);
std::iota(result.begin(), result.end(), 0);
break;
}
case PageSelectionMode::Selection:
{
bool ok = false;
QStringList parts = m_pageSelection.split(QChar(','), Qt::SkipEmptyParts, Qt::CaseSensitive);
for (const QString& part : parts)
{
QStringList numbers = part.split(QChar('-'), Qt::KeepEmptyParts, Qt::CaseSensitive);
switch (numbers.size())
{
case 1:
{
const QString& numberString = numbers.front();
result.push_back(numberString.toLongLong(&ok));
break;
}
case 2:
{
bool ok1 = false;
bool ok2 = false;
const QString& lowString = numbers.front();
const QString& highString = numbers.back();
const PDFInteger low = lowString.toLongLong(&ok1);
const PDFInteger high = highString.toLongLong(&ok2);
ok = ok1 && ok2 && low <= high;
if (ok)
{
const PDFInteger count = high - low + 1;
result.resize(result.size() + count, 0);
std::iota(std::prev(result.end(), count), result.end(), low);
}
break;
}
default:
{
ok = true;
break;
}
}
// If error is detected, do not continue in parsing
if (!ok)
{
break;
}
// We must remove duplicate pages
qSort(result);
result.erase(std::unique(result.begin(), result.end()), result.end());
}
if (!ok)
{
result.clear();
}
break;
}
default:
break;
}
return result;
}
PDFRasterizerPool::PDFRasterizerPool(const PDFDocument* document,
const PDFFontCache* fontCache,
const PDFCMS* cms,
const PDFOptionalContentActivity* optionalContentActivity,
PDFRenderer::Features features,
const PDFMeshQualitySettings& meshQualitySettings,
int rasterizerCount,
bool useOpenGL,
const QSurfaceFormat& surfaceFormat,
QObject* parent) :
BaseClass(parent),
m_document(document),
m_fontCache(fontCache),
m_cms(cms),
m_optionalContentActivity(optionalContentActivity),
m_features(features),
m_meshQualitySettings(meshQualitySettings),
m_semaphore(rasterizerCount)
{
m_rasterizers.reserve(rasterizerCount);
for (int i = 0; i < rasterizerCount; ++i)
{
m_rasterizers.push_back(new PDFRasterizer(this));
m_rasterizers.back()->reset(useOpenGL, surfaceFormat);
}
}
} // namespace pdf } // namespace pdf

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Jakub Melka // Copyright (C) 2019-2020 Jakub Melka
// //
// This file is part of PdfForQt. // This file is part of PdfForQt.
// //
@ -22,6 +22,8 @@
#include "pdfexception.h" #include "pdfexception.h"
#include "pdfmeshqualitysettings.h" #include "pdfmeshqualitysettings.h"
#include <QMutex>
#include <QSemaphore>
#include <QImageWriter> #include <QImageWriter>
#include <QSurfaceFormat> #include <QSurfaceFormat>
@ -33,6 +35,7 @@ class QOpenGLFramebufferObject;
namespace pdf namespace pdf
{ {
class PDFCMS; class PDFCMS;
class PDFProgress;
class PDFFontCache; class PDFFontCache;
class PDFPrecompiledPage; class PDFPrecompiledPage;
class PDFOptionalContentActivity; class PDFOptionalContentActivity;
@ -105,6 +108,7 @@ private:
/// Renders PDF pages to bitmap images (QImage). It can use OpenGL for painting, /// 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 /// if it is enabled, if this is the case, offscreen rendering to framebuffer
/// is used. /// is used.
/// \note Construct this object only in main GUI thread
class PDFRasterizer : public QObject class PDFRasterizer : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -114,7 +118,7 @@ private:
public: public:
explicit PDFRasterizer(QObject* parent); explicit PDFRasterizer(QObject* parent);
~PDFRasterizer(); virtual ~PDFRasterizer() override;
/// Resets the renderer. This function must be called from main GUI thread, /// Resets the renderer. This function must be called from main GUI thread,
/// it cannot be called from deferred threads, because it can create hidden /// it cannot be called from deferred threads, because it can create hidden
@ -155,6 +159,83 @@ private:
QOpenGLFramebufferObject* m_fbo; QOpenGLFramebufferObject* m_fbo;
}; };
/// 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 PDFFORQTLIBSHARED_EXPORT PDFRasterizerPool : public QObject
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
using PageImageSizeGetter = std::function<QSize(const PDFPage*)>;
using ProcessImageMethod = std::function<void(PDFInteger, QImage&&)>;
/// Creates new rasterizer pool
/// \param document Document
/// \param fontCache Font cache
/// \param cms Color management system
/// \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,
const PDFFontCache* fontCache,
const PDFCMS* cms,
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();
signals:
void renderError(PDFRenderError error);
private:
const PDFDocument* m_document;
const PDFFontCache* m_fontCache;
const PDFCMS* m_cms;
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 /// Settings object for image writer
class PDFFORQTLIBSHARED_EXPORT PDFImageWriterSettings class PDFFORQTLIBSHARED_EXPORT PDFImageWriterSettings
{ {
@ -211,7 +292,7 @@ private:
class PDFFORQTLIBSHARED_EXPORT PDFPageImageExportSettings class PDFFORQTLIBSHARED_EXPORT PDFPageImageExportSettings
{ {
public: public:
explicit PDFPageImageExportSettings(); explicit PDFPageImageExportSettings(const PDFDocument* document);
enum class PageSelectionMode enum class PageSelectionMode
{ {
@ -246,7 +327,20 @@ public:
int getPixelResolution() const; int getPixelResolution() const;
void setPixelResolution(int pixelResolution); void setPixelResolution(int pixelResolution);
/// Validates the settings, if they can be used for image generation
bool validate(QString* errorMessagePtr);
/// Returns list of selected pages
std::vector<PDFInteger> getPages() 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: private:
const PDFDocument* m_document;
ResolutionMode m_resolutionMode = ResolutionMode::DPI; ResolutionMode m_resolutionMode = ResolutionMode::DPI;
PageSelectionMode m_pageSelectionMode = PageSelectionMode::All; PageSelectionMode m_pageSelectionMode = PageSelectionMode::All;
QString m_directory; QString m_directory;

View File

@ -25,6 +25,7 @@ int main(int argc, char *argv[])
{ {
QApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents, true); QApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents, true);
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true); QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity, true);
QApplication application(argc, argv); QApplication application(argc, argv);
QCoreApplication::setOrganizationName("MelkaJ"); QCoreApplication::setOrganizationName("MelkaJ");

View File

@ -18,17 +18,29 @@
#include "pdfrendertoimagesdialog.h" #include "pdfrendertoimagesdialog.h"
#include "ui_pdfrendertoimagesdialog.h" #include "ui_pdfrendertoimagesdialog.h"
#include "pdfcms.h"
#include "pdfutils.h" #include "pdfutils.h"
#include "pdfwidgetutils.h" #include "pdfwidgetutils.h"
#include "pdfoptionalcontent.h"
#include "pdfdrawspacecontroller.h"
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox>
#include <QPushButton>
namespace pdfviewer namespace pdfviewer
{ {
PDFRenderToImagesDialog::PDFRenderToImagesDialog(QWidget* parent) : PDFRenderToImagesDialog::PDFRenderToImagesDialog(const pdf::PDFDocument* document,
pdf::PDFDrawWidgetProxy* proxy,
pdf::PDFProgress* progress,
QWidget* parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::PDFRenderToImagesDialog), ui(new Ui::PDFRenderToImagesDialog),
m_document(document),
m_proxy(proxy),
m_progress(progress),
m_imageExportSettings(document),
m_isLoadingData(false) m_isLoadingData(false)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -56,6 +68,9 @@ PDFRenderToImagesDialog::PDFRenderToImagesDialog(QWidget* parent) :
connect(ui->optimizedWriteCheckBox, &QCheckBox::clicked, this, &PDFRenderToImagesDialog::onOptimizedWriteChanged); connect(ui->optimizedWriteCheckBox, &QCheckBox::clicked, this, &PDFRenderToImagesDialog::onOptimizedWriteChanged);
connect(ui->progressiveScanWriteCheckBox, &QCheckBox::clicked, this, &PDFRenderToImagesDialog::onProgressiveScanWriteChanged); connect(ui->progressiveScanWriteCheckBox, &QCheckBox::clicked, this, &PDFRenderToImagesDialog::onProgressiveScanWriteChanged);
ui->resolutionDPIEdit->setRange(pdf::PDFPageImageExportSettings::getMinDPIResolution(), pdf::PDFPageImageExportSettings::getMaxDPIResolution());
ui->resolutionPixelsEdit->setRange(pdf::PDFPageImageExportSettings::getMinPixelResolution(), pdf::PDFPageImageExportSettings::getMaxPixelResolution());
loadImageWriterSettings(); loadImageWriterSettings();
loadImageExportSettings(); loadImageExportSettings();
@ -219,6 +234,11 @@ void PDFRenderToImagesDialog::onProgressiveScanWriteChanged(bool value)
m_imageWriterSettings.setProgressiveScanWrite(value); m_imageWriterSettings.setProgressiveScanWrite(value);
} }
void PDFRenderToImagesDialog::onRenderError(pdf::PDFRenderError error)
{
ui->progressMessagesEdit->setPlainText(QString("%1\n%2").arg(ui->progressMessagesEdit->toPlainText()).arg(error.message));
}
void PDFRenderToImagesDialog::on_selectDirectoryButton_clicked() void PDFRenderToImagesDialog::on_selectDirectoryButton_clicked()
{ {
QString directory = QFileDialog::getExistingDirectory(this, tr("Select output directory"), ui->directoryEdit->text()); QString directory = QFileDialog::getExistingDirectory(this, tr("Select output directory"), ui->directoryEdit->text());
@ -228,6 +248,67 @@ void PDFRenderToImagesDialog::on_selectDirectoryButton_clicked()
} }
} }
void PDFRenderToImagesDialog::on_buttonBox_clicked(QAbstractButton* button)
{
if (button == ui->buttonBox->button(QDialogButtonBox::Apply))
{
QString message;
if (m_imageExportSettings.validate(&message))
{
// We are ready to render the document
std::vector<pdf::PDFInteger> pageIndices = m_imageExportSettings.getPages();
pdf::PDFOptionalContentActivity optionalContentActivity(m_document, pdf::OCUsage::Export, nullptr);
pdf::PDFCMSPointer cms = m_proxy->getCMSManager()->getCurrentCMS();
pdf::PDFRasterizerPool rasterizerPool(m_document, m_proxy->getFontCache(), cms.data(),
&optionalContentActivity, m_proxy->getFeatures(), m_proxy->getMeshQualitySettings(),
pdf::PDFRasterizerPool::getDefaultRasterizerCount(), m_proxy->isUsingOpenGL(), m_proxy->getSurfaceFormat(), this);
connect(&rasterizerPool, &pdf::PDFRasterizerPool::renderError, this, &PDFRenderToImagesDialog::onRenderError);
auto imageSizeGetter = [this](const pdf::PDFPage* page) -> QSize
{
Q_ASSERT(page);
switch (m_imageExportSettings.getResolutionMode())
{
case pdf::PDFPageImageExportSettings::ResolutionMode::DPI:
{
QSizeF size = page->getRotatedMediaBox().size() * m_imageExportSettings.getDpiResolution();
return size.toSize();
}
case pdf::PDFPageImageExportSettings::ResolutionMode::Pixels:
{
int pixelResolution = m_imageExportSettings.getPixelResolution();
QSizeF size = page->getRotatedMediaBox().size().scaled(pixelResolution, pixelResolution, Qt::KeepAspectRatio);
return size.toSize();
}
default:
{
Q_ASSERT(false);
break;
}
}
return QSize();
};
auto processImage = [](const pdf::PDFInteger pageIndex, QImage&& image)
{
Q_UNUSED(pageIndex);
Q_UNUSED(image);
};
setEnabled(false);
rasterizerPool.render(pageIndices, imageSizeGetter, processImage, m_progress);
setEnabled(true);
}
else
{
QMessageBox::critical(this, tr("Error"), message);
}
}
}
} // namespace pdfviewer } // namespace pdfviewer

View File

@ -22,11 +22,19 @@
#include <QDialog> #include <QDialog>
class QAbstractButton;
namespace Ui namespace Ui
{ {
class PDFRenderToImagesDialog; class PDFRenderToImagesDialog;
} }
namespace pdf
{
class PDFProgress;
class PDFDrawWidgetProxy;
}
namespace pdfviewer namespace pdfviewer
{ {
@ -35,11 +43,15 @@ class PDFRenderToImagesDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit PDFRenderToImagesDialog(QWidget* parent); explicit PDFRenderToImagesDialog(const pdf::PDFDocument* document,
pdf::PDFDrawWidgetProxy* proxy,
pdf::PDFProgress* progress,
QWidget* parent);
virtual ~PDFRenderToImagesDialog() override; virtual ~PDFRenderToImagesDialog() override;
private slots: private slots:
void on_selectDirectoryButton_clicked(); void on_selectDirectoryButton_clicked();
void on_buttonBox_clicked(QAbstractButton* button);
private: private:
/// Loads image writer settings to the ui /// Loads image writer settings to the ui
@ -62,8 +74,12 @@ private:
void onGammaChanged(double value); void onGammaChanged(double value);
void onOptimizedWriteChanged(bool value); void onOptimizedWriteChanged(bool value);
void onProgressiveScanWriteChanged(bool value); void onProgressiveScanWriteChanged(bool value);
void onRenderError(pdf::PDFRenderError error);
Ui::PDFRenderToImagesDialog* ui; Ui::PDFRenderToImagesDialog* ui;
const pdf::PDFDocument* m_document;
pdf::PDFDrawWidgetProxy* m_proxy;
pdf::PDFProgress* m_progress;
pdf::PDFImageWriterSettings m_imageWriterSettings; pdf::PDFImageWriterSettings m_imageWriterSettings;
pdf::PDFPageImageExportSettings m_imageExportSettings; pdf::PDFPageImageExportSettings m_imageExportSettings;
bool m_isLoadingData; bool m_isLoadingData;

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>690</width> <width>690</width>
<height>593</height> <height>602</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -234,7 +234,14 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPlainTextEdit" name="plainTextEdit"/> <widget class="QPlainTextEdit" name="progressMessagesEdit">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">

View File

@ -1279,11 +1279,8 @@ void PDFViewerMainWindow::on_actionPrint_triggered()
void PDFViewerMainWindow::on_actionRender_to_Images_triggered() void PDFViewerMainWindow::on_actionRender_to_Images_triggered()
{ {
PDFRenderToImagesDialog dialog(this); PDFRenderToImagesDialog dialog(m_pdfDocument.data(), m_pdfWidget->getDrawWidgetProxy(), m_progress, this);
if (dialog.exec() == QDialog::Accepted) dialog.exec();
{
}
} }
} // namespace pdfviewer } // namespace pdfviewer