Color setting operators

This commit is contained in:
Jakub Melka
2019-02-17 18:01:22 +01:00
parent 8e766376bc
commit 57a9582ffd
5 changed files with 886 additions and 109 deletions

View File

@ -23,6 +23,11 @@
namespace pdf namespace pdf
{ {
QColor PDFDeviceGrayColorSpace::getDefaultColor() const
{
return QColor(Qt::black);
}
QColor PDFDeviceGrayColorSpace::getColor(const PDFColor& color) const QColor PDFDeviceGrayColorSpace::getColor(const PDFColor& color) const
{ {
Q_ASSERT(color.size() == getColorComponentCount()); Q_ASSERT(color.size() == getColorComponentCount());
@ -39,6 +44,11 @@ size_t PDFDeviceGrayColorSpace::getColorComponentCount() const
return 1; return 1;
} }
QColor PDFDeviceRGBColorSpace::getDefaultColor() const
{
return QColor(Qt::black);
}
QColor PDFDeviceRGBColorSpace::getColor(const PDFColor& color) const QColor PDFDeviceRGBColorSpace::getColor(const PDFColor& color) const
{ {
Q_ASSERT(color.size() == getColorComponentCount()); Q_ASSERT(color.size() == getColorComponentCount());
@ -51,6 +61,11 @@ size_t PDFDeviceRGBColorSpace::getColorComponentCount() const
return 3; return 3;
} }
QColor PDFDeviceCMYKColorSpace::getDefaultColor() const
{
return QColor(Qt::black);
}
QColor PDFDeviceCMYKColorSpace::getColor(const PDFColor& color) const QColor PDFDeviceCMYKColorSpace::getColor(const PDFColor& color) const
{ {
Q_ASSERT(color.size() == getColorComponentCount()); Q_ASSERT(color.size() == getColorComponentCount());
@ -77,6 +92,13 @@ PDFColorSpacePointer PDFAbstractColorSpace::createColorSpace(const PDFDictionary
return createColorSpaceImpl(colorSpaceDictionary, document, colorSpace, COLOR_SPACE_MAX_LEVEL_OF_RECURSION); return createColorSpaceImpl(colorSpaceDictionary, document, colorSpace, COLOR_SPACE_MAX_LEVEL_OF_RECURSION);
} }
PDFColorSpacePointer PDFAbstractColorSpace::createDeviceColorSpaceByName(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const QByteArray& name)
{
return createDeviceColorSpaceByNameImpl(colorSpaceDictionary, document, name, COLOR_SPACE_MAX_LEVEL_OF_RECURSION);
}
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictionary* colorSpaceDictionary, PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document, const PDFDocument* document,
const PDFObject& colorSpace, const PDFObject& colorSpace,
@ -89,41 +111,7 @@ PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictio
if (colorSpace.isName()) if (colorSpace.isName())
{ {
QByteArray name = colorSpace.getString(); return createDeviceColorSpaceByNameImpl(colorSpaceDictionary, document, colorSpace.getString(), recursion);
if (name == COLOR_SPACE_NAME_DEVICE_GRAY || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_GRAY)
{
if (colorSpaceDictionary->hasKey(COLOR_SPACE_NAME_DEFAULT_GRAY))
{
return createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(colorSpaceDictionary->get(COLOR_SPACE_NAME_DEFAULT_GRAY)), recursion);
}
else
{
return PDFColorSpacePointer(new PDFDeviceGrayColorSpace());
}
}
else if (name == COLOR_SPACE_NAME_DEVICE_RGB || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_RGB)
{
if (colorSpaceDictionary->hasKey(COLOR_SPACE_NAME_DEFAULT_RGB))
{
return createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(colorSpaceDictionary->get(COLOR_SPACE_NAME_DEFAULT_RGB)), recursion);
}
else
{
return PDFColorSpacePointer(new PDFDeviceRGBColorSpace());
}
}
else if (name == COLOR_SPACE_NAME_DEVICE_CMYK || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_CMYK)
{
if (colorSpaceDictionary->hasKey(COLOR_SPACE_NAME_DEFAULT_CMYK))
{
return createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(colorSpaceDictionary->get(COLOR_SPACE_NAME_DEFAULT_CMYK)), recursion);
}
else
{
return PDFColorSpacePointer(new PDFDeviceCMYKColorSpace());
}
}
} }
else if (colorSpace.isArray()) else if (colorSpace.isArray())
{ {
@ -175,6 +163,11 @@ PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictio
return PDFICCBasedColorSpace::createICCBasedColorSpace(colorSpaceDictionary, document, stream, recursion); return PDFICCBasedColorSpace::createICCBasedColorSpace(colorSpaceDictionary, document, stream, recursion);
} }
if (name == COLOR_SPACE_NAME_INDEXED && count == 4)
{
return PDFIndexedColorSpace::createIndexedColorSpace(colorSpaceDictionary, document, array, recursion);
}
// Try to just load by standard way - we can have "standard" color space stored in array // Try to just load by standard way - we can have "standard" color space stored in array
return createColorSpaceImpl(colorSpaceDictionary, document, colorSpaceIdentifier, recursion); return createColorSpaceImpl(colorSpaceDictionary, document, colorSpaceIdentifier, recursion);
} }
@ -185,6 +178,54 @@ PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictio
return PDFColorSpacePointer(); return PDFColorSpacePointer();
} }
PDFColorSpacePointer PDFAbstractColorSpace::createDeviceColorSpaceByNameImpl(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const QByteArray& name,
int recursion)
{
if (--recursion <= 0)
{
throw PDFParserException(PDFTranslationContext::tr("Can't load color space, because color space structure is too complex."));
}
if (name == COLOR_SPACE_NAME_DEVICE_GRAY || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_GRAY)
{
if (colorSpaceDictionary->hasKey(COLOR_SPACE_NAME_DEFAULT_GRAY))
{
return createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(colorSpaceDictionary->get(COLOR_SPACE_NAME_DEFAULT_GRAY)), recursion);
}
else
{
return PDFColorSpacePointer(new PDFDeviceGrayColorSpace());
}
}
else if (name == COLOR_SPACE_NAME_DEVICE_RGB || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_RGB)
{
if (colorSpaceDictionary->hasKey(COLOR_SPACE_NAME_DEFAULT_RGB))
{
return createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(colorSpaceDictionary->get(COLOR_SPACE_NAME_DEFAULT_RGB)), recursion);
}
else
{
return PDFColorSpacePointer(new PDFDeviceRGBColorSpace());
}
}
else if (name == COLOR_SPACE_NAME_DEVICE_CMYK || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_CMYK)
{
if (colorSpaceDictionary->hasKey(COLOR_SPACE_NAME_DEFAULT_CMYK))
{
return createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(colorSpaceDictionary->get(COLOR_SPACE_NAME_DEFAULT_CMYK)), recursion);
}
else
{
return PDFColorSpacePointer(new PDFDeviceCMYKColorSpace());
}
}
throw PDFParserException(PDFTranslationContext::tr("Invalid color space."));
return PDFColorSpacePointer();
}
/// Conversion matrix from XYZ space to RGB space. Values are taken from this article: /// Conversion matrix from XYZ space to RGB space. Values are taken from this article:
/// https://en.wikipedia.org/wiki/SRGB#The_sRGB_transfer_function_.28.22gamma.22.29 /// https://en.wikipedia.org/wiki/SRGB#The_sRGB_transfer_function_.28.22gamma.22.29
static constexpr const PDFColorComponentMatrix<3, 3> matrixXYZtoRGB( static constexpr const PDFColorComponentMatrix<3, 3> matrixXYZtoRGB(
@ -198,10 +239,20 @@ PDFColor3 PDFAbstractColorSpace::convertXYZtoRGB(const PDFColor3& xyzColor)
return matrixXYZtoRGB * xyzColor; return matrixXYZtoRGB * xyzColor;
} }
PDFCalGrayColorSpace::PDFCalGrayColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent gamma) :
QColor PDFXYZColorSpace::getDefaultColor() const
{
PDFColor color;
const size_t componentCount = getColorComponentCount();
for (size_t i = 0; i < componentCount; ++i)
{
color.push_back(0.0f);
}
return getColor(color);
}
PDFXYZColorSpace::PDFXYZColorSpace(PDFColor3 whitePoint) :
m_whitePoint(whitePoint), m_whitePoint(whitePoint),
m_blackPoint(blackPoint),
m_gamma(gamma),
m_correctionCoefficients() m_correctionCoefficients()
{ {
PDFColor3 mappedWhitePoint = convertXYZtoRGB(m_whitePoint); PDFColor3 mappedWhitePoint = convertXYZtoRGB(m_whitePoint);
@ -211,6 +262,14 @@ PDFCalGrayColorSpace::PDFCalGrayColorSpace(PDFColor3 whitePoint, PDFColor3 black
m_correctionCoefficients[2] = 1.0f / mappedWhitePoint[2]; m_correctionCoefficients[2] = 1.0f / mappedWhitePoint[2];
} }
PDFCalGrayColorSpace::PDFCalGrayColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent gamma) :
PDFXYZColorSpace(whitePoint),
m_blackPoint(blackPoint),
m_gamma(gamma)
{
}
QColor PDFCalGrayColorSpace::getColor(const PDFColor& color) const QColor PDFCalGrayColorSpace::getColor(const PDFColor& color) const
{ {
Q_ASSERT(color.size() == getColorComponentCount()); Q_ASSERT(color.size() == getColorComponentCount());
@ -244,17 +303,12 @@ PDFColorSpacePointer PDFCalGrayColorSpace::createCalGrayColorSpace(const PDFDocu
} }
PDFCalRGBColorSpace::PDFCalRGBColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColor3 gamma, PDFColorComponentMatrix_3x3 matrix) : PDFCalRGBColorSpace::PDFCalRGBColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColor3 gamma, PDFColorComponentMatrix_3x3 matrix) :
m_whitePoint(whitePoint), PDFXYZColorSpace(whitePoint),
m_blackPoint(blackPoint), m_blackPoint(blackPoint),
m_gamma(gamma), m_gamma(gamma),
m_matrix(matrix), m_matrix(matrix)
m_correctionCoefficients()
{ {
PDFColor3 mappedWhitePoint = convertXYZtoRGB(m_whitePoint);
m_correctionCoefficients[0] = 1.0f / mappedWhitePoint[0];
m_correctionCoefficients[1] = 1.0f / mappedWhitePoint[1];
m_correctionCoefficients[2] = 1.0f / mappedWhitePoint[2];
} }
QColor PDFCalRGBColorSpace::getColor(const PDFColor& color) const QColor PDFCalRGBColorSpace::getColor(const PDFColor& color) const
@ -299,19 +353,14 @@ PDFLabColorSpace::PDFLabColorSpace(PDFColor3 whitePoint,
PDFColorComponent aMax, PDFColorComponent aMax,
PDFColorComponent bMin, PDFColorComponent bMin,
PDFColorComponent bMax) : PDFColorComponent bMax) :
m_whitePoint(whitePoint), PDFXYZColorSpace(whitePoint),
m_blackPoint(blackPoint), m_blackPoint(blackPoint),
m_aMin(aMin), m_aMin(aMin),
m_aMax(aMax), m_aMax(aMax),
m_bMin(bMin), m_bMin(bMin),
m_bMax(bMax), m_bMax(bMax)
m_correctionCoefficients()
{ {
PDFColor3 mappedWhitePoint = convertXYZtoRGB(m_whitePoint);
m_correctionCoefficients[0] = 1.0f / mappedWhitePoint[0];
m_correctionCoefficients[1] = 1.0f / mappedWhitePoint[1];
m_correctionCoefficients[2] = 1.0f / mappedWhitePoint[2];
} }
QColor PDFLabColorSpace::getColor(const PDFColor& color) const QColor PDFLabColorSpace::getColor(const PDFColor& color) const
@ -384,6 +433,17 @@ PDFICCBasedColorSpace::PDFICCBasedColorSpace(PDFColorSpacePointer alternateColor
} }
QColor PDFICCBasedColorSpace::getDefaultColor() const
{
PDFColor color;
const size_t componentCount = getColorComponentCount();
for (size_t i = 0; i < componentCount; ++i)
{
color.push_back(0.0f);
}
return getColor(color);
}
QColor PDFICCBasedColorSpace::getColor(const PDFColor& color) const QColor PDFICCBasedColorSpace::getColor(const PDFColor& color) const
{ {
Q_ASSERT(color.size() == getColorComponentCount()); Q_ASSERT(color.size() == getColorComponentCount());
@ -477,4 +537,91 @@ PDFColorSpacePointer PDFICCBasedColorSpace::createICCBasedColorSpace(const PDFDi
return PDFColorSpacePointer(new PDFICCBasedColorSpace(qMove(alternateColorSpace), ranges)); return PDFColorSpacePointer(new PDFICCBasedColorSpace(qMove(alternateColorSpace), ranges));
} }
PDFIndexedColorSpace::PDFIndexedColorSpace(PDFColorSpacePointer baseColorSpace, QByteArray&& colors, int maxValue) :
m_baseColorSpace(qMove(baseColorSpace)),
m_colors(qMove(colors)),
m_maxValue(maxValue)
{
}
QColor PDFIndexedColorSpace::getDefaultColor() const
{
return getColor(PDFColor(0.0f));
}
QColor PDFIndexedColorSpace::getColor(const PDFColor& color) const
{
// Indexed color space value must have exactly one component!
Q_ASSERT(color.size() == 1);
const int colorIndex = qBound(MIN_VALUE, static_cast<int>(color[0]), m_maxValue);
const int componentCount = static_cast<int>(m_baseColorSpace->getColorComponentCount());
const int byteOffset = colorIndex * componentCount;
// We must point into the array. Check first and last component.
Q_ASSERT(byteOffset + componentCount - 1 < m_colors.size());
PDFColor decodedColor;
const char* bytePointer = m_colors.constData() + byteOffset;
for (int i = 0; i < componentCount; ++i)
{
const unsigned char value = *bytePointer++;
const PDFColorComponent component = static_cast<PDFColorComponent>(value) / 255.0f;
decodedColor.push_back(component);
}
return m_baseColorSpace->getColor(decodedColor);
}
size_t PDFIndexedColorSpace::getColorComponentCount() const
{
return 1;
}
PDFColorSpacePointer PDFIndexedColorSpace::createIndexedColorSpace(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const PDFArray* array,
int recursion)
{
Q_ASSERT(array->getCount() == 4);
// Read base color space
PDFColorSpacePointer baseColorSpace = PDFAbstractColorSpace::createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(array->getItem(1)), recursion);
if (!baseColorSpace)
{
throw PDFParserException(PDFTranslationContext::tr("Can't determine base color space for indexed color space."));
}
// Read maximum value
PDFDocumentDataLoaderDecorator loader(document);
const int maxValue = qBound<int>(MIN_VALUE, loader.readInteger(array->getItem(2), 0), MAX_VALUE);
// Read stream/byte string with corresponding color values
QByteArray colors;
const PDFObject& colorDataObject = document->getObject(array->getItem(3));
if (colorDataObject.isString())
{
colors = colorDataObject.getString();
}
else if (colorDataObject.isStream())
{
colors = document->getDecodedStream(colorDataObject.getStream());
}
// Check, if we have enough colors
const int colorCount = maxValue - MIN_VALUE + 1;
const int componentCount = static_cast<int>(baseColorSpace->getColorComponentCount());
const int byteCount = colorCount * componentCount;
if (byteCount != colors.size())
{
throw PDFParserException(PDFTranslationContext::tr("Invalid colors for indexed color space. Color space has %1 colors, %2 color components and must have %3 size. Provided size is %4.").arg(colorCount).arg(componentCount).arg(byteCount).arg(colors.size()));
}
return PDFColorSpacePointer(new PDFIndexedColorSpace(qMove(baseColorSpace), qMove(colors), maxValue));
}
} // namespace pdf } // namespace pdf

