diff --git a/PdfForQtLib/PdfForQtLib.pro b/PdfForQtLib/PdfForQtLib.pro index 99d3432..b0a8889 100644 --- a/PdfForQtLib/PdfForQtLib.pro +++ b/PdfForQtLib/PdfForQtLib.pro @@ -39,6 +39,7 @@ SOURCES += \ sources/pdfaction.cpp \ sources/pdfblendfunction.cpp \ sources/pdfccittfaxdecoder.cpp \ + sources/pdfcms.cpp \ sources/pdfcompiler.cpp \ sources/pdffile.cpp \ sources/pdfitemmodels.cpp \ @@ -77,6 +78,7 @@ HEADERS += \ sources/pdfaction.h \ sources/pdfblendfunction.h \ sources/pdfccittfaxdecoder.h \ + sources/pdfcms.h \ sources/pdfcompiler.h \ sources/pdffile.h \ sources/pdfitemmodels.h \ @@ -174,6 +176,16 @@ zlib.files = $$PDFFORQT_DEPENDENCIES_PATH/zlib/bin/zlib.dll zlib.path = $$DESTDIR/install INSTALLS += zlib +# Link lcms2 +LIBS += -L$$PDFFORQT_DEPENDENCIES_PATH/lcms2/bin/ -llcms2 +INCLUDEPATH += $$PDFFORQT_DEPENDENCIES_PATH/lcms2/include +DEPENDPATH += $$PDFFORQT_DEPENDENCIES_PATH/lcms2/include + +# Add lcms2 to installations +lcms2.files = $$PDFFORQT_DEPENDENCIES_PATH/lcms2/bin/lcms2.dll +lcms2.path = $$DESTDIR/install +INSTALLS += lcms2 + # ensure debug info even for RELEASE build CONFIG += force_debug_info diff --git a/PdfForQtLib/sources/pdfcms.cpp b/PdfForQtLib/sources/pdfcms.cpp new file mode 100644 index 0000000..7f64ea6 --- /dev/null +++ b/PdfForQtLib/sources/pdfcms.cpp @@ -0,0 +1,219 @@ +// Copyright (C) 2019 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 "pdfcms.h" + +#pragma warning(push) +#pragma warning(disable:5033) +#include +#pragma warning(pop) + +namespace pdf +{ + +bool PDFCMSGeneric::isCompatible(const PDFCMSSettings& settings) const +{ + return settings.system == PDFCMSSettings::System::Generic; +} + +QColor PDFCMSGeneric::getColorFromDeviceGray(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const +{ + Q_UNUSED(color); + Q_UNUSED(intent); + Q_UNUSED(reporter); + return QColor(); +} + +QColor PDFCMSGeneric::getColorFromDeviceRGB(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const +{ + Q_UNUSED(color); + Q_UNUSED(intent); + Q_UNUSED(reporter); + return QColor(); +} + +QColor PDFCMSGeneric::getColorFromDeviceCMYK(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const +{ + Q_UNUSED(color); + Q_UNUSED(intent); + Q_UNUSED(reporter); + return QColor(); +} + +QColor PDFCMSGeneric::getColorFromXYZ(const PDFColor3& whitePoint, const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const +{ + Q_UNUSED(color); + Q_UNUSED(intent); + Q_UNUSED(reporter); + Q_UNUSED(whitePoint); + return QColor(); +} + +PDFCMSManager::PDFCMSManager(QObject* parent) : + BaseClass(parent) +{ + +} + +const PDFColorSpaceIdentifiers& PDFCMSManager::getOutputProfiles() const +{ + QMutexLocker lock(&m_mutex); + return m_outputProfiles.get(this, &PDFCMSManager::getOutputProfilesImpl); +} + +const PDFColorSpaceIdentifiers& PDFCMSManager::getGrayProfiles() const +{ + QMutexLocker lock(&m_mutex); + return m_grayProfiles.get(this, &PDFCMSManager::getGrayProfilesImpl); +} + +const PDFColorSpaceIdentifiers& PDFCMSManager::getRGBProfiles() const +{ + QMutexLocker lock(&m_mutex); + return m_RGBProfiles.get(this, &PDFCMSManager::getRGBProfilesImpl); +} + +const PDFColorSpaceIdentifiers& PDFCMSManager::getCMYKProfiles() const +{ + QMutexLocker lock(&m_mutex); + return m_CMYKProfiles.get(this, &PDFCMSManager::getCMYKProfilesImpl); +} + +PDFCMSSettings PDFCMSManager::getDefaultSettings() const +{ + PDFCMSSettings settings; + + auto getFirstProfileId = [](const PDFColorSpaceIdentifiers& identifiers) + { + if (!identifiers.empty()) + { + return identifiers.front().id; + } + return QString(); + }; + + settings.system = PDFCMSSettings::System::LittleCMS2; + settings.outputCS = getFirstProfileId(getOutputProfiles()); + settings.deviceGray = getFirstProfileId(getGrayProfiles()); + settings.deviceRGB = getFirstProfileId(getRGBProfiles()); + settings.deviceCMYK = getFirstProfileId(getCMYKProfiles()); + + return settings; +} + +QString PDFCMSManager::getSystemName(PDFCMSSettings::System system) +{ + switch (system) + { + case PDFCMSSettings::System::Generic: + return tr("Generic"); + + case PDFCMSSettings::System::LittleCMS2: + { + const int major = LCMS_VERSION / 1000; + const int minor = (LCMS_VERSION % 1000) / 10; + return tr("Little CMS %1.%2").arg(major).arg(minor); + } + + default: + { + Q_ASSERT(false); + break; + } + } + + return QString(); +} + +PDFColorSpaceIdentifiers PDFCMSManager::getOutputProfilesImpl() const +{ + // Currently, we only support sRGB output color profile. + return { PDFColorSpaceIdentifier::createSRGB() }; +} + +PDFColorSpaceIdentifiers PDFCMSManager::getGrayProfilesImpl() const +{ + // Jakub Melka: We create gray profiles for temperature 5000K, 6500K and 9300K. + // We also use linear gamma and gamma value 2.2. + return { + PDFColorSpaceIdentifier::createGray(tr("Gray D65, γ = 2.2"), "@GENERIC_Gray_D65_g22", 6500.0, 2.2), + PDFColorSpaceIdentifier::createGray(tr("Gray D50, γ = 2.2"), "@GENERIC_Gray_D50_g22", 5000.0, 2.2), + PDFColorSpaceIdentifier::createGray(tr("Gray D93, γ = 2.2"), "@GENERIC_Gray_D93_g22", 9300.0, 2.2), + PDFColorSpaceIdentifier::createGray(tr("Gray D65, γ = 1.0 (linear)"), "@GENERIC_Gray_D65_g10", 6500.0, 1.0), + PDFColorSpaceIdentifier::createGray(tr("Gray D50, γ = 1.0 (linear)"), "@GENERIC_Gray_D50_g10", 5000.0, 1.0), + PDFColorSpaceIdentifier::createGray(tr("Gray D93, γ = 1.0 (linear)"), "@GENERIC_Gray_D93_g10", 9300.0, 1.0) + }; +} + +PDFColorSpaceIdentifiers PDFCMSManager::getRGBProfilesImpl() const +{ + // Jakub Melka: We create RGB profiles for common standars and also for + // default standard sRGB. See https://en.wikipedia.org/wiki/Color_spaces_with_RGB_primaries. + return { + PDFColorSpaceIdentifier::createSRGB(), + PDFColorSpaceIdentifier::createRGB(tr("HDTV (ITU-R BT.709)"), "@GENERIC_RGB_HDTV", 6500, QPointF(0.64, 0.33), QPointF(0.30, 0.60), QPointF(0.15, 0.06), 20.0 / 9.0), + PDFColorSpaceIdentifier::createRGB(tr("Adobe RGB 1998"), "@GENERIC_RGB_Adobe1998", 6500, QPointF(0.64, 0.33), QPointF(0.30, 0.60), QPointF(0.15, 0.06), 563.0 / 256.0), + PDFColorSpaceIdentifier::createRGB(tr("PAL / SECAM"), "@GENERIC_RGB_PalSecam", 6500, QPointF(0.64, 0.33), QPointF(0.29, 0.60), QPointF(0.15, 0.06), 14.0 / 5.0), + PDFColorSpaceIdentifier::createRGB(tr("NTSC"), "@GENERIC_RGB_NTSC", 6500, QPointF(0.64, 0.34), QPointF(0.31, 0.595), QPointF(0.155, 0.07), 20.0 / 9.0), + PDFColorSpaceIdentifier::createRGB(tr("Adobe Wide Gamut RGB"), "@GENERIC_RGB_AdobeWideGamut", 5000, QPointF(0.735, 0.265), QPointF(0.115, 0.826), QPointF(0.157, 0.018), 563.0 / 256.0), + PDFColorSpaceIdentifier::createRGB(tr("ProPhoto RGB"), "@GENERIC_RGB_ProPhoto", 5000, QPointF(0.7347, 0.2653), QPointF(0.1596, 0.8404), QPointF(0.0366, 0.0001), 9.0 / 5.0) + }; +} + +PDFColorSpaceIdentifiers PDFCMSManager::getCMYKProfilesImpl() const +{ + return { + + }; +} + +PDFColorSpaceIdentifier PDFColorSpaceIdentifier::createGray(QString name, QString id, PDFReal temperature, PDFReal gamma) +{ + PDFColorSpaceIdentifier result; + result.type = Type::Gray; + result.name = qMove(name); + result.id = qMove(id); + result.temperature = temperature; + result.gamma = gamma; + return result; +} + +PDFColorSpaceIdentifier PDFColorSpaceIdentifier::createSRGB() +{ + PDFColorSpaceIdentifier result; + result.type = Type::sRGB; + result.name = PDFCMSManager::tr("sRGB"); + result.id = "@GENERIC_sRGB"; + return result; +} + +PDFColorSpaceIdentifier PDFColorSpaceIdentifier::createRGB(QString name, QString id, PDFReal temperature, QPointF primaryR, QPointF primaryG, QPointF primaryB, PDFReal gamma) +{ + PDFColorSpaceIdentifier result; + result.type = Type::RGB; + result.name = qMove(name); + result.id = qMove(id); + result.temperature = temperature; + result.primaryR = primaryR; + result.primaryG = primaryG; + result.primaryB = primaryB; + result.gamma = gamma; + return result; +} + +} // namespace pdf + diff --git a/PdfForQtLib/sources/pdfcms.h b/PdfForQtLib/sources/pdfcms.h new file mode 100644 index 0000000..311c6d4 --- /dev/null +++ b/PdfForQtLib/sources/pdfcms.h @@ -0,0 +1,225 @@ +// Copyright (C) 2019 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 PDFCMS_H +#define PDFCMS_H + +#include "pdfglobal.h" +#include "pdfcolorspaces.h" +#include "pdfexception.h" +#include "pdfutils.h" + +#include + +#include + +namespace pdf +{ + +/// This simple structure stores settings for color management system, and what +/// color management system should be used. At default, two color management +/// system are available - generic (which uses default imprecise color management), +/// and CMS using engine LittleCMS2, which was written by Marti Maria, and is +/// linked as separate library. +struct PDFCMSSettings +{ + /// Type of color management system + enum class System + { + Generic, + LittleCMS2 + }; + + /// Controls accuracy of the color transformations. High accuracy + /// could mean high memory consumption, but better color accuracy, + /// low accuracy means low memory consumption and low color accuracy. + enum class Accuracy + { + Low, + Medium, + High + }; + + bool operator==(const PDFCMSSettings&) const = default; + + System system = System::Generic; + Accuracy accuracy = Accuracy::Medium; + RenderingIntent intent = RenderingIntent::Auto; + bool isBlackPointCompensationActive = true; + bool isWhitePaperColorTransformed = false; + QString outputCS; ///< Output (rendering) color space + QString deviceGray; ///< Identifiers for color space (device gray) + QString deviceRGB; ///< Identifiers for color space (device RGB) + QString deviceCMYK; ///< Identifiers for color space (device CMYK) +}; + +/// Color management system base class. It contains functions to transform +/// colors from various color system to device color system. If color management +/// system can't handle color transform, it should return invalid color. +class PDFCMS +{ +public: + explicit inline PDFCMS() = default; + virtual ~PDFCMS() = default; + + /// This function should decide, if color management system is compatible with these + /// settings (so, it transforms colors according to this setting). If this + /// function returns false, then this color management system should be replaced + /// by newly created one, according these settings. + virtual bool isCompatible(const PDFCMSSettings& settings) const = 0; + + /// Converts color in Device Gray color space to the target device + /// color space. If error occurs, then invalid color is returned. + /// Caller then should handle this - try to convert color as accurate + /// as possible. + /// \param color Single color channel value + /// \param intent Rendering intent + /// \param reporter Render error reporter (used, when color transform fails) + virtual QColor getColorFromDeviceGray(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const = 0; + + /// Converts color in Device RGB color space to the target device + /// color space. If error occurs, then invalid color is returned. + /// Caller then should handle this - try to convert color as accurate + /// as possible. + /// \param color Three color channel value (R,G,B channel) + /// \param intent Rendering intent + /// \param reporter Render error reporter (used, when color transform fails) + virtual QColor getColorFromDeviceRGB(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const = 0; + + /// Converts color in Device CMYK color space to the target device + /// color space. If error occurs, then invalid color is returned. + /// Caller then should handle this - try to convert color as accurate + /// as possible. + /// \param color Four color channel value (C,M,Y,K channel) + /// \param intent Rendering intent + /// \param reporter Render error reporter (used, when color transform fails) + virtual QColor getColorFromDeviceCMYK(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const = 0; + + /// Converts color in XYZ color space to the target device + /// color space. If error occurs, then invalid color is returned. + /// Caller then should handle this - try to convert color as accurate + /// as possible. + /// \param whitePoint White point of source XYZ color space + /// \param Three color channel value (X,Y,Z channel) + /// \param intent Rendering intent + /// \param reporter Render error reporter (used, when color transform fails) + virtual QColor getColorFromXYZ(const PDFColor3& whitePoint, const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const = 0; +}; + +class PDFCMSGeneric : public PDFCMS +{ +public: + explicit inline PDFCMSGeneric() = default; + + virtual bool isCompatible(const PDFCMSSettings& settings) const override; + virtual QColor getColorFromDeviceGray(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; + virtual QColor getColorFromDeviceRGB(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; + virtual QColor getColorFromDeviceCMYK(const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; + virtual QColor getColorFromXYZ(const PDFColor3& whitePoint, const PDFColor& color, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; +}; + +struct PDFColorSpaceIdentifier +{ + enum class Type + { + Gray, + sRGB, + RGB, + CMYK + }; + + Type type = Type::sRGB; + QString name; + QString id; + PDFReal temperature = 6500.0; + QPointF primaryR; + QPointF primaryG; + QPointF primaryB; + PDFReal gamma = 1.0; + + /// Creates gray color space identifier + /// \param name Name of color profile + /// \param id Identifier of color profile + /// \param temperature White point temperature + /// \param gamma Gamma correction + static PDFColorSpaceIdentifier createGray(QString name, QString id, PDFReal temperature, PDFReal gamma); + + /// Creates sRGB color space identifier + static PDFColorSpaceIdentifier createSRGB(); + + /// Creates RGB color space identifier + /// \param name Name of color profile + /// \param id Identifier of color profile + /// \param temperature White point temperature + /// \param primaryR Primary red + /// \param primaryG Primary green + /// \param primaryB Primary blue + /// \param gamma Gamma correction + static PDFColorSpaceIdentifier createRGB(QString name, QString id, PDFReal temperature, QPointF primaryR, QPointF primaryG, QPointF primaryB, PDFReal gamma); +}; + +using PDFColorSpaceIdentifiers = std::vector; + +/// Manager, that manages current color management system and also list +/// of usable input and output color profiles. It has color profiles +/// for outout device, and color profiles for input (gray/RGB/CMYK). +/// It also handles settings, and it's changes. Constant functions +/// is save to call from multiple threads, this also holds for some +/// non-constant functions - manager is protected by mutexes. +class PDFFORQTLIBSHARED_EXPORT PDFCMSManager : public QObject +{ + Q_OBJECT + +private: + using BaseClass = QObject; + +public: + explicit PDFCMSManager(QObject* parent); + + const PDFColorSpaceIdentifiers& getOutputProfiles() const; + const PDFColorSpaceIdentifiers& getGrayProfiles() const; + const PDFColorSpaceIdentifiers& getRGBProfiles() const; + const PDFColorSpaceIdentifiers& getCMYKProfiles() const; + + /// Returns default color management settings + PDFCMSSettings getDefaultSettings() const; + + /// Get translated name for color management system + /// \param system System + static QString getSystemName(PDFCMSSettings::System system); + +signals: + void colorManagementSystemChanged(); + +private: + PDFColorSpaceIdentifiers getOutputProfilesImpl() const; + PDFColorSpaceIdentifiers getGrayProfilesImpl() const; + PDFColorSpaceIdentifiers getRGBProfilesImpl() const; + PDFColorSpaceIdentifiers getCMYKProfilesImpl() const; + + PDFCMSSettings m_settings; + + mutable QMutex m_mutex; + mutable PDFCachedItem m_outputProfiles; + mutable PDFCachedItem m_grayProfiles; + mutable PDFCachedItem m_RGBProfiles; + mutable PDFCachedItem m_CMYKProfiles; +}; + +} // namespace pdf + +#endif // PDFCMS_H diff --git a/PdfForQtLib/sources/pdfglobal.h b/PdfForQtLib/sources/pdfglobal.h index cd72079..1f3d7b7 100644 --- a/PdfForQtLib/sources/pdfglobal.h +++ b/PdfForQtLib/sources/pdfglobal.h @@ -150,6 +150,7 @@ enum class RendererEngine enum class RenderingIntent { + Auto, ///< Rendering intent is automatically selected Perceptual, AbsoluteColorimetric, RelativeColorimetric, diff --git a/PdfForQtViewer/pdfforqtviewer.qrc b/PdfForQtViewer/pdfforqtviewer.qrc index 002f588..a0bddf9 100644 --- a/PdfForQtViewer/pdfforqtviewer.qrc +++ b/PdfForQtViewer/pdfforqtviewer.qrc @@ -26,5 +26,6 @@ resources/shortcuts.svg resources/info.svg resources/send-mail.svg + resources/cms.svg diff --git a/PdfForQtViewer/pdfviewermainwindow.cpp b/PdfForQtViewer/pdfviewermainwindow.cpp index b7f552d..d4f2f8f 100644 --- a/PdfForQtViewer/pdfviewermainwindow.cpp +++ b/PdfForQtViewer/pdfviewermainwindow.cpp @@ -59,9 +59,10 @@ namespace pdfviewer { -PDFViewerMainWindow::PDFViewerMainWindow(QWidget *parent) : +PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::PDFViewerMainWindow), + m_CMSManager(new pdf::PDFCMSManager(this)), m_settings(new PDFViewerSettings(this)), m_pdfWidget(nullptr), m_sidebarDockWidget(nullptr), @@ -544,8 +545,7 @@ void PDFViewerMainWindow::readSettings() restoreState(state); } - m_settings->readSettings(settings); - + m_settings->readSettings(settings, m_CMSManager->getDefaultSettings()); } void PDFViewerMainWindow::readActionSettings() @@ -976,10 +976,11 @@ void PDFViewerMainWindow::on_actionRendering_Errors_triggered() void PDFViewerMainWindow::on_actionOptions_triggered() { - PDFViewerSettingsDialog dialog(m_settings->getSettings(), getActions(), this); + PDFViewerSettingsDialog dialog(m_settings->getSettings(), m_settings->getColorManagementSystemSettings(), getActions(), m_CMSManager, this); if (dialog.exec() == QDialog::Accepted) { m_settings->setSettings(dialog.getSettings()); + m_settings->setColorManagementSystemSettings(dialog.getCMSSettings()); } } diff --git a/PdfForQtViewer/pdfviewermainwindow.h b/PdfForQtViewer/pdfviewermainwindow.h index 180876e..79f863a 100644 --- a/PdfForQtViewer/pdfviewermainwindow.h +++ b/PdfForQtViewer/pdfviewermainwindow.h @@ -139,6 +139,7 @@ private: }; Ui::PDFViewerMainWindow* ui; + pdf::PDFCMSManager* m_CMSManager; PDFViewerSettings* m_settings; pdf::PDFWidget* m_pdfWidget; QSharedPointer m_pdfDocument; diff --git a/PdfForQtViewer/pdfviewersettings.cpp b/PdfForQtViewer/pdfviewersettings.cpp index f7f9511..9c4e44a 100644 --- a/PdfForQtViewer/pdfviewersettings.cpp +++ b/PdfForQtViewer/pdfviewersettings.cpp @@ -26,11 +26,14 @@ const int PIXMAP_CACHE_LIMIT = QPixmapCache::cacheLimit(); void PDFViewerSettings::setSettings(const PDFViewerSettings::Settings& settings) { - m_settings = settings; - emit settingsChanged(); + if (m_settings != settings) + { + m_settings = settings; + emit settingsChanged(); + } } -void PDFViewerSettings::readSettings(QSettings& settings) +void PDFViewerSettings::readSettings(QSettings& settings, const pdf::PDFCMSSettings& defaultCMSSettings) { Settings defaultSettings; @@ -52,6 +55,18 @@ void PDFViewerSettings::readSettings(QSettings& settings) m_settings.m_allowLaunchURI = settings.value("allowLaunchURI", defaultSettings.m_allowLaunchURI).toBool(); settings.endGroup(); + settings.beginGroup("ColorManagementSystemSettings"); + m_colorManagementSystemSettings.system = static_cast(settings.value("system", int(defaultCMSSettings.system)).toInt()); + m_colorManagementSystemSettings.accuracy = static_cast(settings.value("accuracy", int(defaultCMSSettings.accuracy)).toInt()); + m_colorManagementSystemSettings.intent = static_cast(settings.value("intent", int(defaultCMSSettings.intent)).toInt()); + m_colorManagementSystemSettings.isBlackPointCompensationActive = settings.value("isBlackPointCompensationActive", defaultCMSSettings.isBlackPointCompensationActive).toBool(); + m_colorManagementSystemSettings.isWhitePaperColorTransformed = settings.value("isWhitePaperColorTransformed", defaultCMSSettings.isWhitePaperColorTransformed).toBool(); + m_colorManagementSystemSettings.outputCS = settings.value("outputCS", defaultCMSSettings.outputCS).toString(); + m_colorManagementSystemSettings.deviceGray = settings.value("deviceGray", defaultCMSSettings.deviceGray).toString(); + m_colorManagementSystemSettings.deviceRGB = settings.value("deviceRGB", defaultCMSSettings.deviceRGB).toString(); + m_colorManagementSystemSettings.deviceCMYK = settings.value("deviceCMYK", defaultCMSSettings.deviceCMYK).toString(); + settings.endGroup(); + emit settingsChanged(); } @@ -74,6 +89,18 @@ void PDFViewerSettings::writeSettings(QSettings& settings) settings.setValue("allowLaunchApplications", m_settings.m_allowLaunchApplications); settings.setValue("allowLaunchURI", m_settings.m_allowLaunchURI); settings.endGroup(); + + settings.beginGroup("ColorManagementSystemSettings"); + settings.setValue("system", int(m_colorManagementSystemSettings.system)); + settings.setValue("accuracy", int(m_colorManagementSystemSettings.accuracy)); + settings.setValue("intent", int(m_colorManagementSystemSettings.intent)); + settings.setValue("isBlackPointCompensationActive", m_colorManagementSystemSettings.isBlackPointCompensationActive); + settings.setValue("isWhitePaperColorTransformed", m_colorManagementSystemSettings.isWhitePaperColorTransformed); + settings.setValue("outputCS", m_colorManagementSystemSettings.outputCS); + settings.setValue("deviceGray", m_colorManagementSystemSettings.deviceGray); + settings.setValue("deviceRGB", m_colorManagementSystemSettings.deviceRGB); + settings.setValue("deviceCMYK", m_colorManagementSystemSettings.deviceCMYK); + settings.endGroup(); } QString PDFViewerSettings::getDirectory() const diff --git a/PdfForQtViewer/pdfviewersettings.h b/PdfForQtViewer/pdfviewersettings.h index 71af27b..fe421d1 100644 --- a/PdfForQtViewer/pdfviewersettings.h +++ b/PdfForQtViewer/pdfviewersettings.h @@ -19,6 +19,7 @@ #define PDFVIEWERSETTINGS_H #include "pdfrenderer.h" +#include "pdfcms.h" #include @@ -40,6 +41,9 @@ public: { Settings(); + bool operator==(const Settings&) const = default; + bool operator!=(const Settings&) const = default; + pdf::PDFRenderer::Features m_features; QString m_directory; pdf::RendererEngine m_rendererEngine; @@ -62,7 +66,7 @@ public: const Settings& getSettings() const { return m_settings; } void setSettings(const Settings& settings); - void readSettings(QSettings& settings); + void readSettings(QSettings& settings, const pdf::PDFCMSSettings& defaultCMSSettings); void writeSettings(QSettings& settings); QString getDirectory() const; @@ -94,14 +98,17 @@ public: int getFontCacheLimit() const { return m_settings.m_fontCacheLimit; } int getInstancedFontCacheLimit() const { return m_settings.m_instancedFontCacheLimit; } + const pdf::PDFCMSSettings& getColorManagementSystemSettings() const { return m_colorManagementSystemSettings; } + void setColorManagementSystemSettings(const pdf::PDFCMSSettings& settings) { m_colorManagementSystemSettings = settings; } + signals: void settingsChanged(); private: Settings m_settings; + pdf::PDFCMSSettings m_colorManagementSystemSettings; }; - } // namespace pdfviewer #endif // PDFVIEWERSETTINGS_H diff --git a/PdfForQtViewer/pdfviewersettingsdialog.cpp b/PdfForQtViewer/pdfviewersettingsdialog.cpp index a14c62d..3a202d2 100644 --- a/PdfForQtViewer/pdfviewersettingsdialog.cpp +++ b/PdfForQtViewer/pdfviewersettingsdialog.cpp @@ -29,10 +29,14 @@ namespace pdfviewer { -PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settings& settings, QList actions, QWidget *parent) : +PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settings& settings, + const pdf::PDFCMSSettings& cmsSettings, + QList actions, + pdf::PDFCMSManager* cmsManager, QWidget *parent) : QDialog(parent), ui(new Ui::PDFViewerSettingsDialog), m_settings(settings), + m_cmsSettings(cmsSettings), m_actions(), m_isLoadingData(false) { @@ -43,6 +47,7 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin new QListWidgetItem(QIcon(":/resources/shading.svg"), tr("Shading"), ui->optionsPagesWidget, ShadingSettings); new QListWidgetItem(QIcon(":/resources/cache.svg"), tr("Cache"), ui->optionsPagesWidget, CacheSettings); new QListWidgetItem(QIcon(":/resources/shortcuts.svg"), tr("Shortcuts"), ui->optionsPagesWidget, ShortcutSettings); + new QListWidgetItem(QIcon(":/resources/cms.svg"), tr("Colors"), ui->optionsPagesWidget, ColorManagementSystemSettings); new QListWidgetItem(QIcon(":/resources/security.svg"), tr("Security"), ui->optionsPagesWidget, SecuritySettings); ui->renderingEngineComboBox->addItem(tr("Software"), static_cast(pdf::RendererEngine::Software)); @@ -53,7 +58,33 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin ui->multisampleAntialiasingSamplesCountComboBox->addItem(QString::number(i), i); } - for (QWidget* widget : { ui->engineInfoLabel, ui->renderingInfoLabel, ui->securityInfoLabel }) + // Load CMS data + ui->cmsTypeComboBox->addItem(pdf::PDFCMSManager::getSystemName(pdf::PDFCMSSettings::System::Generic), int(pdf::PDFCMSSettings::System::Generic)); + ui->cmsTypeComboBox->addItem(pdf::PDFCMSManager::getSystemName(pdf::PDFCMSSettings::System::LittleCMS2), int(pdf::PDFCMSSettings::System::LittleCMS2)); + + ui->cmsRenderingIntentComboBox->addItem(tr("Auto"), int(pdf::RenderingIntent::Auto)); + ui->cmsRenderingIntentComboBox->addItem(tr("Perceptual"), int(pdf::RenderingIntent::Perceptual)); + ui->cmsRenderingIntentComboBox->addItem(tr("Relative colorimetric"), int(pdf::RenderingIntent::RelativeColorimetric)); + ui->cmsRenderingIntentComboBox->addItem(tr("Absolute colorimetric"), int(pdf::RenderingIntent::AbsoluteColorimetric)); + ui->cmsRenderingIntentComboBox->addItem(tr("Saturation"), int(pdf::RenderingIntent::Saturation)); + + ui->cmsAccuracyComboBox->addItem(tr("Low"), int(pdf::PDFCMSSettings::Accuracy::Low)); + ui->cmsAccuracyComboBox->addItem(tr("Medium"), int(pdf::PDFCMSSettings::Accuracy::Medium)); + ui->cmsAccuracyComboBox->addItem(tr("High"), int(pdf::PDFCMSSettings::Accuracy::High)); + + auto fillColorProfileList = [](QComboBox* comboBox, const pdf::PDFColorSpaceIdentifiers& identifiers) + { + for (const pdf::PDFColorSpaceIdentifier& identifier : identifiers) + { + comboBox->addItem(identifier.name, identifier.id); + } + }; + fillColorProfileList(ui->cmsOutputColorProfileComboBox, cmsManager->getOutputProfiles()); + fillColorProfileList(ui->cmsDeviceGrayColorProfileComboBox, cmsManager->getGrayProfiles()); + fillColorProfileList(ui->cmsDeviceRGBColorProfileComboBox, cmsManager->getRGBProfiles()); + fillColorProfileList(ui->cmsDeviceCMYKColorProfileComboBox, cmsManager->getCMYKProfiles()); + + for (QWidget* widget : { ui->engineInfoLabel, ui->renderingInfoLabel, ui->securityInfoLabel, ui->cmsInfoLabel }) { widget->setMinimumWidth(widget->sizeHint().width()); } @@ -120,6 +151,10 @@ void PDFViewerSettingsDialog::on_optionsPagesWidget_currentItemChanged(QListWidg ui->stackedWidget->setCurrentWidget(ui->shortcutsPage); break; + case ColorManagementSystemSettings: + ui->stackedWidget->setCurrentWidget(ui->cmsPage); + break; + case SecuritySettings: ui->stackedWidget->setCurrentWidget(ui->securityPage); break; @@ -183,6 +218,47 @@ void PDFViewerSettingsDialog::loadData() // Security ui->allowLaunchCheckBox->setChecked(m_settings.m_allowLaunchApplications); ui->allowRunURICheckBox->setChecked(m_settings.m_allowLaunchURI); + + // CMS + ui->cmsTypeComboBox->setCurrentIndex(ui->cmsTypeComboBox->findData(int(m_cmsSettings.system))); + if (m_cmsSettings.system != pdf::PDFCMSSettings::System::Generic) + { + ui->cmsRenderingIntentComboBox->setEnabled(true); + ui->cmsRenderingIntentComboBox->setCurrentIndex(ui->cmsRenderingIntentComboBox->findData(int(m_cmsSettings.intent))); + ui->cmsAccuracyComboBox->setEnabled(true); + ui->cmsAccuracyComboBox->setCurrentIndex(ui->cmsAccuracyComboBox->findData(int(m_cmsSettings.accuracy))); + ui->cmsIsBlackPointCompensationCheckBox->setEnabled(true); + ui->cmsIsBlackPointCompensationCheckBox->setChecked(m_cmsSettings.isBlackPointCompensationActive); + ui->cmsWhitePaperColorTransformedCheckBox->setEnabled(true); + ui->cmsWhitePaperColorTransformedCheckBox->setChecked(m_cmsSettings.isWhitePaperColorTransformed); + ui->cmsOutputColorProfileComboBox->setEnabled(true); + ui->cmsOutputColorProfileComboBox->setCurrentIndex(ui->cmsOutputColorProfileComboBox->findData(m_cmsSettings.outputCS)); + ui->cmsDeviceGrayColorProfileComboBox->setEnabled(true); + ui->cmsDeviceGrayColorProfileComboBox->setCurrentIndex(ui->cmsDeviceGrayColorProfileComboBox->findData(m_cmsSettings.deviceGray)); + ui->cmsDeviceRGBColorProfileComboBox->setEnabled(true); + ui->cmsDeviceRGBColorProfileComboBox->setCurrentIndex(ui->cmsDeviceRGBColorProfileComboBox->findData(m_cmsSettings.deviceRGB)); + ui->cmsDeviceCMYKColorProfileComboBox->setEnabled(true); + ui->cmsDeviceCMYKColorProfileComboBox->setCurrentIndex(ui->cmsDeviceCMYKColorProfileComboBox->findData(m_cmsSettings.deviceCMYK)); + } + else + { + ui->cmsRenderingIntentComboBox->setEnabled(false); + ui->cmsRenderingIntentComboBox->setCurrentIndex(-1); + ui->cmsAccuracyComboBox->setEnabled(false); + ui->cmsAccuracyComboBox->setCurrentIndex(-1); + ui->cmsIsBlackPointCompensationCheckBox->setEnabled(false); + ui->cmsIsBlackPointCompensationCheckBox->setChecked(false); + ui->cmsWhitePaperColorTransformedCheckBox->setEnabled(false); + ui->cmsWhitePaperColorTransformedCheckBox->setChecked(false); + ui->cmsOutputColorProfileComboBox->setEnabled(false); + ui->cmsOutputColorProfileComboBox->setCurrentIndex(-1); + ui->cmsDeviceGrayColorProfileComboBox->setEnabled(false); + ui->cmsDeviceGrayColorProfileComboBox->setCurrentIndex(-1); + ui->cmsDeviceRGBColorProfileComboBox->setEnabled(false); + ui->cmsDeviceRGBColorProfileComboBox->setCurrentIndex(-1); + ui->cmsDeviceCMYKColorProfileComboBox->setEnabled(false); + ui->cmsDeviceCMYKColorProfileComboBox->setCurrentIndex(-1); + } } void PDFViewerSettingsDialog::saveData() @@ -270,6 +346,42 @@ void PDFViewerSettingsDialog::saveData() { m_settings.m_instancedFontCacheLimit = ui->cachedInstancedFontLimitEdit->value(); } + else if (sender == ui->cmsTypeComboBox) + { + m_cmsSettings.system = static_cast(ui->cmsTypeComboBox->currentData().toInt()); + } + else if (sender == ui->cmsRenderingIntentComboBox) + { + m_cmsSettings.intent = static_cast(ui->cmsRenderingIntentComboBox->currentData().toInt()); + } + else if (sender == ui->cmsAccuracyComboBox) + { + m_cmsSettings.accuracy = static_cast(ui->cmsAccuracyComboBox->currentData().toInt()); + } + else if (sender == ui->cmsIsBlackPointCompensationCheckBox) + { + m_cmsSettings.isBlackPointCompensationActive = ui->cmsIsBlackPointCompensationCheckBox->isChecked(); + } + else if (sender == ui->cmsWhitePaperColorTransformedCheckBox) + { + m_cmsSettings.isWhitePaperColorTransformed = ui->cmsWhitePaperColorTransformedCheckBox->isChecked(); + } + else if (sender == ui->cmsOutputColorProfileComboBox) + { + m_cmsSettings.outputCS = ui->cmsOutputColorProfileComboBox->currentData().toString(); + } + else if (sender == ui->cmsDeviceGrayColorProfileComboBox) + { + m_cmsSettings.deviceGray = ui->cmsDeviceGrayColorProfileComboBox->currentData().toString(); + } + else if (sender == ui->cmsDeviceRGBColorProfileComboBox) + { + m_cmsSettings.deviceRGB = ui->cmsDeviceRGBColorProfileComboBox->currentData().toString(); + } + else if (sender == ui->cmsDeviceCMYKColorProfileComboBox) + { + m_cmsSettings.deviceCMYK = ui->cmsDeviceCMYKColorProfileComboBox->currentData().toString(); + } loadData(); } diff --git a/PdfForQtViewer/pdfviewersettingsdialog.h b/PdfForQtViewer/pdfviewersettingsdialog.h index cff9c62..7be8093 100644 --- a/PdfForQtViewer/pdfviewersettingsdialog.h +++ b/PdfForQtViewer/pdfviewersettingsdialog.h @@ -37,7 +37,17 @@ class PDFViewerSettingsDialog : public QDialog Q_OBJECT public: - explicit PDFViewerSettingsDialog(const PDFViewerSettings::Settings& settings, QList actions, QWidget* parent); + /// Constructor + /// \param settings Viewer settings + /// \param cmsSettings Color management system settings + /// \param actions Actions + /// \param cmsManager CMS manager + /// \param parent Parent widget + explicit PDFViewerSettingsDialog(const PDFViewerSettings::Settings& settings, + const pdf::PDFCMSSettings& cmsSettings, + QList actions, + pdf::PDFCMSManager* cmsManager, + QWidget* parent); virtual ~PDFViewerSettingsDialog() override; virtual void accept() override; @@ -49,10 +59,12 @@ public: ShadingSettings, CacheSettings, ShortcutSettings, + ColorManagementSystemSettings, SecuritySettings }; const PDFViewerSettings::Settings& getSettings() const { return m_settings; } + const pdf::PDFCMSSettings& getCMSSettings() const { return m_cmsSettings; } private slots: void on_optionsPagesWidget_currentItemChanged(QListWidgetItem* current, QListWidgetItem* previous); @@ -66,6 +78,7 @@ private: Ui::PDFViewerSettingsDialog* ui; PDFViewerSettings::Settings m_settings; + pdf::PDFCMSSettings m_cmsSettings; QList m_actions; bool m_isLoadingData; }; diff --git a/PdfForQtViewer/pdfviewersettingsdialog.ui b/PdfForQtViewer/pdfviewersettingsdialog.ui index 504e76a..9ef3719 100644 --- a/PdfForQtViewer/pdfviewersettingsdialog.ui +++ b/PdfForQtViewer/pdfviewersettingsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 720 - 538 + 742 + 573 @@ -26,7 +26,7 @@ - 4 + 5 @@ -564,6 +564,144 @@ + + + + + + Color management system settings + + + + + + + + + + + Enabled + + + + + + + White paper color transformed + + + + + + + Device RGB color profile + + + + + + + Output color profile + + + + + + + Enabled + + + + + + + Device gray color profile + + + + + + + + + + Rendering intent + + + + + + + + + + Accuracy + + + + + + + Color management system + + + + + + + Black point compensation + + + + + + + Device CMYK color profile + + + + + + + + + + + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Color management system </span>manages input and output color profiles and color transformations. It enables to perceive real colors defined in PDF document. Select 'Generic' to disable this functionality for fast color transform. <span style=" font-weight:600;">Rendering intent</span> affects how colors are transformed. Rendering intents are usually defined in content streams in PDF document, but you can override them, if you select other intent, than 'Auto'. <span style=" font-weight:600;">Accuracy </span>affects how precise the color transformation would be, at the cost of more memory consumption. <span style=" font-weight:600;">Black point compensation</span> compensates black colors out of gamut. <span style=" font-weight:600;">White paper color transformed </span>affects color of the underlying white paper - it transforms pure white from device RGB profile to output profile, if enabled.</p><p><span style=" font-weight:600;">Output color profile</span> defines output (target) rendering profile. This profile should be color space, in which your screen is displaying colors. You can also define color spaces for <span style=" font-weight:600;">gray/RGB/CMYK</span> device color spaces, which are used to transform gray/RGB/CMYK colors to the output color profile.</p></body></html> + + + true + + + + + + + Qt::Vertical + + + + 20 + 41 + + + + + + + + + diff --git a/PdfForQtViewer/resources/cms.svg b/PdfForQtViewer/resources/cms.svg new file mode 100644 index 0000000..998597e --- /dev/null +++ b/PdfForQtViewer/resources/cms.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + image/svg+xml + + + + + + Jakub Melka + + + + + + + + + + + + + + + + + + + +