Render tool basics

This commit is contained in:
Jakub Melka 2020-10-26 19:28:56 +01:00
parent 584a992da6
commit 428e5dc2ad
6 changed files with 251 additions and 40 deletions

View File

@ -681,64 +681,73 @@ void PDFPageImageExportSettings::setPixelResolution(int pixelResolution)
m_pixelResolution = pixelResolution;
}
bool PDFPageImageExportSettings::validate(QString* errorMessagePtr)
bool PDFPageImageExportSettings::validate(QString* errorMessagePtr, bool validatePageSelection, bool validateFileSettings, bool validateResolution) const
{
QString dummy;
QString& errorMessage = errorMessagePtr ? *errorMessagePtr : dummy;
if (m_directory.isEmpty())
if (validateFileSettings)
{
errorMessage = PDFTranslationContext::tr("Target directory is empty.");
return false;
}
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;
}
// 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.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;
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)
if (validatePageSelection)
{
std::vector<PDFInteger> pages = getPages();
if (pages.empty())
if (m_pageSelectionMode == PageSelectionMode::Selection)
{
errorMessage = PDFTranslationContext::tr("Page list is invalid. It should have form such as '1-12,17,24,27-29'.");
return false;
}
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 (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()))
if (validateResolution)
{
errorMessage = PDFTranslationContext::tr("DPI resolution should be in range %1 to %2.").arg(getMinDPIResolution()).arg(getMaxDPIResolution());
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;
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;

View File

@ -338,7 +338,7 @@ public:
void setPixelResolution(int pixelResolution);
/// 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
std::vector<PDFInteger> getPages() const;

View File

@ -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-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
@ -729,6 +738,26 @@ PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser
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;
}
@ -865,4 +894,17 @@ std::vector<pdf::PDFInteger> PDFToolOptions::getPageRange(pdf::PDFInteger pageCo
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

View File

@ -122,11 +122,24 @@ struct PDFToolOptions
// For option 'ColorManagementSystem'
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.
/// \param pageCount Page count
/// \param[out] errorMessage Error message
/// \param zeroBased Convert to zero based page range?
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

View File

@ -20,5 +20,145 @@
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

View File

@ -36,6 +36,13 @@ public:
virtual Options getOptionsFlags() const override;
};
class PDFToolBenchmark : public PDFToolRenderBase
{
public:
virtual QString getStandardString(StandardString standardString) const override;
virtual Options getOptionsFlags() const override;
};
} // namespace pdftool
#endif // PDFTOOLRENDER_H