Color spaces and images - bugfixing, compliance to PDF 2.0 specification

This commit is contained in:
Jakub Melka 2020-08-15 15:57:55 +02:00
parent 8eedb45576
commit 7129793109
5 changed files with 115 additions and 8 deletions

View File

@ -1401,7 +1401,7 @@ PDFSeparationColorSpace::PDFSeparationColorSpace(QByteArray&& colorName, PDFColo
QColor PDFSeparationColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
{
return getColor(PDFColor(0.0f), cms, intent, reporter);
return getColor(PDFColor(1.0f), cms, intent, reporter);
}
QColor PDFSeparationColorSpace::getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
@ -1528,9 +1528,10 @@ PDFDeviceNColorSpace::PDFDeviceNColorSpace(PDFDeviceNColorSpace::Type type,
m_processColorSpace(qMove(processColorSpace)),
m_tintTransform(qMove(tintTransform)),
m_colorantsPrintingOrder(qMove(colorantsPrintingOrder)),
m_processColorSpaceComponents(qMove(processColorSpaceComponents))
m_processColorSpaceComponents(qMove(processColorSpaceComponents)),
m_isNone(false)
{
m_isNone = std::all_of(m_colorants.cbegin(), m_colorants.cend(), [](const auto& colorant) { return colorant.name == "None"; });
}
QColor PDFDeviceNColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
@ -1550,6 +1551,13 @@ QColor PDFDeviceNColorSpace::getDefaultColor(const PDFCMS* cms, RenderingIntent
QColor PDFDeviceNColorSpace::getColor(const PDFColor& color, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const
{
// According to the PDF 2.0 specification, DeviceN color space, with all colorant name "None"
// should not produce any visible output.
if (m_isNone)
{
return Qt::transparent;
}
// Input values
std::vector<double> inputColor(color.size(), 0.0);
for (size_t i = 0, count = inputColor.size(); i < count; ++i)

View File

@ -678,6 +678,14 @@ public:
/// Returns type of DeviceN color space
Type getType() const { return m_type; }
const Colorants& getColorants() const { return m_colorants; }
const PDFColorSpacePointer& getAlternateColorSpace() const { return m_alternateColorSpace; }
const PDFColorSpacePointer& getProcessColorSpace() const { return m_processColorSpace; }
const PDFFunctionPtr& getTintTransform() const { return m_tintTransform; }
const std::vector<QByteArray>& getPrintingOrder() const { return m_colorantsPrintingOrder; }
const std::vector<QByteArray>& getProcessColorSpaceComponents() const { return m_processColorSpaceComponents; }
bool isNone() const { return m_isNone; }
/// Creates DeviceN color space from provided values.
/// \param colorSpaceDictionary Color space dictionary
/// \param document Document
@ -698,6 +706,7 @@ private:
PDFFunctionPtr m_tintTransform;
std::vector<QByteArray> m_colorantsPrintingOrder;
std::vector<QByteArray> m_processColorSpaceComponents;
bool m_isNone;
};
class PDFPatternColorSpace : public PDFAbstractColorSpace

View File

@ -73,6 +73,17 @@ PDFImage PDFImage::createImage(const PDFDocument* document,
bool imageMask = loader.readBooleanFromDictionary(dictionary, "ImageMask", false);
std::vector<PDFReal> matte = loader.readNumberArrayFromDictionary(dictionary, "Matte");
PDFInteger sMaskInData = loader.readIntegerFromDictionary(dictionary, "SMaskInData", 0);
image.m_interpolate = loader.readBooleanFromDictionary(dictionary, "Interpolate", false);
image.m_alternates = loader.readObjectList<PDFAlternateImage>(dictionary->get("Alternates"));
image.m_name = loader.readNameFromDictionary(dictionary, "Name");
image.m_structuralParent = loader.readIntegerFromDictionary(dictionary, "StructParent", 0);
image.m_webCaptureContentSetId = loader.readStringFromDictionary(dictionary, "ID");
image.m_OPI = dictionary->get("OPI");
image.m_OC = dictionary->get("OC");
image.m_metadata = dictionary->get("Metadata");
image.m_associatedFiles = dictionary->get("AF");
image.m_measure = dictionary->get("Measure");
image.m_pointData = dictionary->get("PtData");
if (isSoftMask && (imageMask || dictionary->hasKey("Mask") || dictionary->hasKey("SMask")))
{
@ -706,9 +717,9 @@ PDFImage PDFImage::createImage(const PDFDocument* document,
}
else if (imageMask)
{
// We intentionally have 8 bits in the following code, because if ImageMask is set to true, then "BitsPerComponent"
// should have always value of 1.
const unsigned int bitsPerComponent = static_cast<unsigned int>(loader.readIntegerFromDictionary(dictionary, "BitsPerComponent", 8));
// If ImageMask is set to true, then "BitsPerComponent" should have always value of 1.
// If this entry is not specified, then the value should be implicitly 1.
const unsigned int bitsPerComponent = static_cast<unsigned int>(loader.readIntegerFromDictionary(dictionary, "BitsPerComponent", 1));
if (bitsPerComponent != 1)
{
@ -840,6 +851,19 @@ OPJ_OFF_T PDFJPEG2000ImageData::skip(OPJ_OFF_T p_nb_bytes, void* p_user_data)
return length;
}
// Implement image rendering intent
PDFAlternateImage PDFAlternateImage::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDFAlternateImage result;
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(storage);
result.m_image = loader.readReferenceFromDictionary(dictionary, "Image");
result.m_oc = loader.readReferenceFromDictionary(dictionary, "OC");
result.m_defaultForPrinting = loader.readBooleanFromDictionary(dictionary, "DefaultForPrinting", false);
}
return result;
}
} // namespace pdf

View File

@ -18,6 +18,7 @@
#ifndef PDFIMAGE_H
#define PDFIMAGE_H
#include "pdfobject.h"
#include "pdfcolorspaces.h"
#include <QByteArray>
@ -28,8 +29,31 @@ namespace pdf
{
class PDFStream;
class PDFDocument;
class PDFObjectStorage;
class PDFRenderErrorReporter;
/// Alternate image object. Defines alternate image, which
/// can be shown in some circumstances instead of main image.
class PDFAlternateImage
{
public:
explicit inline PDFAlternateImage() = default;
/// Parses alternate image dictionary object.
/// \param storage Object storage
/// \param object Alternate image object
static PDFAlternateImage parse(const PDFObjectStorage* storage, PDFObject object);
PDFObjectReference getImage() const { return m_image; }
PDFObjectReference getOc() const { return m_oc; }
bool isDefaultForPrinting() const { return m_defaultForPrinting; }
private:
PDFObjectReference m_image;
PDFObjectReference m_oc;
bool m_defaultForPrinting = false;
};
class PDFImage
{
public:
@ -54,6 +78,33 @@ public:
/// Returns rendering intent of the image
RenderingIntent getRenderingIntent() const { return m_renderingIntent; }
/// Color space of image samples
const PDFColorSpacePointer& getColorSpace() const { return m_colorSpace; }
/// Should PDF processor perform interpolation on this image?
bool isInterpolated() const { return m_interpolate; }
/// Array of alternate images
const std::vector<PDFAlternateImage>& getAlternates() const { return m_alternates; }
/// Returns name of image, under which is referenced in resources dictionary.
/// It was mandatory in PDF 1.0, otherwise it is optional and now it is deprecated
/// in PDF 2.0 specification.
const QByteArray& getName() const { return m_name; }
/// Returns idenfitier to structural parent in structure tree
PDFInteger getStructuralParent() const { return m_structuralParent; }
/// Web capture content set identifier
const QByteArray& getWebCaptureContentSetID() const { return m_webCaptureContentSetId; }
const PDFObject& getOPI() const { return m_OPI; }
const PDFObject& getOC() const { return m_OC; }
const PDFObject& getMetadata() const { return m_metadata; }
const PDFObject& getAssociatedFiles() const { return m_associatedFiles; }
const PDFObject& getMeasure() const { return m_measure; }
const PDFObject& getPointData() const { return m_pointData; }
private:
PDFImage() = default;
@ -61,6 +112,17 @@ private:
PDFImageData m_softMask;
PDFColorSpacePointer m_colorSpace;
RenderingIntent m_renderingIntent = RenderingIntent::Perceptual;
bool m_interpolate = false;
std::vector<PDFAlternateImage> m_alternates;
QByteArray m_name;
QByteArray m_webCaptureContentSetId;
PDFInteger m_structuralParent = 0;
PDFObject m_OPI;
PDFObject m_OC;
PDFObject m_metadata;
PDFObject m_associatedFiles;
PDFObject m_measure;
PDFObject m_pointData;
};
} // namespace pdf

View File

@ -500,7 +500,11 @@ void PDFPageContentProcessor::processContent(const QByteArray& content)
{ "H", "Height" },
{ "IM", "ImageMask" },
{ "I", "Interpolate" },
{ "W", "Width" }
{ "W", "Width" },
{ "L", "Length" },
{ "G", "DeviceGray" },
{ "RGB", "DeviceRGB" },
{ "CMYK", "DeviceCMYK" }
};
std::shared_ptr<PDFDictionary> dictionarySharedPointer = std::make_shared<PDFDictionary>();