mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2024-12-27 08:42:48 +01:00
Rendering pdf to images (first part)
This commit is contained in:
parent
a1d270e2ab
commit
15805d80af
@ -393,7 +393,8 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
|
||||
m_compiler(new PDFAsynchronousPageCompiler(this)),
|
||||
m_textLayoutCompiler(new PDFAsynchronousTextLayoutCompiler(this)),
|
||||
m_rasterizer(new PDFRasterizer(this)),
|
||||
m_progress(nullptr)
|
||||
m_progress(nullptr),
|
||||
m_useOpenGL(false)
|
||||
{
|
||||
m_controller = new PDFDrawSpaceController(this);
|
||||
connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update);
|
||||
@ -1117,6 +1118,8 @@ bool PDFDrawWidgetProxy::isBlockMode() const
|
||||
|
||||
void PDFDrawWidgetProxy::updateRenderer(bool useOpenGL, const QSurfaceFormat& surfaceFormat)
|
||||
{
|
||||
m_useOpenGL = useOpenGL;
|
||||
m_surfaceFormat = surfaceFormat;
|
||||
m_rasterizer->reset(useOpenGL, surfaceFormat);
|
||||
}
|
||||
|
||||
|
@ -320,6 +320,8 @@ public:
|
||||
void setProgress(PDFProgress* progress) { m_progress = progress; }
|
||||
PDFAsynchronousTextLayoutCompiler* getTextLayoutCompiler() const { return m_textLayoutCompiler; }
|
||||
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 setPreferredMeshResolutionRatio(PDFReal ratio);
|
||||
@ -470,6 +472,12 @@ private:
|
||||
|
||||
/// Additional drawing interfaces
|
||||
std::set<IDocumentDrawInterface*> m_drawInterfaces;
|
||||
|
||||
/// Use OpenGL for rendering?
|
||||
bool m_useOpenGL;
|
||||
|
||||
/// Surface format for OpenGL
|
||||
QSurfaceFormat m_surfaceFormat;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -44,7 +44,8 @@ enum RenderErrorType
|
||||
Error,
|
||||
Warning,
|
||||
NotImplemented,
|
||||
NotSupported
|
||||
NotSupported,
|
||||
Information
|
||||
};
|
||||
|
||||
struct PDFRenderError
|
||||
|
@ -18,7 +18,10 @@
|
||||
#include "pdfrenderer.h"
|
||||
#include "pdfpainter.h"
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfexecutionpolicy.h"
|
||||
#include "pdfprogress.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QElapsedTimer>
|
||||
#include <QOpenGLContext>
|
||||
#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()
|
||||
{
|
||||
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)
|
||||
@ -460,7 +573,8 @@ void PDFImageWriterSettings::setCurrentSubtype(const QByteArray& currentSubtype)
|
||||
m_currentSubtype = currentSubtype;
|
||||
}
|
||||
|
||||
PDFPageImageExportSettings::PDFPageImageExportSettings()
|
||||
PDFPageImageExportSettings::PDFPageImageExportSettings(const PDFDocument* document) :
|
||||
m_document(document)
|
||||
{
|
||||
m_fileTemplate = PDFTranslationContext::tr("Image_%");
|
||||
}
|
||||
@ -535,4 +649,174 @@ void PDFPageImageExportSettings::setPixelResolution(int 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2020 Jakub Melka
|
||||
// Copyright (C) 2019-2020 Jakub Melka
|
||||
//
|
||||
// This file is part of PdfForQt.
|
||||
//
|
||||
@ -22,6 +22,8 @@
|
||||
#include "pdfexception.h"
|
||||
#include "pdfmeshqualitysettings.h"
|
||||
|
||||
#include <QMutex>
|
||||
#include <QSemaphore>
|
||||
#include <QImageWriter>
|
||||
#include <QSurfaceFormat>
|
||||
|
||||
@ -33,6 +35,7 @@ class QOpenGLFramebufferObject;
|
||||
namespace pdf
|
||||
{
|
||||
class PDFCMS;
|
||||
class PDFProgress;
|
||||
class PDFFontCache;
|
||||
class PDFPrecompiledPage;
|
||||
class PDFOptionalContentActivity;
|
||||
@ -105,6 +108,7 @@ private:
|
||||
/// 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 PDFRasterizer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -114,7 +118,7 @@ private:
|
||||
|
||||
public:
|
||||
explicit PDFRasterizer(QObject* parent);
|
||||
~PDFRasterizer();
|
||||
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
|
||||
@ -155,6 +159,83 @@ private:
|
||||
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
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFImageWriterSettings
|
||||
{
|
||||
@ -211,7 +292,7 @@ private:
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFPageImageExportSettings
|
||||
{
|
||||
public:
|
||||
explicit PDFPageImageExportSettings();
|
||||
explicit PDFPageImageExportSettings(const PDFDocument* document);
|
||||
|
||||
enum class PageSelectionMode
|
||||
{
|
||||
@ -246,7 +327,20 @@ public:
|
||||
int getPixelResolution() const;
|
||||
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:
|
||||
const PDFDocument* m_document;
|
||||
ResolutionMode m_resolutionMode = ResolutionMode::DPI;
|
||||
PageSelectionMode m_pageSelectionMode = PageSelectionMode::All;
|
||||
QString m_directory;
|
||||
|
@ -25,6 +25,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents, true);
|
||||
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
|
||||
QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity, true);
|
||||
QApplication application(argc, argv);
|
||||
|
||||
QCoreApplication::setOrganizationName("MelkaJ");
|
||||
|
@ -18,17 +18,29 @@
|
||||
#include "pdfrendertoimagesdialog.h"
|
||||
#include "ui_pdfrendertoimagesdialog.h"
|
||||
|
||||
#include "pdfcms.h"
|
||||
#include "pdfutils.h"
|
||||
#include "pdfwidgetutils.h"
|
||||
#include "pdfoptionalcontent.h"
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
|
||||
namespace pdfviewer
|
||||
{
|
||||
|
||||
PDFRenderToImagesDialog::PDFRenderToImagesDialog(QWidget* parent) :
|
||||
PDFRenderToImagesDialog::PDFRenderToImagesDialog(const pdf::PDFDocument* document,
|
||||
pdf::PDFDrawWidgetProxy* proxy,
|
||||
pdf::PDFProgress* progress,
|
||||
QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PDFRenderToImagesDialog),
|
||||
m_document(document),
|
||||
m_proxy(proxy),
|
||||
m_progress(progress),
|
||||
m_imageExportSettings(document),
|
||||
m_isLoadingData(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
@ -56,6 +68,9 @@ PDFRenderToImagesDialog::PDFRenderToImagesDialog(QWidget* parent) :
|
||||
connect(ui->optimizedWriteCheckBox, &QCheckBox::clicked, this, &PDFRenderToImagesDialog::onOptimizedWriteChanged);
|
||||
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();
|
||||
loadImageExportSettings();
|
||||
|
||||
@ -219,6 +234,11 @@ void PDFRenderToImagesDialog::onProgressiveScanWriteChanged(bool 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()
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
|
@ -22,11 +22,19 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QAbstractButton;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PDFRenderToImagesDialog;
|
||||
}
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFProgress;
|
||||
class PDFDrawWidgetProxy;
|
||||
}
|
||||
|
||||
namespace pdfviewer
|
||||
{
|
||||
|
||||
@ -35,11 +43,15 @@ class PDFRenderToImagesDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFRenderToImagesDialog(QWidget* parent);
|
||||
explicit PDFRenderToImagesDialog(const pdf::PDFDocument* document,
|
||||
pdf::PDFDrawWidgetProxy* proxy,
|
||||
pdf::PDFProgress* progress,
|
||||
QWidget* parent);
|
||||
virtual ~PDFRenderToImagesDialog() override;
|
||||
|
||||
private slots:
|
||||
void on_selectDirectoryButton_clicked();
|
||||
void on_buttonBox_clicked(QAbstractButton* button);
|
||||
|
||||
private:
|
||||
/// Loads image writer settings to the ui
|
||||
@ -62,8 +74,12 @@ private:
|
||||
void onGammaChanged(double value);
|
||||
void onOptimizedWriteChanged(bool value);
|
||||
void onProgressiveScanWriteChanged(bool value);
|
||||
void onRenderError(pdf::PDFRenderError error);
|
||||
|
||||
Ui::PDFRenderToImagesDialog* ui;
|
||||
const pdf::PDFDocument* m_document;
|
||||
pdf::PDFDrawWidgetProxy* m_proxy;
|
||||
pdf::PDFProgress* m_progress;
|
||||
pdf::PDFImageWriterSettings m_imageWriterSettings;
|
||||
pdf::PDFPageImageExportSettings m_imageExportSettings;
|
||||
bool m_isLoadingData;
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>690</width>
|
||||
<height>593</height>
|
||||
<height>602</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -234,7 +234,14 @@
|
||||
</widget>
|
||||
</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>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
|
@ -1279,11 +1279,8 @@ void PDFViewerMainWindow::on_actionPrint_triggered()
|
||||
|
||||
void PDFViewerMainWindow::on_actionRender_to_Images_triggered()
|
||||
{
|
||||
PDFRenderToImagesDialog dialog(this);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
|
||||
}
|
||||
PDFRenderToImagesDialog dialog(m_pdfDocument.data(), m_pdfWidget->getDrawWidgetProxy(), m_progress, this);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
} // namespace pdfviewer
|
||||
|
Loading…
Reference in New Issue
Block a user