mirror of https://github.com/JakubMelka/PDF4QT.git
Render tool basics
This commit is contained in:
parent
584a992da6
commit
428e5dc2ad
|
@ -681,64 +681,73 @@ void PDFPageImageExportSettings::setPixelResolution(int pixelResolution)
|
||||||
m_pixelResolution = pixelResolution;
|
m_pixelResolution = pixelResolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PDFPageImageExportSettings::validate(QString* errorMessagePtr)
|
bool PDFPageImageExportSettings::validate(QString* errorMessagePtr, bool validatePageSelection, bool validateFileSettings, bool validateResolution) const
|
||||||
{
|
{
|
||||||
QString dummy;
|
QString dummy;
|
||||||
QString& errorMessage = errorMessagePtr ? *errorMessagePtr : dummy;
|
QString& errorMessage = errorMessagePtr ? *errorMessagePtr : dummy;
|
||||||
|
|
||||||
if (m_directory.isEmpty())
|
if (validateFileSettings)
|
||||||
{
|
{
|
||||||
errorMessage = PDFTranslationContext::tr("Target directory is empty.");
|
if (m_directory.isEmpty())
|
||||||
return false;
|
{
|
||||||
}
|
errorMessage = PDFTranslationContext::tr("Target directory is empty.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check, if target directory exists
|
// Check, if target directory exists
|
||||||
QDir directory(m_directory);
|
QDir directory(m_directory);
|
||||||
if (!directory.exists())
|
if (!directory.exists())
|
||||||
{
|
{
|
||||||
errorMessage = PDFTranslationContext::tr("Target directory '%1' doesn't exist.").arg(m_directory);
|
errorMessage = PDFTranslationContext::tr("Target directory '%1' doesn't exist.").arg(m_directory);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_fileTemplate.isEmpty())
|
if (m_fileTemplate.isEmpty())
|
||||||
{
|
{
|
||||||
errorMessage = PDFTranslationContext::tr("File template is empty.");
|
errorMessage = PDFTranslationContext::tr("File template is empty.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_fileTemplate.contains("%"))
|
if (!m_fileTemplate.contains("%"))
|
||||||
{
|
{
|
||||||
errorMessage = PDFTranslationContext::tr("File template must contain character '%' for page number.");
|
errorMessage = PDFTranslationContext::tr("File template must contain character '%' for page number.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check page selection
|
// Check page selection
|
||||||
if (m_pageSelectionMode == PageSelectionMode::Selection)
|
if (validatePageSelection)
|
||||||
{
|
{
|
||||||
std::vector<PDFInteger> pages = getPages();
|
if (m_pageSelectionMode == PageSelectionMode::Selection)
|
||||||
if (pages.empty())
|
|
||||||
{
|
{
|
||||||
errorMessage = PDFTranslationContext::tr("Page list is invalid. It should have form such as '1-12,17,24,27-29'.");
|
std::vector<PDFInteger> pages = getPages();
|
||||||
return false;
|
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()))
|
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());
|
errorMessage = PDFTranslationContext::tr("Page list contains page, which is not in the document (%1).").arg(pages.back());
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_resolutionMode == ResolutionMode::DPI && (m_dpiResolution < getMinDPIResolution() || m_dpiResolution > getMaxDPIResolution()))
|
if (validateResolution)
|
||||||
{
|
{
|
||||||
errorMessage = PDFTranslationContext::tr("DPI resolution should be in range %1 to %2.").arg(getMinDPIResolution()).arg(getMaxDPIResolution());
|
if (m_resolutionMode == ResolutionMode::DPI && (m_dpiResolution < getMinDPIResolution() || m_dpiResolution > getMaxDPIResolution()))
|
||||||
return false;
|
{
|
||||||
}
|
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()))
|
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());
|
errorMessage = PDFTranslationContext::tr("Pixel resolution should be in range %1 to %2.").arg(getMinPixelResolution()).arg(getMaxPixelResolution());
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -338,7 +338,7 @@ public:
|
||||||
void setPixelResolution(int pixelResolution);
|
void setPixelResolution(int pixelResolution);
|
||||||
|
|
||||||
/// Validates the settings, if they can be used for image generation
|
/// Validates the settings, if they can be used for image generation
|
||||||
bool validate(QString* errorMessagePtr);
|
bool validate(QString* errorMessagePtr, bool validatePageSelection = true, bool validateFileSettings = true, bool validateResolution = true) const;
|
||||||
|
|
||||||
/// Returns list of selected pages
|
/// Returns list of selected pages
|
||||||
std::vector<PDFInteger> getPages() const;
|
std::vector<PDFInteger> getPages() const;
|
||||||
|
|
|
@ -278,6 +278,15 @@ void PDFToolAbstractApplication::initializeCommandLineParser(QCommandLineParser*
|
||||||
parser->addOption(QCommandLineOption("cms-profile-cmyk", "CMYK color profile for CMYK device.", "profile"));
|
parser->addOption(QCommandLineOption("cms-profile-cmyk", "CMYK color profile for CMYK device.", "profile"));
|
||||||
parser->addOption(QCommandLineOption("cms-profile-dir", "External directory containing color profiles.", "directory"));
|
parser->addOption(QCommandLineOption("cms-profile-dir", "External directory containing color profiles.", "directory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (optionFlags.testFlag(RenderFlags))
|
||||||
|
{
|
||||||
|
const pdf::PDFRenderer::Features defaultFeatures = pdf::PDFRenderer::getDefaultFeatures();
|
||||||
|
for (const PDFToolOptions::RenderFeatureInfo& info : PDFToolOptions::getRenderFeatures())
|
||||||
|
{
|
||||||
|
parser->addOption(QCommandLineOption(info.option, info.description, "bool", defaultFeatures.testFlag(info.feature) ? "1" : "0"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser) const
|
PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser) const
|
||||||
|
@ -729,6 +738,26 @@ PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser
|
||||||
setProfile("cms-profile-dir", options.cmsSettings.profileDirectory);
|
setProfile("cms-profile-dir", options.cmsSettings.profileDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (optionFlags.testFlag(RenderFlags))
|
||||||
|
{
|
||||||
|
for (const PDFToolOptions::RenderFeatureInfo& info : PDFToolOptions::getRenderFeatures())
|
||||||
|
{
|
||||||
|
QString textValue = parser->value(info.option);
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
bool value = textValue.toInt(&ok);
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
options.renderFeatures.setFlag(info.feature, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PDFConsole::writeError(PDFToolTranslationContext::tr("Uknown bool value '%1'. Default value is used.").arg(textValue), options.outputCodec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,4 +894,17 @@ std::vector<pdf::PDFInteger> PDFToolOptions::getPageRange(pdf::PDFInteger pageCo
|
||||||
return pageIndices;
|
return pageIndices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<PDFToolOptions::RenderFeatureInfo> PDFToolOptions::getRenderFeatures()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
RenderFeatureInfo{ "render-antialiasing", "Antialiasing for lines, shapes, etc.", pdf::PDFRenderer::Antialiasing },
|
||||||
|
RenderFeatureInfo{ "render-text-antialiasing", "Antialiasing for text outlines.", pdf::PDFRenderer::TextAntialiasing },
|
||||||
|
RenderFeatureInfo{ "render-smooth-img", "Smooth image transformation (slower, but better quality images).", pdf::PDFRenderer::SmoothImages },
|
||||||
|
RenderFeatureInfo{ "render-ignore-opt-content", "Ignore optional content settings (draw everything).", pdf::PDFRenderer::IgnoreOptionalContent },
|
||||||
|
RenderFeatureInfo{ "render-clip-to-crop-box", "Clip page graphics to crop box.", pdf::PDFRenderer::ClipToCropBox },
|
||||||
|
RenderFeatureInfo{ "render-invert-colors", "Invert all colors.", pdf::PDFRenderer::InvertColors },
|
||||||
|
RenderFeatureInfo{ "render-display-annot", "Display annotations.", pdf::PDFRenderer::DisplayAnnotations }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} // pdftool
|
} // pdftool
|
||||||
|
|
|
@ -122,11 +122,24 @@ struct PDFToolOptions
|
||||||
// For option 'ColorManagementSystem'
|
// For option 'ColorManagementSystem'
|
||||||
pdf::PDFCMSSettings cmsSettings;
|
pdf::PDFCMSSettings cmsSettings;
|
||||||
|
|
||||||
|
// For option 'RenderFlags'
|
||||||
|
pdf::PDFRenderer::Features renderFeatures = pdf::PDFRenderer::getDefaultFeatures();
|
||||||
|
|
||||||
/// Returns page range. If page range is invalid, then \p errorMessage is empty.
|
/// Returns page range. If page range is invalid, then \p errorMessage is empty.
|
||||||
/// \param pageCount Page count
|
/// \param pageCount Page count
|
||||||
/// \param[out] errorMessage Error message
|
/// \param[out] errorMessage Error message
|
||||||
/// \param zeroBased Convert to zero based page range?
|
/// \param zeroBased Convert to zero based page range?
|
||||||
std::vector<pdf::PDFInteger> getPageRange(pdf::PDFInteger pageCount, QString& errorMessage, bool zeroBased) const;
|
std::vector<pdf::PDFInteger> getPageRange(pdf::PDFInteger pageCount, QString& errorMessage, bool zeroBased) const;
|
||||||
|
|
||||||
|
struct RenderFeatureInfo
|
||||||
|
{
|
||||||
|
QString option;
|
||||||
|
QString description;
|
||||||
|
pdf::PDFRenderer::Feature feature;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns a list of available renderer features
|
||||||
|
static std::vector<RenderFeatureInfo> getRenderFeatures();
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for all applications
|
/// Base class for all applications
|
||||||
|
|
|
@ -20,5 +20,145 @@
|
||||||
namespace pdftool
|
namespace pdftool
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static PDFToolRender s_toolRenderApplication;
|
||||||
|
static PDFToolBenchmark s_toolBenchmarkApplication;
|
||||||
|
|
||||||
|
QString PDFToolRender::getStandardString(PDFToolAbstractApplication::StandardString standardString) const
|
||||||
|
{
|
||||||
|
switch (standardString)
|
||||||
|
{
|
||||||
|
case Command:
|
||||||
|
return "render";
|
||||||
|
|
||||||
|
case Name:
|
||||||
|
return PDFToolTranslationContext::tr("Render document");
|
||||||
|
|
||||||
|
case Description:
|
||||||
|
return PDFToolTranslationContext::tr("Render selected pages of document into image files.");
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFToolAbstractApplication::Options PDFToolRender::getOptionsFlags() const
|
||||||
|
{
|
||||||
|
return ConsoleFormat | OpenDocument | PageSelector | ImageWriterSettings | ImageExportSettingsFiles | ImageExportSettingsResolution | ColorManagementSystem | RenderFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PDFToolBenchmark::getStandardString(PDFToolAbstractApplication::StandardString standardString) const
|
||||||
|
{
|
||||||
|
switch (standardString)
|
||||||
|
{
|
||||||
|
case Command:
|
||||||
|
return "benchmark";
|
||||||
|
|
||||||
|
case Name:
|
||||||
|
return PDFToolTranslationContext::tr("Benchmark rendering");
|
||||||
|
|
||||||
|
case Description:
|
||||||
|
return PDFToolTranslationContext::tr("Benchmark page rendering (measure time, detect errors).");
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFToolAbstractApplication::Options PDFToolBenchmark::getOptionsFlags() const
|
||||||
|
{
|
||||||
|
return ConsoleFormat | OpenDocument | PageSelector | ImageExportSettingsResolution | ColorManagementSystem | RenderFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PDFToolRenderBase::execute(const PDFToolOptions& options)
|
||||||
|
{
|
||||||
|
pdf::PDFDocument document;
|
||||||
|
QByteArray sourceData;
|
||||||
|
if (!readDocument(options, document, &sourceData))
|
||||||
|
{
|
||||||
|
return ErrorDocumentReading;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString parseError;
|
||||||
|
std::vector<pdf::PDFInteger> pages = options.getPageRange(document.getCatalog()->getPageCount(), parseError, true);
|
||||||
|
|
||||||
|
if (!parseError.isEmpty())
|
||||||
|
{
|
||||||
|
PDFConsole::writeError(parseError, options.outputCodec);
|
||||||
|
return ErrorInvalidArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString errorMessage;
|
||||||
|
Options optionFlags = getOptionsFlags();
|
||||||
|
if (!options.imageExportSettings.validate(&errorMessage, false, optionFlags.testFlag(ImageExportSettingsFiles), optionFlags.testFlag(ImageExportSettingsResolution)))
|
||||||
|
{
|
||||||
|
PDFConsole::writeError(errorMessage, options.outputCodec);
|
||||||
|
return ErrorInvalidArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are ready to render the document
|
||||||
|
pdf::PDFOptionalContentActivity optionalContentActivity(&document, pdf::OCUsage::Export, nullptr);
|
||||||
|
m_cms = m_proxy->getCMSManager()->getCurrentCMS();
|
||||||
|
pdf::PDFRasterizerPool rasterizerPool(&document, m_proxy->getFontCache(), m_proxy->getCMSManager(),
|
||||||
|
&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 = [&options](const pdf::PDFPage* page) -> QSize
|
||||||
|
{
|
||||||
|
Q_ASSERT(page);
|
||||||
|
|
||||||
|
switch (options.imageExportSettings.getResolutionMode())
|
||||||
|
{
|
||||||
|
case pdf::PDFPageImageExportSettings::ResolutionMode::DPI:
|
||||||
|
{
|
||||||
|
QSizeF size = page->getRotatedMediaBox().size() * pdf::PDF_POINT_TO_INCH * options.imageExportSettings.getDpiResolution();
|
||||||
|
return size.toSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
case pdf::PDFPageImageExportSettings::ResolutionMode::Pixels:
|
||||||
|
{
|
||||||
|
int pixelResolution = options.imageExportSettings.getPixelResolution();
|
||||||
|
QSizeF size = page->getRotatedMediaBox().size().scaled(pixelResolution, pixelResolution, Qt::KeepAspectRatio);
|
||||||
|
return size.toSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QSize();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto processImage = [this](const pdf::PDFInteger pageIndex, QImage&& image)
|
||||||
|
{
|
||||||
|
QString fileName = m_imageExportSettings.getOutputFileName(pageIndex, m_imageWriterSettings.getCurrentFormat());
|
||||||
|
|
||||||
|
QImageWriter imageWriter(fileName, m_imageWriterSettings.getCurrentFormat());
|
||||||
|
imageWriter.setSubType(m_imageWriterSettings.getCurrentSubtype());
|
||||||
|
imageWriter.setCompression(m_imageWriterSettings.getCompression());
|
||||||
|
imageWriter.setQuality(m_imageWriterSettings.getQuality());
|
||||||
|
imageWriter.setGamma(m_imageWriterSettings.getGamma());
|
||||||
|
imageWriter.setOptimizedWrite(m_imageWriterSettings.hasOptimizedWrite());
|
||||||
|
imageWriter.setProgressiveScanWrite(m_imageWriterSettings.hasProgressiveScanWrite());
|
||||||
|
|
||||||
|
if (!imageWriter.write(image))
|
||||||
|
{
|
||||||
|
emit m_rasterizerPool->renderError(pdf::PDFRenderError(pdf::RenderErrorType::Error, tr("Can't write page image to file '%1', because: %2.").arg(fileName).arg(imageWriter.errorString())));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rasterizerPool.render(pageIndices, imageSizeGetter, processImage, m_progress);
|
||||||
|
|
||||||
|
return ExitSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdftool
|
} // namespace pdftool
|
||||||
|
|
|
@ -36,6 +36,13 @@ public:
|
||||||
virtual Options getOptionsFlags() const override;
|
virtual Options getOptionsFlags() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PDFToolBenchmark : public PDFToolRenderBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual QString getStandardString(StandardString standardString) const override;
|
||||||
|
virtual Options getOptionsFlags() const override;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace pdftool
|
} // namespace pdftool
|
||||||
|
|
||||||
#endif // PDFTOOLRENDER_H
|
#endif // PDFTOOLRENDER_H
|
||||||
|
|
Loading…
Reference in New Issue