View File

@ -25,6 +25,7 @@
namespace pdf namespace pdf
{ {
class PDFArray;
class PDFObject; class PDFObject;
class PDFStream; class PDFStream;
class PDFDocument; class PDFDocument;
@ -37,6 +38,8 @@ using PDFColorSpacePointer = QSharedPointer<PDFAbstractColorSpace>;
static constexpr const int COLOR_SPACE_MAX_LEVEL_OF_RECURSION = 12; static constexpr const int COLOR_SPACE_MAX_LEVEL_OF_RECURSION = 12;
static constexpr const char* COLOR_SPACE_DICTIONARY = "ColorSpace";
static constexpr const char* COLOR_SPACE_NAME_DEVICE_GRAY = "DeviceGray"; static constexpr const char* COLOR_SPACE_NAME_DEVICE_GRAY = "DeviceGray";
static constexpr const char* COLOR_SPACE_NAME_DEVICE_RGB = "DeviceRGB"; static constexpr const char* COLOR_SPACE_NAME_DEVICE_RGB = "DeviceRGB";
static constexpr const char* COLOR_SPACE_NAME_DEVICE_CMYK = "DeviceCMYK"; static constexpr const char* COLOR_SPACE_NAME_DEVICE_CMYK = "DeviceCMYK";
@ -53,6 +56,7 @@ static constexpr const char* COLOR_SPACE_NAME_CAL_GRAY = "CalGray";
static constexpr const char* COLOR_SPACE_NAME_CAL_RGB = "CalRGB"; static constexpr const char* COLOR_SPACE_NAME_CAL_RGB = "CalRGB";
static constexpr const char* COLOR_SPACE_NAME_LAB = "Lab"; static constexpr const char* COLOR_SPACE_NAME_LAB = "Lab";
static constexpr const char* COLOR_SPACE_NAME_ICCBASED = "ICCBased"; static constexpr const char* COLOR_SPACE_NAME_ICCBASED = "ICCBased";
static constexpr const char* COLOR_SPACE_NAME_INDEXED = "Indexed";
static constexpr const char* CAL_WHITE_POINT = "WhitePoint"; static constexpr const char* CAL_WHITE_POINT = "WhitePoint";
static constexpr const char* CAL_BLACK_POINT = "BlackPoint"; static constexpr const char* CAL_BLACK_POINT = "BlackPoint";
@ -108,6 +112,7 @@ public:
explicit PDFAbstractColorSpace() = default; explicit PDFAbstractColorSpace() = default;
virtual ~PDFAbstractColorSpace() = default; virtual ~PDFAbstractColorSpace() = default;
virtual QColor getDefaultColor() const = 0;
virtual QColor getColor(const PDFColor& color) const = 0; virtual QColor getColor(const PDFColor& color) const = 0;
virtual size_t getColorComponentCount() const = 0; virtual size_t getColorComponentCount() const = 0;
@ -120,6 +125,15 @@ public:
const PDFDocument* document, const PDFDocument* document,
const PDFObject& colorSpace); const PDFObject& colorSpace);
/// Creates device color space by name. Color space can be created by this function only, if
/// it is simple - one of the basic device color spaces (gray, RGB or CMYK).
/// \param colorSpaceDictionary Dictionary containing color spaces of the page
/// \param document Document (for loading objects)
/// \param name Name of the color space
static PDFColorSpacePointer createDeviceColorSpaceByName(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const QByteArray& name);
protected: protected:
/// Clips the color component to range [0, 1] /// Clips the color component to range [0, 1]
static constexpr PDFColorComponent clip01(PDFColorComponent component) { return qBound<PDFColorComponent>(0.0, component, 1.0); } static constexpr PDFColorComponent clip01(PDFColorComponent component) { return qBound<PDFColorComponent>(0.0, component, 1.0); }
@ -148,6 +162,16 @@ protected:
const PDFObject& colorSpace, const PDFObject& colorSpace,
int recursion); int recursion);
/// Creates device color space by name. Color space can be created by this function only, if
/// it is simple - one of the basic device color spaces (gray, RGB or CMYK).
/// \param colorSpaceDictionary Dictionary containing color spaces of the page
/// \param document Document (for loading objects)
/// \param name Name of the color space
static PDFColorSpacePointer createDeviceColorSpaceByNameImpl(const PDFDictionary* colorSpaceDictionary,
const PDFDocument* document,
const QByteArray& name,
int recursion);
/// Converts XYZ value to the standard RGB value (linear). No gamma correction is applied. /// Converts XYZ value to the standard RGB value (linear). No gamma correction is applied.
/// Default transformation matrix is applied. /// Default transformation matrix is applied.
/// \param xyzColor Color in XYZ space /// \param xyzColor Color in XYZ space
@ -212,6 +236,7 @@ public:
explicit PDFDeviceGrayColorSpace() = default; explicit PDFDeviceGrayColorSpace() = default;
virtual ~PDFDeviceGrayColorSpace() = default; virtual ~PDFDeviceGrayColorSpace() = default;
virtual QColor getDefaultColor() const override;
virtual QColor getColor(const PDFColor& color) const override; virtual QColor getColor(const PDFColor& color) const override;
virtual size_t getColorComponentCount() const override; virtual size_t getColorComponentCount() const override;
}; };
@ -222,6 +247,7 @@ public:
explicit PDFDeviceRGBColorSpace() = default; explicit PDFDeviceRGBColorSpace() = default;
virtual ~PDFDeviceRGBColorSpace() = default; virtual ~PDFDeviceRGBColorSpace() = default;
virtual QColor getDefaultColor() const override;
virtual QColor getColor(const PDFColor& color) const override; virtual QColor getColor(const PDFColor& color) const override;
virtual size_t getColorComponentCount() const override; virtual size_t getColorComponentCount() const override;
}; };
@ -232,11 +258,30 @@ public:
explicit PDFDeviceCMYKColorSpace() = default; explicit PDFDeviceCMYKColorSpace() = default;
virtual ~PDFDeviceCMYKColorSpace() = default; virtual ~PDFDeviceCMYKColorSpace() = default;
virtual QColor getDefaultColor() const override;
virtual QColor getColor(const PDFColor& color) const override; virtual QColor getColor(const PDFColor& color) const override;
virtual size_t getColorComponentCount() const override; virtual size_t getColorComponentCount() const override;
}; };
class PDFCalGrayColorSpace : public PDFAbstractColorSpace class PDFXYZColorSpace : public PDFAbstractColorSpace
{
public:
virtual QColor getDefaultColor() const override;
protected:
explicit PDFXYZColorSpace(PDFColor3 whitePoint);
virtual ~PDFXYZColorSpace() = default;
PDFColor3 m_whitePoint;
/// What are these coefficients? We want to map white point from XYZ space to white point
/// of RGB space. These coefficients are reciprocal values to the point converted from XYZ white
/// point. So, if we call getColor(m_whitePoint), then we should get vector (1.0, 1.0, 1.0)
/// after multiplication by these coefficients.
PDFColor3 m_correctionCoefficients;
};
class PDFCalGrayColorSpace : public PDFXYZColorSpace
{ {
public: public:
explicit inline PDFCalGrayColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent gamma); explicit inline PDFCalGrayColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent gamma);
@ -251,18 +296,11 @@ public:
static PDFColorSpacePointer createCalGrayColorSpace(const PDFDocument* document, const PDFDictionary* dictionary); static PDFColorSpacePointer createCalGrayColorSpace(const PDFDocument* document, const PDFDictionary* dictionary);
private: private:
PDFColor3 m_whitePoint;
PDFColor3 m_blackPoint; PDFColor3 m_blackPoint;
PDFColorComponent m_gamma; PDFColorComponent m_gamma;
/// What are these coefficients? We want to map white point from XYZ space to white point
/// of RGB space. These coefficients are reciprocal values to the point converted from XYZ white
/// point. So, if we call getColor(m_whitePoint), then we should get vector (1.0, 1.0, 1.0)
/// after multiplication by these coefficients.
PDFColor3 m_correctionCoefficients;
}; };
class PDFCalRGBColorSpace : public PDFAbstractColorSpace class PDFCalRGBColorSpace : public PDFXYZColorSpace
{ {
public: public:
explicit inline PDFCalRGBColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColor3 gamma, PDFColorComponentMatrix_3x3 matrix); explicit inline PDFCalRGBColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColor3 gamma, PDFColorComponentMatrix_3x3 matrix);
@ -277,19 +315,12 @@ public:
static PDFColorSpacePointer createCalRGBColorSpace(const PDFDocument* document, const PDFDictionary* dictionary); static PDFColorSpacePointer createCalRGBColorSpace(const PDFDocument* document, const PDFDictionary* dictionary);
private: private:
PDFColor3 m_whitePoint;
PDFColor3 m_blackPoint; PDFColor3 m_blackPoint;
PDFColor3 m_gamma; PDFColor3 m_gamma;
PDFColorComponentMatrix_3x3 m_matrix; PDFColorComponentMatrix_3x3 m_matrix;
/// What are these coefficients? We want to map white point from XYZ space to white point
/// of RGB space. These coefficients are reciprocal values to the point converted from XYZ white
/// point. So, if we call getColor(m_whitePoint), then we should get vector (1.0, 1.0, 1.0)
/// after multiplication by these coefficients.
PDFColor3 m_correctionCoefficients;
}; };
class PDFLabColorSpace : public PDFAbstractColorSpace class PDFLabColorSpace : public PDFXYZColorSpace
{ {
public: public:
explicit inline PDFLabColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent aMin, PDFColorComponent aMax, PDFColorComponent bMin, PDFColorComponent bMax); explicit inline PDFLabColorSpace(PDFColor3 whitePoint, PDFColor3 blackPoint, PDFColorComponent aMin, PDFColorComponent aMax, PDFColorComponent bMin, PDFColorComponent bMax);
@ -304,18 +335,11 @@ public:
static PDFColorSpacePointer createLabColorSpace(const PDFDocument* document, const PDFDictionary* dictionary); static PDFColorSpacePointer createLabColorSpace(const PDFDocument* document, const PDFDictionary* dictionary);
private: private:
PDFColor3 m_whitePoint;
PDFColor3 m_blackPoint; PDFColor3 m_blackPoint;
PDFColorComponent m_aMin; PDFColorComponent m_aMin;
PDFColorComponent m_aMax; PDFColorComponent m_aMax;
PDFColorComponent m_bMin; PDFColorComponent m_bMin;
PDFColorComponent m_bMax; PDFColorComponent m_bMax;
/// What are these coefficients? We want to map white point from XYZ space to white point
/// of RGB space. These coefficients are reciprocal values to the point converted from XYZ white
/// point. So, if we call getColor(m_whitePoint), then we should get vector (1.0, 1.0, 1.0)
/// after multiplication by these coefficients.
PDFColor3 m_correctionCoefficients;
}; };
class PDFICCBasedColorSpace : public PDFAbstractColorSpace class PDFICCBasedColorSpace : public PDFAbstractColorSpace
@ -328,6 +352,7 @@ public:
explicit inline PDFICCBasedColorSpace(PDFColorSpacePointer alternateColorSpace, Ranges range); explicit inline PDFICCBasedColorSpace(PDFColorSpacePointer alternateColorSpace, Ranges range);
virtual ~PDFICCBasedColorSpace() = default; virtual ~PDFICCBasedColorSpace() = default;
virtual QColor getDefaultColor() const override;
virtual QColor getColor(const PDFColor& color) const override; virtual QColor getColor(const PDFColor& color) const override;
virtual size_t getColorComponentCount() const override; virtual size_t getColorComponentCount() const override;
@ -343,6 +368,36 @@ private:
Ranges m_range; Ranges m_range;
}; };
class PDFIndexedColorSpace : public PDFAbstractColorSpace
{
public:
explicit inline PDFIndexedColorSpace(PDFColorSpacePointer baseColorSpace, QByteArray&& colors, int maxValue);
virtual ~PDFIndexedColorSpace() = default;
virtual QColor getDefaultColor() const override;
virtual QColor getColor(const PDFColor& color) const override;
virtual size_t getColorComponentCount() const override;
/// Creates indexed color space from provided values.
/// \param colorSpaceDictionary Color space dictionary
/// \param document Document
/// \param array Array with indexed color space definition
/// \param recursion Recursion guard
static PDFColorSpacePointer createIndexedColorSpace(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFArray* array, int recursion);
private:
static constexpr const int MIN_VALUE = 0;
static constexpr const int MAX_VALUE = 255;
PDFColorSpacePointer m_baseColorSpace;
QByteArray m_colors;
int m_maxValue;
};
// TODO: Implement Separation color space
// TODO: Implement DeviceN color space
// TODO: Implement Pattern color space
} // namespace pdf } // namespace pdf
#endif // PDFCOLORSPACES_H #endif // PDFCOLORSPACES_H

