From 584a992da651800fb7f19761ea092df12f13d31c Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sun, 25 Oct 2020 18:55:25 +0100 Subject: [PATCH] Settings for rendering --- PdfForQtLib/sources/pdfrenderer.h | 1 + PdfTool/PdfTool.pro | 2 + PdfTool/pdftoolabstractapplication.cpp | 315 +++++++++++++++++++++++++ PdfTool/pdftoolabstractapplication.h | 42 +++- PdfTool/pdftoolrender.cpp | 24 ++ PdfTool/pdftoolrender.h | 41 ++++ 6 files changed, 412 insertions(+), 13 deletions(-) create mode 100644 PdfTool/pdftoolrender.cpp create mode 100644 PdfTool/pdftoolrender.h diff --git a/PdfForQtLib/sources/pdfrenderer.h b/PdfForQtLib/sources/pdfrenderer.h index 7e46036..7a0b58d 100644 --- a/PdfForQtLib/sources/pdfrenderer.h +++ b/PdfForQtLib/sources/pdfrenderer.h @@ -301,6 +301,7 @@ private: class PDFFORQTLIBSHARED_EXPORT PDFPageImageExportSettings { public: + explicit PDFPageImageExportSettings() : PDFPageImageExportSettings(nullptr) { } explicit PDFPageImageExportSettings(const PDFDocument* document); enum class PageSelectionMode diff --git a/PdfTool/PdfTool.pro b/PdfTool/PdfTool.pro index f4c768f..8f55171 100644 --- a/PdfTool/PdfTool.pro +++ b/PdfTool/PdfTool.pro @@ -53,6 +53,7 @@ SOURCES += \ pdftoolinfonameddestinations.cpp \ pdftoolinfopageboxes.cpp \ pdftoolinfostructuretree.cpp \ + pdftoolrender.cpp \ pdftoolverifysignatures.cpp \ pdftoolxml.cpp @@ -79,5 +80,6 @@ HEADERS += \ pdftoolinfonameddestinations.h \ pdftoolinfopageboxes.h \ pdftoolinfostructuretree.h \ + pdftoolrender.h \ pdftoolverifysignatures.h \ pdftoolxml.h diff --git a/PdfTool/pdftoolabstractapplication.cpp b/PdfTool/pdftoolabstractapplication.cpp index c5778a7..0eb46e5 100644 --- a/PdfTool/pdftoolabstractapplication.cpp +++ b/PdfTool/pdftoolabstractapplication.cpp @@ -241,6 +241,43 @@ void PDFToolAbstractApplication::initializeCommandLineParser(QCommandLineParser* { parser->addOption(QCommandLineOption("character-maps", "Show character maps for embedded fonts.")); } + + if (optionFlags.testFlag(ImageWriterSettings)) + { + parser->addOption(QCommandLineOption("image-format", "Image format. Common formats as png, jpeg, are supported.", "format", "png")); + parser->addOption(QCommandLineOption("image-subtype", "Image format subtype. Some image formats can have this setting.", "subtype")); + parser->addOption(QCommandLineOption("image-compress-lvl", "Image compression level. Different formats can have different meaning.", "level", "9")); + parser->addOption(QCommandLineOption("image-quality", "Image quality. Different formats can have different meaning.", "quality", "100")); + parser->addOption(QCommandLineOption("image-optimized-write", "Use optimized write mode.")); + parser->addOption(QCommandLineOption("image-progressive-scan-write", "Use image progressive scan mode.")); + } + + if (optionFlags.testFlag(ImageExportSettingsFiles)) + { + parser->addOption(QCommandLineOption("image-output-dir", "Output directory, where images are saved.", "dir")); + parser->addOption(QCommandLineOption("image-template-fn", "Template file name, must contain '%' character, must not contain suffix.", "template file name", "Image_%")); + } + + if (optionFlags.testFlag(ImageExportSettingsResolution)) + { + parser->addOption(QCommandLineOption("image-res-mode", "Image resolution mode (valid values are dpi|pixel). Dpi is default.", "mode", "dpi")); + parser->addOption(QCommandLineOption("image-res-dpi", "DPI resolution of target image.", "dpi")); + parser->addOption(QCommandLineOption("image-res-pixel", "Pixel resolution of target image.", "pixel")); + } + + if (optionFlags.testFlag(ColorManagementSystem)) + { + parser->addOption(QCommandLineOption("cms", "Color management system. Valid values are generic|lcms.", "cms", "lcms")); + parser->addOption(QCommandLineOption("cms-accuracy", "Accuracy of cms system. Valid values are low|medium|high. Higher accuracy means higher memory consumption.", "accuracy", "medium")); + parser->addOption(QCommandLineOption("cms-intent", "Rendering intent. Valid values are auto|perceptual|abs|rel|saturation.", "intent", "auto")); + parser->addOption(QCommandLineOption("cms-black-compensated", "Black point compensation.", "bool", "1")); + parser->addOption(QCommandLineOption("cms-white-paper-trans", "Transform also color of paper using cms.", "bool", "0")); + parser->addOption(QCommandLineOption("cms-profile-output", "Output color profile.", "profile")); + parser->addOption(QCommandLineOption("cms-profile-gray", "Gray color profile for gray device.", "profile")); + parser->addOption(QCommandLineOption("cms-profile-rgb", "RGB color profile for RGB 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")); + } } PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser) const @@ -414,6 +451,284 @@ PDFToolOptions PDFToolAbstractApplication::getOptions(QCommandLineParser* parser options.showCharacterMapsForEmbeddedFonts = parser->isSet("character-maps"); } + if (optionFlags.testFlag(ImageWriterSettings)) + { + // Image format + QByteArray imageWriterFormat = parser->value("image-format").toLatin1(); + if (!options.imageWriterSettings.getFormats().contains(imageWriterFormat)) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Image format '%1' is not supported. Defaulting to png.").arg(QString::fromLatin1(imageWriterFormat)), options.outputCodec); + imageWriterFormat = "png"; + } + Q_ASSERT(options.imageWriterSettings.getFormats().contains(imageWriterFormat)); + + options.imageWriterSettings.selectFormat(imageWriterFormat); + + // Image subtype + if (parser->isSet("image-subtype")) + { + QByteArray imageWriterSubtype = parser->value("image-subtype").toLatin1(); + if (options.imageWriterSettings.getSubtypes().contains(imageWriterSubtype)) + { + options.imageWriterSettings.setCurrentSubtype(imageWriterSubtype); + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Image format subtype '%1' is not supported.").arg(QString::fromLatin1(imageWriterSubtype)), options.outputCodec); + } + } + + // Compression level + if (parser->isSet("image-compress-lvl")) + { + QString valueText = parser->value("image-compress-lvl"); + + bool ok = false; + int value = valueText.toInt(&ok); + if (ok) + { + if (options.imageWriterSettings.isOptionSupported(QImageIOHandler::CompressionRatio)) + { + options.imageWriterSettings.setCompression(value); + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Image compression for current format is not supported."), options.outputCodec); + } + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid compression level '%1'.").arg(valueText), options.outputCodec); + } + } + + // Quality + if (parser->isSet("image-quality")) + { + QString valueText = parser->value("image-quality"); + + bool ok = false; + int value = valueText.toInt(&ok); + if (ok) + { + if (options.imageWriterSettings.isOptionSupported(QImageIOHandler::Quality)) + { + options.imageWriterSettings.setQuality(value); + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Image quality settings for current format is not supported."), options.outputCodec); + } + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid image quality '%1'.").arg(valueText), options.outputCodec); + } + } + + options.imageWriterSettings.setOptimizedWrite(false); + options.imageWriterSettings.setProgressiveScanWrite(false); + + if (parser->isSet("image-optimized-write")) + { + if (options.imageWriterSettings.isOptionSupported(QImageIOHandler::OptimizedWrite)) + { + options.imageWriterSettings.setOptimizedWrite(true); + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Optimized write is not supported."), options.outputCodec); + } + } + + if (parser->isSet("image-progressive-scan-write")) + { + if (options.imageWriterSettings.isOptionSupported(QImageIOHandler::ProgressiveScanWrite)) + { + options.imageWriterSettings.setProgressiveScanWrite(true); + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Progressive scan write is not supported."), options.outputCodec); + } + } + } + + if (optionFlags.testFlag(ImageExportSettingsFiles)) + { + QFileInfo documentFileInfo(options.document); + QString outputDir = documentFileInfo.path(); + + if (parser->isSet("image-output-dir")) + { + outputDir = parser->value("image-output-dir"); + } + + options.imageExportSettings.setDirectory(outputDir); + options.imageExportSettings.setFileTemplate(parser->value("image-template-fn")); + } + + if (optionFlags.testFlag(ImageExportSettingsResolution)) + { + QString resMode = parser->value("image-res-mode").toLower(); + if (resMode == "dpi") + { + options.imageExportSettings.setResolutionMode(pdf::PDFPageImageExportSettings::ResolutionMode::DPI); + } + else if (resMode == "pixel") + { + options.imageExportSettings.setResolutionMode(pdf::PDFPageImageExportSettings::ResolutionMode::Pixels); + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid image resolution mode '%1'. Defaulting to dpi.").arg(resMode), options.outputCodec); + options.imageExportSettings.setResolutionMode(pdf::PDFPageImageExportSettings::ResolutionMode::DPI); + } + + if (parser->isSet("image-res-dpi")) + { + if (options.imageExportSettings.getResolutionMode() != pdf::PDFPageImageExportSettings::ResolutionMode::DPI) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Cannot set dpi value, resolution mode must be dpi."), options.outputCodec); + } + + bool ok = false; + int dpi = parser->value("image-res-dpi").toInt(&ok); + if (ok) + { + int boundedDpi = qBound(pdf::PDFPageImageExportSettings::getMinDPIResolution(), dpi, pdf::PDFPageImageExportSettings::getMaxDPIResolution()); + + if (boundedDpi != dpi) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Dpi must be in range from %1 to %2. Defaulting to %3.").arg(pdf::PDFPageImageExportSettings::getMinDPIResolution()).arg(pdf::PDFPageImageExportSettings::getMaxDPIResolution()).arg(boundedDpi), options.outputCodec); + } + + options.imageExportSettings.setDpiResolution(dpi); + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid image dpi value '%1'.").arg(parser->value("image-res-dpi")), options.outputCodec); + } + } + + if (parser->isSet("image-res-pixel")) + { + if (options.imageExportSettings.getResolutionMode() != pdf::PDFPageImageExportSettings::ResolutionMode::Pixels) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Cannot set pixel value, resolution mode must be pixel."), options.outputCodec); + } + + bool ok = false; + int pixel = parser->value("image-res-pixel").toInt(&ok); + if (ok) + { + int boundedPixel = qBound(pdf::PDFPageImageExportSettings::getMinPixelResolution(), pixel, pdf::PDFPageImageExportSettings::getMaxPixelResolution()); + + if (boundedPixel != pixel) + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Pixel value must be in range from %1 to %2. Defaulting to %3.").arg(pdf::PDFPageImageExportSettings::getMinPixelResolution()).arg(pdf::PDFPageImageExportSettings::getMaxPixelResolution()).arg(boundedPixel), options.outputCodec); + } + + options.imageExportSettings.setPixelResolution(pixel); + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Invalid image pixel value '%1'.").arg(parser->value("image-res-pixel")), options.outputCodec); + } + } + } + + if (optionFlags.testFlag(ColorManagementSystem)) + { + pdf::PDFCMSManager cmsManager(nullptr); + options.cmsSettings = cmsManager.getDefaultSettings(); + + QString cms = parser->value("cms"); + if (cms == "generic") + { + options.cmsSettings.system = pdf::PDFCMSSettings::System::Generic; + } + else if (cms == "lcms") + { + options.cmsSettings.system = pdf::PDFCMSSettings::System::LittleCMS2; + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Unknown color management system '%1'. Defaulting to lcms.").arg(cms), options.outputCodec); + options.cmsSettings.system = pdf::PDFCMSSettings::System::LittleCMS2; + } + + QString accuracy = parser->value("cms-accuracy"); + if (accuracy == "medium") + { + options.cmsSettings.accuracy = pdf::PDFCMSSettings::Accuracy::Medium; + } + else if (accuracy == "low") + { + options.cmsSettings.accuracy = pdf::PDFCMSSettings::Accuracy::Low; + } + else if (accuracy == "high") + { + options.cmsSettings.accuracy = pdf::PDFCMSSettings::Accuracy::High; + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Uknown color management system accuracy '%1'. Defaulting to medium.").arg(accuracy), options.outputCodec); + options.cmsSettings.accuracy = pdf::PDFCMSSettings::Accuracy::Medium; + } + + QString intent = parser->value("cms-intent"); + if (intent == "auto") + { + options.cmsSettings.intent = pdf::RenderingIntent::Auto; + } + else if (intent == "perceptual") + { + options.cmsSettings.intent = pdf::RenderingIntent::Perceptual; + } + else if (intent == "abs") + { + options.cmsSettings.intent = pdf::RenderingIntent::AbsoluteColorimetric; + } + else if (intent == "rel") + { + options.cmsSettings.intent = pdf::RenderingIntent::RelativeColorimetric; + } + else if (intent == "saturation") + { + options.cmsSettings.intent = pdf::RenderingIntent::Saturation; + } + else + { + PDFConsole::writeError(PDFToolTranslationContext::tr("Uknown color management system rendering intent '%1'. Defaulting to auto.").arg(intent), options.outputCodec); + options.cmsSettings.intent = pdf::RenderingIntent::Auto; + } + + if (parser->isSet("cms-black-compensated")) + { + options.cmsSettings.isBlackPointCompensationActive = parser->value("cms-black-compensated").toInt(); + } + + if (parser->isSet("cms-white-paper-trans")) + { + options.cmsSettings.isWhitePaperColorTransformed = parser->value("cms-white-paper-trans").toInt(); + } + + auto setProfile = [&parser, &options](QString settings, QString& profile) + { + if (parser->isSet(settings)) + { + profile = parser->value(settings); + } + }; + + setProfile("cms-profile-output", options.cmsSettings.outputCS); + setProfile("cms-profile-gray", options.cmsSettings.deviceGray); + setProfile("cms-profile-rgb", options.cmsSettings.deviceRGB); + setProfile("cms-profile-cmyk", options.cmsSettings.deviceCMYK); + setProfile("cms-profile-dir", options.cmsSettings.profileDirectory); + } + return options; } diff --git a/PdfTool/pdftoolabstractapplication.h b/PdfTool/pdftoolabstractapplication.h index 18f242b..b46fe01 100644 --- a/PdfTool/pdftoolabstractapplication.h +++ b/PdfTool/pdftoolabstractapplication.h @@ -21,6 +21,8 @@ #include "pdfoutputformatter.h" #include "pdfdocument.h" #include "pdfdocumenttextflow.h" +#include "pdfrenderer.h" +#include "pdfcms.h" #include #include @@ -111,6 +113,15 @@ struct PDFToolOptions // For option 'CharacterMaps' bool showCharacterMapsForEmbeddedFonts = false; + // For option 'ImageWriterSettings' + pdf::PDFImageWriterSettings imageWriterSettings; + + // For option 'ImageExportSettings' + pdf::PDFPageImageExportSettings imageExportSettings; + + // For option 'ColorManagementSystem' + pdf::PDFCMSSettings cmsSettings; + /// Returns page range. If page range is invalid, then \p errorMessage is empty. /// \param pageCount Page count /// \param[out] errorMessage Error message @@ -148,19 +159,24 @@ public: enum Option { - ConsoleFormat = 0x0001, ///< Set format of console output (text, xml or html) - OpenDocument = 0x0002, ///< Flags for document reading - SignatureVerification = 0x0004, ///< Flags for signature verification, - XmlExport = 0x0008, ///< Flags for xml export - Attachments = 0x0010, ///< Flags for attachments manipulating - DateFormat = 0x0020, ///< Date format - ComputeHashes = 0x0040, ///< Compute hashes - PageSelector = 0x0080, ///< Select page range (or all pages) - TextAnalysis = 0x0100, ///< Text analysis options - TextShow = 0x0200, ///< Text extract and show options - VoiceSelector = 0x0400, ///< Select voice from SAPI - TextSpeech = 0x0800, ///< Text speech options - CharacterMaps = 0x1000, ///< Character maps for embedded fonts + ConsoleFormat = 0x00000001, ///< Set format of console output (text, xml or html) + OpenDocument = 0x00000002, ///< Flags for document reading + SignatureVerification = 0x00000004, ///< Flags for signature verification, + XmlExport = 0x00000008, ///< Flags for xml export + Attachments = 0x00000010, ///< Flags for attachments manipulating + DateFormat = 0x00000020, ///< Date format + ComputeHashes = 0x00000040, ///< Compute hashes + PageSelector = 0x00000080, ///< Select page range (or all pages) + TextAnalysis = 0x00000100, ///< Text analysis options + TextShow = 0x00000200, ///< Text extract and show options + VoiceSelector = 0x00000400, ///< Select voice from SAPI + TextSpeech = 0x00000800, ///< Text speech options + CharacterMaps = 0x00001000, ///< Character maps for embedded fonts + ImageWriterSettings = 0x00002000, ///< Settings for writing images (for example, format, etc.) + ImageExportSettingsFiles = 0x00004000, ///< Settings for exporting page images to files + ImageExportSettingsResolution = 0x00008000, ///< Settings for resolution of exported images + ColorManagementSystem = 0x00010000, ///< Color management system settings + RenderFlags = 0x00020000, ///< Render flags for page image rasterizer }; Q_DECLARE_FLAGS(Options, Option) diff --git a/PdfTool/pdftoolrender.cpp b/PdfTool/pdftoolrender.cpp new file mode 100644 index 0000000..cb90d72 --- /dev/null +++ b/PdfTool/pdftoolrender.cpp @@ -0,0 +1,24 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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 +// (at your option) any later version. +// +// PdfForQt 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 PDFForQt. If not, see . + +#include "pdftoolrender.h" + +namespace pdftool +{ + + +} // namespace pdftool diff --git a/PdfTool/pdftoolrender.h b/PdfTool/pdftoolrender.h new file mode 100644 index 0000000..fd88a7a --- /dev/null +++ b/PdfTool/pdftoolrender.h @@ -0,0 +1,41 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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 +// (at your option) any later version. +// +// PdfForQt 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 PDFForQt. If not, see . + +#ifndef PDFTOOLRENDER_H +#define PDFTOOLRENDER_H + +#include "pdftoolabstractapplication.h" + +namespace pdftool +{ + +class PDFToolRenderBase : public PDFToolAbstractApplication +{ +public: + virtual int execute(const PDFToolOptions& options) override; +}; + +class PDFToolRender : public PDFToolRenderBase +{ +public: + virtual QString getStandardString(StandardString standardString) const override; + virtual Options getOptionsFlags() const override; +}; + +} // namespace pdftool + +#endif // PDFTOOLRENDER_H