View File

@ -49,9 +49,9 @@ public:
} }
template<typename... Arguments, typename std::enable_if<sizeof...(Arguments) < FlatSize, int>::type = 0> template<typename... Arguments, typename std::enable_if<sizeof...(Arguments) <= FlatSize, int>::type = 0>
explicit inline PDFFlatArray(Arguments... arguments) : explicit inline PDFFlatArray(Arguments... arguments) :
m_flatBlock(arguments...), m_flatBlock({ arguments... }),
m_flatBlockEndIterator(std::next(m_flatBlock.begin(), sizeof...(Arguments))), m_flatBlockEndIterator(std::next(m_flatBlock.begin(), sizeof...(Arguments))),
m_variableBlock() m_variableBlock()
{ {

View File

@ -178,10 +178,21 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& recta
PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document) : PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document) :
m_page(page), m_page(page),
m_document(document) m_document(document),
m_colorSpaceDictionary(nullptr)
{ {
Q_ASSERT(page); Q_ASSERT(page);
Q_ASSERT(document); Q_ASSERT(document);
const PDFObject& resources = m_document->getObject(m_page->getResources());
if (resources.isDictionary() && resources.getDictionary()->hasKey(COLOR_SPACE_DICTIONARY))
{
const PDFObject& colorSpace = m_document->getObject(resources.getDictionary()->get(COLOR_SPACE_DICTIONARY));
if (colorSpace.isDictionary())
{
m_colorSpaceDictionary = colorSpace.getDictionary();
}
}
} }
QList<PDFRenderError> PDFPageContentProcessor::processContents() QList<PDFRenderError> PDFPageContentProcessor::processContents()
@ -191,6 +202,23 @@ QList<PDFRenderError> PDFPageContentProcessor::processContents()
// Clear the old errors // Clear the old errors
m_errorList.clear(); m_errorList.clear();
// Initialize default color spaces (gray, RGB, CMYK)
try
{
m_deviceGrayColorSpace = PDFAbstractColorSpace::createDeviceColorSpaceByName(m_colorSpaceDictionary, m_document, COLOR_SPACE_NAME_DEVICE_GRAY);
m_deviceRGBColorSpace = PDFAbstractColorSpace::createDeviceColorSpaceByName(m_colorSpaceDictionary, m_document, COLOR_SPACE_NAME_DEVICE_RGB);
m_deviceCMYKColorSpace = PDFAbstractColorSpace::createDeviceColorSpaceByName(m_colorSpaceDictionary, m_document, COLOR_SPACE_NAME_DEVICE_CMYK);
}
catch (PDFParserException exception)
{
m_errorList.append(PDFRenderError(RenderErrorType::Error, exception.getMessage()));
// Create default color spaces anyway, but do not try to load them...
m_deviceGrayColorSpace.reset(new PDFDeviceGrayColorSpace);
m_deviceRGBColorSpace.reset(new PDFDeviceRGBColorSpace);
m_deviceCMYKColorSpace.reset(new PDFDeviceCMYKColorSpace);
}
if (contents.isArray()) if (contents.isArray())
{ {
const PDFArray* array = contents.getArray(); const PDFArray* array = contents.getArray();
@ -229,6 +257,17 @@ void PDFPageContentProcessor::performPathPainting(const QPainterPath& path, bool
Q_UNUSED(fillRule); Q_UNUSED(fillRule);
} }
void PDFPageContentProcessor::performClipping(const QPainterPath& path, Qt::FillRule fillRule)
{
Q_UNUSED(path);
Q_UNUSED(fillRule);
}
void PDFPageContentProcessor::performUpdateGraphicsState(const PDFPageContentProcessor::PDFPageContentProcessorState& state)
{
Q_UNUSED(state);
}
void PDFPageContentProcessor::processContentStream(const PDFStream* stream) void PDFPageContentProcessor::processContentStream(const PDFStream* stream)
{ {
QByteArray content = m_document->getDecodedStream(stream); QByteArray content = m_document->getDecodedStream(stream);
@ -388,6 +427,102 @@ void PDFPageContentProcessor::processCommand(const QByteArray& command)
break; break;
} }
case Operator::ClipEvenOdd:
{
operatorClipEvenOdd();
break;
}
case Operator::ClipWinding:
{
operatorClipWinding();
break;
}
case Operator::ColorSetStrokingColorSpace:
{
// CS, set current color space for stroking operations
invokeOperator(&PDFPageContentProcessor::operatorColorSetStrokingColorSpace);
break;
}
case Operator::ColorSetFillingColorSpace:
{
// cs, set current color space for filling operations
invokeOperator(&PDFPageContentProcessor::operatorColorSetFillingColorSpace);
break;
}
case Operator::ColorSetStrokingColor:
{
// SC, set current stroking color
operatorColorSetStrokingColor();
break;
}
case Operator::ColorSetStrokingColorN:
{
// SCN, same as SC, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
operatorColorSetStrokingColorN();
break;
}
case Operator::ColorSetFillingColor:
{
// sc, set current filling color
operatorColorSetFillingColor();
break;
}
case Operator::ColorSetFillingColorN:
{
// scn, same as sc, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
operatorColorSetFillingColorN();
break;
}
case Operator::ColorSetDeviceGrayStroking:
{
// G, set DeviceGray color space for stroking color and set color
invokeOperator(&PDFPageContentProcessor::operatorColorSetDeviceGrayStroking);
break;
}
case Operator::ColorSetDeviceGrayFilling:
{
// g, set DeviceGray color space for filling color and set color
invokeOperator(&PDFPageContentProcessor::operatorColorSetDeviceGrayFilling);
break;
}
case Operator::ColorSetDeviceRGBStroking:
{
// RG, set DeviceRGB color space for stroking color and set color
invokeOperator(&PDFPageContentProcessor::operatorColorSetDeviceRGBStroking);
break;
}
case Operator::ColorSetDeviceRGBFilling:
{
// rg, set DeviceRGB color space for filling color and set color
invokeOperator(&PDFPageContentProcessor::operatorColorSetDeviceRGBFilling);
break;
}
case Operator::ColorSetDeviceCMYKStroking:
{
// K, set DeviceCMYK color space for stroking color and set color
invokeOperator(&PDFPageContentProcessor::operatorColorSetDeviceCMYKStroking);
break;
}
case Operator::ColorSetDeviceCMYKFilling:
{
// k, set DeviceCMYK color space for filling color and set color
invokeOperator(&PDFPageContentProcessor::operatorColorSetDeviceCMYKFilling);
break;
}
case Operator::Invalid: case Operator::Invalid:
{ {
m_errorList.append(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Unknown operator '%1'.").arg(QString::fromLatin1(command)))); m_errorList.append(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Unknown operator '%1'.").arg(QString::fromLatin1(command))));
@ -418,6 +553,15 @@ QPointF PDFPageContentProcessor::getCurrentPoint() const
return QPointF(); return QPointF();
} }
void PDFPageContentProcessor::updateGraphicState()
{
if (m_graphicState.getStateFlags())
{
performUpdateGraphicsState(m_graphicState);
m_graphicState.setStateFlags(PDFPageContentProcessorState::StateUnchanged);
}
}
template<> template<>
PDFReal PDFPageContentProcessor::readOperand<PDFReal>(size_t index) const PDFReal PDFPageContentProcessor::readOperand<PDFReal>(size_t index) const
{ {
@ -443,6 +587,31 @@ PDFReal PDFPageContentProcessor::readOperand<PDFReal>(size_t index) const
return 0.0; return 0.0;
} }
template<>
PDFPageContentProcessor::PDFName PDFPageContentProcessor::readOperand<PDFPageContentProcessor::PDFName>(size_t index) const
{
if (index < m_operands.size())
{
const PDFLexicalAnalyzer::Token& token = m_operands[index];
switch (token.type)
{
case PDFLexicalAnalyzer::TokenType::Name:
return PDFName{ token.data.toByteArray() };
default:
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't read operand (name) on index %1. Operand is of type '%2'.").arg(index + 1).arg(PDFLexicalAnalyzer::getStringFromOperandType(token.type)));
}
}
else
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Can't read operand (name) on index %1. Only %2 operands provided.").arg(index + 1).arg(m_operands.size()));
}
return PDFName();
}
void PDFPageContentProcessor::operatorMoveCurrentPoint(PDFReal x, PDFReal y) void PDFPageContentProcessor::operatorMoveCurrentPoint(PDFReal x, PDFReal y)
{ {
m_currentPath.moveTo(x, y);; m_currentPath.moveTo(x, y);;
@ -572,6 +741,154 @@ void PDFPageContentProcessor::operatorPathClear()
m_currentPath = QPainterPath(); m_currentPath = QPainterPath();
} }
void PDFPageContentProcessor::operatorClipWinding()
{
if (!m_currentPath.isEmpty())
{
m_currentPath.setFillRule(Qt::WindingFill);
performClipping(m_currentPath, Qt::WindingFill);
}
}
void PDFPageContentProcessor::operatorClipEvenOdd()
{
if (!m_currentPath.isEmpty())
{
m_currentPath.setFillRule(Qt::OddEvenFill);
performClipping(m_currentPath, Qt::OddEvenFill);
}
}
void PDFPageContentProcessor::operatorColorSetStrokingColorSpace(PDFPageContentProcessor::PDFName name)
{
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(m_colorSpaceDictionary, m_document, PDFObject::createName(std::make_shared<PDFString>(QByteArray(name.name))));
if (colorSpace)
{
// We must also set default color (it can depend on the color space)
m_graphicState.setStrokeColorSpace(colorSpace);
m_graphicState.setStrokeColor(colorSpace->getDefaultColor());
updateGraphicState();
}
else
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid color space."));
}
}
void PDFPageContentProcessor::operatorColorSetFillingColorSpace(PDFName name)
{
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(m_colorSpaceDictionary, m_document, PDFObject::createName(std::make_shared<PDFString>(QByteArray(name.name))));
if (colorSpace)
{
// We must also set default color (it can depend on the color space)
m_graphicState.setFillColorSpace(colorSpace);
m_graphicState.setFillColor(colorSpace->getDefaultColor());
updateGraphicState();
}
else
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid color space."));
}
}
void PDFPageContentProcessor::operatorColorSetStrokingColor()
{
const PDFAbstractColorSpace* colorSpace = m_graphicState.getStrokeColorSpace();
const size_t colorSpaceComponentCount = colorSpace->getColorComponentCount();
const size_t operandCount = m_operands.size();
if (operandCount == colorSpaceComponentCount)
{
PDFColor color;
for (size_t i = 0; i < operandCount; ++i)
{
color.push_back(readOperand<PDFReal>(i));
}
m_graphicState.setStrokeColor(colorSpace->getColor(color));
updateGraphicState();
}
else
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid color component count. Provided %1, required %2.").arg(operandCount).arg(colorSpaceComponentCount));
}
}
void PDFPageContentProcessor::operatorColorSetStrokingColorN()
{
// TODO: Implement operator SCN
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Not implemented!"));
}
void PDFPageContentProcessor::operatorColorSetFillingColor()
{
const PDFAbstractColorSpace* colorSpace = m_graphicState.getFillColorSpace();
const size_t colorSpaceComponentCount = colorSpace->getColorComponentCount();
const size_t operandCount = m_operands.size();
if (operandCount == colorSpaceComponentCount)
{
PDFColor color;
for (size_t i = 0; i < operandCount; ++i)
{
color.push_back(readOperand<PDFReal>(i));
}
m_graphicState.setFillColor(colorSpace->getColor(color));
updateGraphicState();
}
else
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid color component count. Provided %1, required %2.").arg(operandCount).arg(colorSpaceComponentCount));
}
}
void PDFPageContentProcessor::operatorColorSetFillingColorN()
{
// TODO: Implement operator scn
throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Not implemented!"));
}
void PDFPageContentProcessor::operatorColorSetDeviceGrayStroking(PDFReal gray)
{
m_graphicState.setStrokeColorSpace(m_deviceGrayColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), gray));
updateGraphicState();
}
void PDFPageContentProcessor::operatorColorSetDeviceGrayFilling(PDFReal gray)
{
m_graphicState.setFillColorSpace(m_deviceGrayColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), gray));
updateGraphicState();
}
void PDFPageContentProcessor::operatorColorSetDeviceRGBStroking(PDFReal r, PDFReal g, PDFReal b)
{
m_graphicState.setStrokeColorSpace(m_deviceRGBColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), r, g, b));
updateGraphicState();
}
void PDFPageContentProcessor::operatorColorSetDeviceRGBFilling(PDFReal r, PDFReal g, PDFReal b)
{
m_graphicState.setFillColorSpace(m_deviceRGBColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), r, g, b));
updateGraphicState();
}
void PDFPageContentProcessor::operatorColorSetDeviceCMYKStroking(PDFReal c, PDFReal m, PDFReal y, PDFReal k)
{
m_graphicState.setStrokeColorSpace(m_deviceCMYKColorSpace);
m_graphicState.setStrokeColor(getColorFromColorSpace(m_graphicState.getStrokeColorSpace(), c, m, y, k));
updateGraphicState();
}
void PDFPageContentProcessor::operatorColorSetDeviceCMYKFilling(PDFReal c, PDFReal m, PDFReal y, PDFReal k)
{
m_graphicState.setFillColorSpace(m_deviceCMYKColorSpace);
m_graphicState.setFillColor(getColorFromColorSpace(m_graphicState.getFillColorSpace(), c, m, y, k));
updateGraphicState();
}
PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() : PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() :
m_currentTransformationMatrix(), m_currentTransformationMatrix(),
m_fillColorSpace(), m_fillColorSpace(),
@ -584,7 +901,8 @@ PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorSt
m_mitterLimit(10.0), m_mitterLimit(10.0),
m_renderingIntent(), m_renderingIntent(),
m_flatness(1.0), m_flatness(1.0),
m_smoothness(0.01) m_smoothness(0.01),
m_stateFlags(StateUnchanged)
{ {
m_fillColorSpace.reset(new PDFDeviceGrayColorSpace); m_fillColorSpace.reset(new PDFDeviceGrayColorSpace);
m_strokeColorSpace = m_fillColorSpace; m_strokeColorSpace = m_fillColorSpace;
@ -595,4 +913,129 @@ PDFPageContentProcessor::PDFPageContentProcessorState::~PDFPageContentProcessorS
} }
PDFPageContentProcessor::PDFPageContentProcessorState& PDFPageContentProcessor::PDFPageContentProcessorState::operator=(const PDFPageContentProcessor::PDFPageContentProcessorState& other)
{
setCurrentTransformationMatrix(other.getCurrentTransformationMatrix());
setStrokeColorSpace(other.m_strokeColorSpace);
setFillColorSpace(other.m_fillColorSpace);
setStrokeColor(other.getStrokeColor());
setFillColor(other.getFillColor());
setLineWidth(other.getLineWidth());
setLineCapStyle(other.getLineCapStyle());
setLineJoinStyle(other.getLineJoinStyle());
setMitterLimit(other.getMitterLimit());
setRenderingIntent(other.getRenderingIntent());
setFlatness(other.getFlatness());
setSmoothness(other.getSmoothness());
return *this;
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setCurrentTransformationMatrix(const QMatrix& currentTransformationMatrix)
{
if (m_currentTransformationMatrix != currentTransformationMatrix)
{
m_currentTransformationMatrix = currentTransformationMatrix;
m_stateFlags |= StateCurrentTransformationMatrix;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setStrokeColorSpace(const QSharedPointer<PDFAbstractColorSpace>& strokeColorSpace)
{
if (m_strokeColorSpace != strokeColorSpace)
{
m_strokeColorSpace = strokeColorSpace;
m_stateFlags |= StateStrokeColorSpace;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setFillColorSpace(const QSharedPointer<PDFAbstractColorSpace>& fillColorSpace)
{
if (m_fillColorSpace != fillColorSpace)
{
m_fillColorSpace = fillColorSpace;
m_stateFlags |= StateFillColorSpace;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setStrokeColor(const QColor& strokeColor)
{
if (m_strokeColor != strokeColor)
{
m_strokeColor = strokeColor;
m_stateFlags |= StateStrokeColor;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setFillColor(const QColor& fillColor)
{
if (m_fillColor != fillColor)
{
m_fillColor = fillColor;
m_stateFlags |= StateFillColor;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setLineWidth(PDFReal lineWidth)
{
if (m_lineWidth != lineWidth)
{
m_lineWidth = lineWidth;
m_stateFlags |= StateLineWidth;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setLineCapStyle(Qt::PenCapStyle lineCapStyle)
{
if (m_lineCapStyle != lineCapStyle)
{
m_lineCapStyle = lineCapStyle;
m_stateFlags |= StateLineCapStyle;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setLineJoinStyle(Qt::PenJoinStyle lineJoinStyle)
{
if (m_lineJoinStyle != lineJoinStyle)
{
m_lineJoinStyle = lineJoinStyle;
m_stateFlags |= StateLineJoinStyle;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setMitterLimit(const PDFReal& mitterLimit)
{
if (m_mitterLimit != mitterLimit)
{
m_mitterLimit = mitterLimit;
m_stateFlags |= StateMitterLimit;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setRenderingIntent(const QByteArray& renderingIntent)
{
if (m_renderingIntent != renderingIntent)
{
m_renderingIntent = renderingIntent;
m_stateFlags |= StateRenderingIntent;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setFlatness(PDFReal flatness)
{
if (m_flatness != flatness)
{
m_flatness = flatness;
m_stateFlags |= StateFlatness;
}
}
void PDFPageContentProcessor::PDFPageContentProcessorState::setSmoothness(PDFReal smoothness)
{
if (m_smoothness != smoothness)
{
m_smoothness = smoothness;
m_stateFlags |= StateSmoothness;
}
}
} // namespace pdf } // namespace pdf

View File

@ -129,9 +129,9 @@ public:
ColorSetStrokingColorSpace, ///< CS, set current color space for stroking operations ColorSetStrokingColorSpace, ///< CS, set current color space for stroking operations
ColorSetFillingColorSpace, ///< cs, set current color space for filling operations ColorSetFillingColorSpace, ///< cs, set current color space for filling operations
ColorSetStrokingColor, ///< SC, set current stroking color ColorSetStrokingColor, ///< SC, set current stroking color
ColorSetStrokingColorN, ///< SCN, same as SC, but also supports Pattern, Separtion, DeviceN and ICCBased color spaces ColorSetStrokingColorN, ///< SCN, same as SC, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
ColorSetFillingColor, ///< sc, set current filling color ColorSetFillingColor, ///< sc, set current filling color
ColorSetFillingColorN, ///< scn, same as sc, but also supports Pattern, Separtion, DeviceN and ICCBased color spaces ColorSetFillingColorN, ///< scn, same as sc, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
ColorSetDeviceGrayStroking, ///< G, set DeviceGray color space for stroking color and set color ColorSetDeviceGrayStroking, ///< G, set DeviceGray color space for stroking color and set color
ColorSetDeviceGrayFilling, ///< g, set DeviceGray color space for filling color and set color ColorSetDeviceGrayFilling, ///< g, set DeviceGray color space for filling color and set color
ColorSetDeviceRGBStroking, ///< RG, set DeviceRGB color space for stroking color and set color ColorSetDeviceRGBStroking, ///< RG, set DeviceRGB color space for stroking color and set color
@ -167,6 +167,94 @@ public:
QList<PDFRenderError> processContents(); QList<PDFRenderError> processContents();
protected: protected:
/// Represents graphic state of the PDF (holding current graphic state parameters).
/// Please see PDF Reference 1.7, Chapter 4.3 "Graphic State"
class PDFPageContentProcessorState
{
public:
explicit PDFPageContentProcessorState();
~PDFPageContentProcessorState();
PDFPageContentProcessorState(const PDFPageContentProcessorState&) = default;
PDFPageContentProcessorState(PDFPageContentProcessorState&&) = default;
PDFPageContentProcessorState& operator=(PDFPageContentProcessorState&&) = delete;
PDFPageContentProcessorState& operator=(const PDFPageContentProcessorState& other);
enum StateFlag
{
StateUnchanged = 0x0000,
StateCurrentTransformationMatrix = 0x0001,
StateStrokeColorSpace = 0x0002,
StateFillColorSpace = 0x0004,
StateStrokeColor = 0x0008,
StateFillColor = 0x0010,
StateLineWidth = 0x0020,
StateLineCapStyle = 0x0040,
StateLineJoinStyle = 0x0080,
StateMitterLimit = 0x0100,
StateRenderingIntent = 0x0200,
StateFlatness = 0x0400,
StateSmoothness = 0x0800
};
Q_DECLARE_FLAGS(StateFlags, StateFlag)
const QMatrix& getCurrentTransformationMatrix() const { return m_currentTransformationMatrix; }
void setCurrentTransformationMatrix(const QMatrix& currentTransformationMatrix);
const PDFAbstractColorSpace* getStrokeColorSpace() const { return m_strokeColorSpace.data(); }
void setStrokeColorSpace(const QSharedPointer<PDFAbstractColorSpace>& strokeColorSpace);
const PDFAbstractColorSpace* getFillColorSpace() const { return m_fillColorSpace.data(); }
void setFillColorSpace(const QSharedPointer<PDFAbstractColorSpace>& fillColorSpace);
const QColor& getStrokeColor() const { return m_strokeColor; }
void setStrokeColor(const QColor& strokeColor);
const QColor& getFillColor() const { return m_fillColor; }
void setFillColor(const QColor& fillColor);
PDFReal getLineWidth() const { return m_lineWidth; }
void setLineWidth(PDFReal lineWidth);
Qt::PenCapStyle getLineCapStyle() const { return m_lineCapStyle; }
void setLineCapStyle(Qt::PenCapStyle lineCapStyle);
Qt::PenJoinStyle getLineJoinStyle() const { return m_lineJoinStyle; }
void setLineJoinStyle(Qt::PenJoinStyle lineJoinStyle);
PDFReal getMitterLimit() const { return m_mitterLimit; }
void setMitterLimit(const PDFReal& mitterLimit);
const QByteArray& getRenderingIntent() const { return m_renderingIntent; }
void setRenderingIntent(const QByteArray& renderingIntent);
PDFReal getFlatness() const { return m_flatness; }
void setFlatness(PDFReal flatness);
PDFReal getSmoothness() const { return m_smoothness; }
void setSmoothness(PDFReal smoothness);
StateFlags getStateFlags() const { return m_stateFlags; }
void setStateFlags(StateFlags stateFlags) { m_stateFlags = stateFlags; }
private:
QMatrix m_currentTransformationMatrix;
PDFColorSpacePointer m_strokeColorSpace;
PDFColorSpacePointer m_fillColorSpace;
QColor m_strokeColor;
QColor m_fillColor;
PDFReal m_lineWidth;
Qt::PenCapStyle m_lineCapStyle;
Qt::PenJoinStyle m_lineJoinStyle;
PDFReal m_mitterLimit;
QByteArray m_renderingIntent;
PDFReal m_flatness;
PDFReal m_smoothness;
StateFlags m_stateFlags;
};
/// This function has to be implemented in the client drawing implementation, it should /// This function has to be implemented in the client drawing implementation, it should
/// draw the path according to the parameters. /// draw the path according to the parameters.
/// \param path Path, which should be drawn (can be emtpy - in that case nothing happens) /// \param path Path, which should be drawn (can be emtpy - in that case nothing happens)
@ -175,6 +263,15 @@ protected:
/// \param fillRule Fill rule used in the fill mode /// \param fillRule Fill rule used in the fill mode
virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, Qt::FillRule fillRule); virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, Qt::FillRule fillRule);
/// This function has to be implemented in the client drawing implementation, it should
/// clip along the path (intersect with current clipping path).
virtual void performClipping(const QPainterPath& path, Qt::FillRule fillRule);
/// This function has to be implemented in the client drawing implementation, it should
/// update the device accordin to the graphic state change. The flags are set when
/// the value differs from the previous graphic state.
virtual void performUpdateGraphicsState(const PDFPageContentProcessorState& state);
private: private:
/// Process the content stream /// Process the content stream
void processContentStream(const PDFStream* stream); void processContentStream(const PDFStream* stream);
@ -182,12 +279,21 @@ private:
/// Processes single command /// Processes single command
void processCommand(const QByteArray& command); void processCommand(const QByteArray& command);
/// Wrapper for PDF Name
struct PDFName
{
QByteArray name;
};
template<typename T> template<typename T>
T readOperand(size_t index) const; T readOperand(size_t index) const;
template<> template<>
PDFReal readOperand<PDFReal>(size_t index) const; PDFReal readOperand<PDFReal>(size_t index) const;
template<>
PDFName readOperand<PDFName>(size_t index) const;
template<size_t index, typename T> template<size_t index, typename T>
inline T readOperand() const { return readOperand<T>(index); } inline T readOperand() const { return readOperand<T>(index); }
@ -211,6 +317,28 @@ private:
/// exception is thrown. /// exception is thrown.
QPointF getCurrentPoint() const; QPointF getCurrentPoint() const;
/// Notifies the updated graphic state. If nothing changed in graphic state, then nothing happens.
void updateGraphicState();
template<typename... Operands>
inline QColor getColorFromColorSpace(const PDFAbstractColorSpace* colorSpace, Operands... operands)
{
constexpr const size_t operandCount = sizeof...(Operands);
const size_t colorSpaceComponentCount = colorSpace->getColorComponentCount();
if (operandCount == colorSpaceComponentCount)
{
return colorSpace->getColor(PDFColor(static_cast<PDFColorComponent>(operands)...));
}
else
{
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid color component count. Provided %1, required %2.").arg(operandCount).arg(colorSpaceComponentCount));
}
return QColor();
}
// Path construction operators // Path construction operators
void operatorMoveCurrentPoint(PDFReal x, PDFReal y); void operatorMoveCurrentPoint(PDFReal x, PDFReal y);
void operatorLineTo(PDFReal x, PDFReal y); void operatorLineTo(PDFReal x, PDFReal y);
@ -231,38 +359,42 @@ private:
void operatorPathCloseFillStrokeEvenOdd(); void operatorPathCloseFillStrokeEvenOdd();
void operatorPathClear(); void operatorPathClear();
/// Represents graphic state of the PDF (holding current graphic state parameters). // Clipping paths: W, W*
/// Please see PDF Reference 1.7, Chapter 4.3 "Graphic State" void operatorClipWinding(); ///< W, modify current clipping path by intersecting it with current path using "Non zero winding number rule"
class PDFPageContentProcessorState void operatorClipEvenOdd(); ///< W*, modify current clipping path by intersecting it with current path using "Even-odd rule"
{
public:
explicit PDFPageContentProcessorState();
~PDFPageContentProcessorState();
private: // Color: CS, cs, SC, SCN, sc, scn, G, g, RG, rg, K, k
QMatrix m_currentTransformationMatrix; void operatorColorSetStrokingColorSpace(PDFName name); ///< CS, set current color space for stroking operations
QSharedPointer<PDFAbstractColorSpace> m_fillColorSpace; void operatorColorSetFillingColorSpace(PDFName name); ///< cs, set current color space for filling operations
QSharedPointer<PDFAbstractColorSpace> m_strokeColorSpace; void operatorColorSetStrokingColor(); ///< SC, set current stroking color
QColor m_fillColor; void operatorColorSetStrokingColorN(); ///< SCN, same as SC, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
QColor m_strokeColor; void operatorColorSetFillingColor(); ///< sc, set current filling color
PDFReal m_lineWidth; void operatorColorSetFillingColorN(); ///< scn, same as sc, but also supports Pattern, Separation, DeviceN and ICCBased color spaces
Qt::PenCapStyle m_lineCapStyle; void operatorColorSetDeviceGrayStroking(PDFReal gray); ///< G, set DeviceGray color space for stroking color and set color
Qt::PenJoinStyle m_lineJoinStyle; void operatorColorSetDeviceGrayFilling(PDFReal gray); ///< g, set DeviceGray color space for filling color and set color
PDFReal m_mitterLimit; void operatorColorSetDeviceRGBStroking(PDFReal r, PDFReal g, PDFReal b); ///< RG, set DeviceRGB color space for stroking color and set color
QByteArray m_renderingIntent; void operatorColorSetDeviceRGBFilling(PDFReal r, PDFReal g, PDFReal b); ///< rg, set DeviceRGB color space for filling color and set color
PDFReal m_flatness; void operatorColorSetDeviceCMYKStroking(PDFReal c, PDFReal m, PDFReal y, PDFReal k); ///< K, set DeviceCMYK color space for stroking color and set color
PDFReal m_smoothness; void operatorColorSetDeviceCMYKFilling(PDFReal c, PDFReal m, PDFReal y, PDFReal k); ///< k, set DeviceCMYK color space for filling color and set color
};
const PDFPage* m_page; const PDFPage* m_page;
const PDFDocument* m_document; const PDFDocument* m_document;
const PDFDictionary* m_colorSpaceDictionary;
// Default color spaces
PDFColorSpacePointer m_deviceGrayColorSpace;
PDFColorSpacePointer m_deviceRGBColorSpace;
PDFColorSpacePointer m_deviceCMYKColorSpace;
/// Array with current operand arguments /// Array with current operand arguments
PDFFlatArray<PDFLexicalAnalyzer::Token, 33> m_operands; PDFFlatArray<PDFLexicalAnalyzer::Token, 33> m_operands;
/// Stack with current graphic states /// Stack with saved graphic states
std::stack<PDFPageContentProcessorState> m_stack; std::stack<PDFPageContentProcessorState> m_stack;
/// Current graphic state
PDFPageContentProcessorState m_graphicState;
/// List of errors /// List of errors
QList<PDFRenderError> m_errorList; QList<PDFRenderError> m_errorList;