mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Shading: Axial shading (first part)
This commit is contained in:
@ -42,6 +42,7 @@ SOURCES += \
|
|||||||
sources/pdfparser.cpp \
|
sources/pdfparser.cpp \
|
||||||
sources/pdfdocument.cpp \
|
sources/pdfdocument.cpp \
|
||||||
sources/pdfdocumentreader.cpp \
|
sources/pdfdocumentreader.cpp \
|
||||||
|
sources/pdfpattern.cpp \
|
||||||
sources/pdfsecurityhandler.cpp \
|
sources/pdfsecurityhandler.cpp \
|
||||||
sources/pdfutils.cpp \
|
sources/pdfutils.cpp \
|
||||||
sources/pdfxreftable.cpp \
|
sources/pdfxreftable.cpp \
|
||||||
@ -71,6 +72,7 @@ HEADERS += \
|
|||||||
sources/pdfconstants.h \
|
sources/pdfconstants.h \
|
||||||
sources/pdfdocument.h \
|
sources/pdfdocument.h \
|
||||||
sources/pdfdocumentreader.h \
|
sources/pdfdocumentreader.h \
|
||||||
|
sources/pdfpattern.h \
|
||||||
sources/pdfsecurityhandler.h \
|
sources/pdfsecurityhandler.h \
|
||||||
sources/pdfxreftable.h \
|
sources/pdfxreftable.h \
|
||||||
sources/pdfflatmap.h \
|
sources/pdfflatmap.h \
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "pdfdocument.h"
|
#include "pdfdocument.h"
|
||||||
#include "pdfexception.h"
|
#include "pdfexception.h"
|
||||||
#include "pdfutils.h"
|
#include "pdfutils.h"
|
||||||
|
#include "pdfpattern.h"
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
@ -238,6 +239,16 @@ QImage PDFAbstractColorSpace::getImage(const PDFImageData& imageData) const
|
|||||||
return QImage();
|
return QImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QColor PDFAbstractColorSpace::getCheckedColor(const PDFColor& color) const
|
||||||
|
{
|
||||||
|
if (getColorComponentCount() != color.size())
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid number of color components. Expected number is %1, actual number is %2.").arg(getColorComponentCount(), color.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return getColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpace(const PDFDictionary* colorSpaceDictionary,
|
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpace(const PDFDictionary* colorSpaceDictionary,
|
||||||
const PDFDocument* document,
|
const PDFDocument* document,
|
||||||
const PDFObject& colorSpace)
|
const PDFObject& colorSpace)
|
||||||
@ -252,6 +263,18 @@ PDFColorSpacePointer PDFAbstractColorSpace::createDeviceColorSpaceByName(const P
|
|||||||
return createDeviceColorSpaceByNameImpl(colorSpaceDictionary, document, name, COLOR_SPACE_MAX_LEVEL_OF_RECURSION);
|
return createDeviceColorSpaceByNameImpl(colorSpaceDictionary, document, name, COLOR_SPACE_MAX_LEVEL_OF_RECURSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFColor PDFAbstractColorSpace::convertToColor(const std::vector<PDFReal>& components)
|
||||||
|
{
|
||||||
|
PDFColor result;
|
||||||
|
|
||||||
|
for (PDFReal component : components)
|
||||||
|
{
|
||||||
|
result.push_back(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictionary* colorSpaceDictionary,
|
PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictionary* colorSpaceDictionary,
|
||||||
const PDFDocument* document,
|
const PDFDocument* document,
|
||||||
const PDFObject& colorSpace,
|
const PDFObject& colorSpace,
|
||||||
@ -293,6 +316,12 @@ PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictio
|
|||||||
{
|
{
|
||||||
stream = colorSpaceSettings.getStream();
|
stream = colorSpaceSettings.getStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name == COLOR_SPACE_NAME_PATTERN)
|
||||||
|
{
|
||||||
|
PDFPatternPtr pattern = PDFPattern::createPattern(colorSpaceDictionary, document, array->getItem(1));
|
||||||
|
return PDFColorSpacePointer(new PDFPatternColorSpace(qMove(pattern)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dictionary)
|
if (dictionary)
|
||||||
@ -915,4 +944,19 @@ const unsigned char* PDFImageData::getRow(unsigned int rowIndex) const
|
|||||||
return data + (rowIndex * m_stride);
|
return data + (rowIndex * m_stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QColor PDFPatternColorSpace::getDefaultColor() const
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Pattern doesn't have default color."));
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor PDFPatternColorSpace::getColor(const PDFColor& color) const
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Pattern doesn't have defined uniform color."));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PDFPatternColorSpace::getColorComponentCount() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -30,6 +30,7 @@ namespace pdf
|
|||||||
class PDFArray;
|
class PDFArray;
|
||||||
class PDFObject;
|
class PDFObject;
|
||||||
class PDFStream;
|
class PDFStream;
|
||||||
|
class PDFPattern;
|
||||||
class PDFDocument;
|
class PDFDocument;
|
||||||
class PDFDictionary;
|
class PDFDictionary;
|
||||||
class PDFAbstractColorSpace;
|
class PDFAbstractColorSpace;
|
||||||
@ -60,6 +61,7 @@ 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* COLOR_SPACE_NAME_INDEXED = "Indexed";
|
||||||
static constexpr const char* COLOR_SPACE_NAME_SEPARATION = "Separation";
|
static constexpr const char* COLOR_SPACE_NAME_SEPARATION = "Separation";
|
||||||
|
static constexpr const char* COLOR_SPACE_NAME_PATTERN = "Pattern";
|
||||||
|
|
||||||
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";
|
||||||
@ -209,6 +211,10 @@ public:
|
|||||||
virtual size_t getColorComponentCount() const = 0;
|
virtual size_t getColorComponentCount() const = 0;
|
||||||
virtual QImage getImage(const PDFImageData& imageData) const;
|
virtual QImage getImage(const PDFImageData& imageData) const;
|
||||||
|
|
||||||
|
/// Checks, if number of color components is OK, and if yes, converts them to the QColor value.
|
||||||
|
/// If they are not OK, exception is thrown.
|
||||||
|
QColor getCheckedColor(const PDFColor& color) const;
|
||||||
|
|
||||||
/// Parses the desired color space. If desired color space is not found, then exception is thrown.
|
/// Parses the desired color space. If desired color space is not found, then exception is thrown.
|
||||||
/// If everything is OK, then shared pointer to the new color space is returned.
|
/// If everything is OK, then shared pointer to the new color space is returned.
|
||||||
/// \param colorSpaceDictionary Dictionary containing color spaces of the page
|
/// \param colorSpaceDictionary Dictionary containing color spaces of the page
|
||||||
@ -227,6 +233,9 @@ public:
|
|||||||
const PDFDocument* document,
|
const PDFDocument* document,
|
||||||
const QByteArray& name);
|
const QByteArray& name);
|
||||||
|
|
||||||
|
/// Converts a vector of real numbers to the PDFColor
|
||||||
|
static PDFColor convertToColor(const std::vector<PDFReal>& components);
|
||||||
|
|
||||||
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); }
|
||||||
@ -511,8 +520,23 @@ private:
|
|||||||
PDFFunctionPtr m_tintTransform;
|
PDFFunctionPtr m_tintTransform;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PDFPatternColorSpace : public PDFAbstractColorSpace
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFPatternColorSpace(std::shared_ptr<PDFPattern>&& pattern) : m_pattern(qMove(pattern)) { }
|
||||||
|
virtual ~PDFPatternColorSpace() override = default;
|
||||||
|
|
||||||
|
virtual QColor getDefaultColor() const override;
|
||||||
|
virtual QColor getColor(const PDFColor& color) const override;
|
||||||
|
virtual size_t getColorComponentCount() const override;
|
||||||
|
|
||||||
|
const PDFPattern* getPattern() const { return m_pattern.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<PDFPattern> m_pattern;
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Implement DeviceN color space
|
// TODO: Implement DeviceN color space
|
||||||
// TODO: Implement Pattern color space
|
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
||||||
|
@ -296,6 +296,22 @@ QRectF PDFDocumentDataLoaderDecorator::readRectangle(const PDFObject& object, co
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMatrix PDFDocumentDataLoaderDecorator::readMatrixFromDictionary(const PDFDictionary* dictionary, const char* key, QMatrix defaultValue)
|
||||||
|
{
|
||||||
|
if (dictionary->hasKey(key))
|
||||||
|
{
|
||||||
|
std::vector<PDFReal> matrixNumbers = readNumberArrayFromDictionary(dictionary, key);
|
||||||
|
if (matrixNumbers.size() != 6)
|
||||||
|
{
|
||||||
|
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid number of matrix elements. Expected 6, actual %1.").arg(matrixNumbers.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return QMatrix(matrixNumbers[0], matrixNumbers[1], matrixNumbers[2], matrixNumbers[3], matrixNumbers[4], matrixNumbers[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<PDFReal> PDFDocumentDataLoaderDecorator::readNumberArrayFromDictionary(const PDFDictionary* dictionary, const char* key)
|
std::vector<PDFReal> PDFDocumentDataLoaderDecorator::readNumberArrayFromDictionary(const PDFDictionary* dictionary, const char* key)
|
||||||
{
|
{
|
||||||
if (dictionary->hasKey(key))
|
if (dictionary->hasKey(key))
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "pdfsecurityhandler.h"
|
#include "pdfsecurityhandler.h"
|
||||||
|
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
|
#include <QMatrix>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
@ -195,6 +196,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to read matrix from the dictionary. If matrix entry is not present, default value is returned.
|
||||||
|
/// If it is present and invalid, exception is thrown.
|
||||||
|
QMatrix readMatrixFromDictionary(const PDFDictionary* dictionary, const char* key, QMatrix defaultValue);
|
||||||
|
|
||||||
/// Tries to read array of real values from dictionary. If entry dictionary doesn't exist,
|
/// Tries to read array of real values from dictionary. If entry dictionary doesn't exist,
|
||||||
/// or error occurs, empty array is returned.
|
/// or error occurs, empty array is returned.
|
||||||
std::vector<PDFReal> readNumberArrayFromDictionary(const PDFDictionary* dictionary, const char* key);
|
std::vector<PDFReal> readNumberArrayFromDictionary(const PDFDictionary* dictionary, const char* key);
|
||||||
|
@ -116,6 +116,11 @@ QRectF PDFPage::getRectMM(const QRectF& rect) const
|
|||||||
convertPDFPointToMM(rect.height()));
|
convertPDFPointToMM(rect.height()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRectF PDFPage::getRotatedMediaBox() const
|
||||||
|
{
|
||||||
|
return getRotatedBox(getMediaBox(), getPageRotation());
|
||||||
|
}
|
||||||
|
|
||||||
QRectF PDFPage::getRotatedBox(const QRectF& rect, PageRotation rotation)
|
QRectF PDFPage::getRotatedBox(const QRectF& rect, PageRotation rotation)
|
||||||
{
|
{
|
||||||
switch (rotation)
|
switch (rotation)
|
||||||
|
@ -94,6 +94,8 @@ public:
|
|||||||
inline QRectF getTrimBoxMM() const { return getRectMM(m_trimBox); }
|
inline QRectF getTrimBoxMM() const { return getRectMM(m_trimBox); }
|
||||||
inline QRectF getArtBoxMM() const { return getRectMM(m_artBox); }
|
inline QRectF getArtBoxMM() const { return getRectMM(m_artBox); }
|
||||||
|
|
||||||
|
QRectF getRotatedMediaBox() const;
|
||||||
|
|
||||||
static QRectF getRotatedBox(const QRectF& rect, PageRotation rotation);
|
static QRectF getRotatedBox(const QRectF& rect, PageRotation rotation);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "pdfdocument.h"
|
#include "pdfdocument.h"
|
||||||
#include "pdfexception.h"
|
#include "pdfexception.h"
|
||||||
#include "pdfimage.h"
|
#include "pdfimage.h"
|
||||||
|
#include "pdfpattern.h"
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
@ -173,12 +174,14 @@ void PDFPageContentProcessor::initDictionaries(const PDFObject& resourcesObject)
|
|||||||
m_xobjectDictionary = getDictionary("XObject");
|
m_xobjectDictionary = getDictionary("XObject");
|
||||||
m_extendedGraphicStateDictionary = getDictionary(PDF_RESOURCE_EXTGSTATE);
|
m_extendedGraphicStateDictionary = getDictionary(PDF_RESOURCE_EXTGSTATE);
|
||||||
m_propertiesDictionary = getDictionary("Properties");
|
m_propertiesDictionary = getDictionary("Properties");
|
||||||
|
m_shadingDictionary = getDictionary("Shading");
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
|
PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
|
||||||
const PDFDocument* document,
|
const PDFDocument* document,
|
||||||
const PDFFontCache* fontCache,
|
const PDFFontCache* fontCache,
|
||||||
const PDFOptionalContentActivity* optionalContentActivity) :
|
const PDFOptionalContentActivity* optionalContentActivity,
|
||||||
|
QMatrix patternBaseMatrix) :
|
||||||
m_page(page),
|
m_page(page),
|
||||||
m_document(document),
|
m_document(document),
|
||||||
m_fontCache(fontCache),
|
m_fontCache(fontCache),
|
||||||
@ -188,12 +191,18 @@ PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page,
|
|||||||
m_xobjectDictionary(nullptr),
|
m_xobjectDictionary(nullptr),
|
||||||
m_extendedGraphicStateDictionary(nullptr),
|
m_extendedGraphicStateDictionary(nullptr),
|
||||||
m_propertiesDictionary(nullptr),
|
m_propertiesDictionary(nullptr),
|
||||||
|
m_shadingDictionary(nullptr),
|
||||||
m_textBeginEndState(0),
|
m_textBeginEndState(0),
|
||||||
m_compatibilityBeginEndState(0)
|
m_compatibilityBeginEndState(0),
|
||||||
|
m_patternBaseMatrix(patternBaseMatrix)
|
||||||
{
|
{
|
||||||
Q_ASSERT(page);
|
Q_ASSERT(page);
|
||||||
Q_ASSERT(document);
|
Q_ASSERT(document);
|
||||||
|
|
||||||
|
QPainterPath pageRectPath;
|
||||||
|
pageRectPath.addRect(m_page->getRotatedMediaBox());
|
||||||
|
m_pageBoundingRectDeviceSpace = patternBaseMatrix.map(pageRectPath).boundingRect();
|
||||||
|
|
||||||
initDictionaries(m_page->getResources());
|
initDictionaries(m_page->getResources());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,6 +548,8 @@ void PDFPageContentProcessor::processForm(const QMatrix& matrix, const QRectF& b
|
|||||||
m_graphicState.setCurrentTransformationMatrix(formMatrix);
|
m_graphicState.setCurrentTransformationMatrix(formMatrix);
|
||||||
updateGraphicState();
|
updateGraphicState();
|
||||||
|
|
||||||
|
PDFTemporaryValueChange patternMatrixGuard(&m_patternBaseMatrix, formMatrix);
|
||||||
|
|
||||||
// If the clipping box is valid, then use clipping. Clipping box is in the form coordinate system
|
// If the clipping box is valid, then use clipping. Clipping box is in the form coordinate system
|
||||||
if (boundingBox.isValid())
|
if (boundingBox.isValid())
|
||||||
{
|
{
|
||||||
@ -971,6 +982,13 @@ void PDFPageContentProcessor::processCommand(const QByteArray& command)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Operator::ShadingPaintShape:
|
||||||
|
{
|
||||||
|
// sh, paint shape
|
||||||
|
invokeOperator(&PDFPageContentProcessor::operatorShadingPaintShape);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Operator::MarkedContentPoint:
|
case Operator::MarkedContentPoint:
|
||||||
{
|
{
|
||||||
// MP, marked content point
|
// MP, marked content point
|
||||||
@ -2024,6 +2042,34 @@ void PDFPageContentProcessor::operatorTextSetSpacingAndShowText(PDFReal t_w, PDF
|
|||||||
operatorTextNextLineShowText(qMove(text));
|
operatorTextNextLineShowText(qMove(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFPageContentProcessor::operatorShadingPaintShape(PDFPageContentProcessor::PDFOperandName name)
|
||||||
|
{
|
||||||
|
QMatrix matrix = getGraphicState()->getCurrentTransformationMatrix();
|
||||||
|
PDFPageContentProcessorStateGuard guard(this);
|
||||||
|
PDFTemporaryValueChange guard2(&m_patternBaseMatrix, matrix);
|
||||||
|
|
||||||
|
if (!m_shadingDictionary)
|
||||||
|
{
|
||||||
|
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Shading '%1' not found.").arg(QString::fromLatin1(name.name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFPatternPtr pattern = PDFPattern::createShadingPattern(m_colorSpaceDictionary, m_document, m_shadingDictionary->get(name.name), QMatrix(), PDFObject(), true);
|
||||||
|
|
||||||
|
// We will do a trick: we will set current fill color space, and then paint
|
||||||
|
// bounding rectangle in the color pattern.
|
||||||
|
m_graphicState.setFillColorSpace(PDFColorSpacePointer(new PDFPatternColorSpace(qMove(pattern))));
|
||||||
|
updateGraphicState();
|
||||||
|
|
||||||
|
Q_ASSERT(matrix.isInvertible());
|
||||||
|
QMatrix inverted = matrix.inverted();
|
||||||
|
|
||||||
|
QPainterPath deviceBoundingRectPath;
|
||||||
|
deviceBoundingRectPath.addRect(m_pageBoundingRectDeviceSpace);
|
||||||
|
QPainterPath boundingRectPath = inverted.map(deviceBoundingRectPath);
|
||||||
|
|
||||||
|
performPathPainting(boundingRectPath, false, true, false, boundingRectPath.fillRule());
|
||||||
|
}
|
||||||
|
|
||||||
void PDFPageContentProcessor::paintXObjectImage(const PDFStream* stream)
|
void PDFPageContentProcessor::paintXObjectImage(const PDFStream* stream)
|
||||||
{
|
{
|
||||||
PDFColorSpacePointer colorSpace;
|
PDFColorSpacePointer colorSpace;
|
||||||
@ -2112,18 +2158,7 @@ void PDFPageContentProcessor::operatorPaintXObject(PDFPageContentProcessor::PDFO
|
|||||||
QRectF boundingBox = loader.readRectangle(streamDictionary->get("BBox"), QRectF());
|
QRectF boundingBox = loader.readRectangle(streamDictionary->get("BBox"), QRectF());
|
||||||
|
|
||||||
// Read the transformation matrix, if it is present
|
// Read the transformation matrix, if it is present
|
||||||
QMatrix transformationMatrix;
|
QMatrix transformationMatrix = loader.readMatrixFromDictionary(streamDictionary, "Matrix", QMatrix());
|
||||||
|
|
||||||
if (streamDictionary->hasKey("Matrix"))
|
|
||||||
{
|
|
||||||
std::vector<PDFReal> matrixNumbers = loader.readNumberArrayFromDictionary(streamDictionary, "Matrix");
|
|
||||||
if (matrixNumbers.size() != 6)
|
|
||||||
{
|
|
||||||
throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Invalid number of matrix elements. Expected 6, actual %1.").arg(matrixNumbers.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
transformationMatrix = QMatrix(matrixNumbers[0], matrixNumbers[1], matrixNumbers[2], matrixNumbers[3], matrixNumbers[4], matrixNumbers[5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the dictionary content
|
// Read the dictionary content
|
||||||
QByteArray content = m_document->getDecodedStream(stream);
|
QByteArray content = m_document->getDecodedStream(stream);
|
||||||
@ -2721,7 +2756,8 @@ PDFPageContentProcessor::PDFPageContentProcessorStateGuard::PDFPageContentProces
|
|||||||
m_fontDictionary(processor->m_fontDictionary),
|
m_fontDictionary(processor->m_fontDictionary),
|
||||||
m_xobjectDictionary(processor->m_xobjectDictionary),
|
m_xobjectDictionary(processor->m_xobjectDictionary),
|
||||||
m_extendedGraphicStateDictionary(processor->m_extendedGraphicStateDictionary),
|
m_extendedGraphicStateDictionary(processor->m_extendedGraphicStateDictionary),
|
||||||
m_propertiesDictionary(processor->m_propertiesDictionary)
|
m_propertiesDictionary(processor->m_propertiesDictionary),
|
||||||
|
m_shadingDictionary(processor->m_shadingDictionary)
|
||||||
{
|
{
|
||||||
m_processor->operatorSaveGraphicState();
|
m_processor->operatorSaveGraphicState();
|
||||||
}
|
}
|
||||||
@ -2734,6 +2770,7 @@ PDFPageContentProcessor::PDFPageContentProcessorStateGuard::~PDFPageContentProce
|
|||||||
m_processor->m_xobjectDictionary = m_xobjectDictionary;
|
m_processor->m_xobjectDictionary = m_xobjectDictionary;
|
||||||
m_processor->m_extendedGraphicStateDictionary = m_extendedGraphicStateDictionary;
|
m_processor->m_extendedGraphicStateDictionary = m_extendedGraphicStateDictionary;
|
||||||
m_processor->m_propertiesDictionary = m_propertiesDictionary;
|
m_processor->m_propertiesDictionary = m_propertiesDictionary;
|
||||||
|
m_processor->m_shadingDictionary = m_shadingDictionary;
|
||||||
|
|
||||||
m_processor->operatorRestoreGraphicState();
|
m_processor->operatorRestoreGraphicState();
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,8 @@ public:
|
|||||||
explicit PDFPageContentProcessor(const PDFPage* page,
|
explicit PDFPageContentProcessor(const PDFPage* page,
|
||||||
const PDFDocument* document,
|
const PDFDocument* document,
|
||||||
const PDFFontCache* fontCache,
|
const PDFFontCache* fontCache,
|
||||||
const PDFOptionalContentActivity* optionalContentActivity);
|
const PDFOptionalContentActivity* optionalContentActivity,
|
||||||
|
QMatrix patternBaseMatrix);
|
||||||
virtual ~PDFPageContentProcessor();
|
virtual ~PDFPageContentProcessor();
|
||||||
|
|
||||||
enum class Operator
|
enum class Operator
|
||||||
@ -473,6 +474,7 @@ private:
|
|||||||
const PDFDictionary* m_xobjectDictionary;
|
const PDFDictionary* m_xobjectDictionary;
|
||||||
const PDFDictionary* m_extendedGraphicStateDictionary;
|
const PDFDictionary* m_extendedGraphicStateDictionary;
|
||||||
const PDFDictionary* m_propertiesDictionary;
|
const PDFDictionary* m_propertiesDictionary;
|
||||||
|
const PDFDictionary* m_shadingDictionary;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Wrapper for PDF Name
|
/// Wrapper for PDF Name
|
||||||
@ -647,6 +649,9 @@ private:
|
|||||||
void operatorTextNextLineShowText(PDFOperandString text); ///< ', move to the next line and show text ("string '" is equivalent to "T* string Tj")
|
void operatorTextNextLineShowText(PDFOperandString text); ///< ', move to the next line and show text ("string '" is equivalent to "T* string Tj")
|
||||||
void operatorTextSetSpacingAndShowText(PDFReal t_w, PDFReal t_c, PDFOperandString text); ///< ", move to the next line, set spacing and show text (equivalent to sequence "w1 Tw w2 Tc string '")
|
void operatorTextSetSpacingAndShowText(PDFReal t_w, PDFReal t_c, PDFOperandString text); ///< ", move to the next line, set spacing and show text (equivalent to sequence "w1 Tw w2 Tc string '")
|
||||||
|
|
||||||
|
// Shading pattern: sh
|
||||||
|
void operatorShadingPaintShape(PDFOperandName name); ///< sh, paint shape
|
||||||
|
|
||||||
// XObject: Do
|
// XObject: Do
|
||||||
void operatorPaintXObject(PDFOperandName name); ///< Do, paint the X Object (image, form, ...)
|
void operatorPaintXObject(PDFOperandName name); ///< Do, paint the X Object (image, form, ...)
|
||||||
|
|
||||||
@ -691,6 +696,7 @@ private:
|
|||||||
const PDFDictionary* m_xobjectDictionary;
|
const PDFDictionary* m_xobjectDictionary;
|
||||||
const PDFDictionary* m_extendedGraphicStateDictionary;
|
const PDFDictionary* m_extendedGraphicStateDictionary;
|
||||||
const PDFDictionary* m_propertiesDictionary;
|
const PDFDictionary* m_propertiesDictionary;
|
||||||
|
const PDFDictionary* m_shadingDictionary;
|
||||||
|
|
||||||
// Default color spaces
|
// Default color spaces
|
||||||
PDFColorSpacePointer m_deviceGrayColorSpace;
|
PDFColorSpacePointer m_deviceGrayColorSpace;
|
||||||
@ -727,6 +733,14 @@ private:
|
|||||||
/// Actual clipping path obtained from text. Clipping path
|
/// Actual clipping path obtained from text. Clipping path
|
||||||
/// is in device space coordinates.
|
/// is in device space coordinates.
|
||||||
QPainterPath m_textClippingPath;
|
QPainterPath m_textClippingPath;
|
||||||
|
|
||||||
|
/// Base matrix to be used when drawing patterns. Concatenate this matrix
|
||||||
|
/// with pattern matrix to get transformation from pattern space to device space.
|
||||||
|
QMatrix m_patternBaseMatrix;
|
||||||
|
|
||||||
|
/// Bounding rectangle of pages media box in device space coordinates. If drawing rotation
|
||||||
|
/// is zero, then it corresponds to the scaled media box of the page.
|
||||||
|
QRectF m_pageBoundingRectDeviceSpace;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -29,7 +29,7 @@ PDFPainter::PDFPainter(QPainter* painter,
|
|||||||
const PDFDocument* document,
|
const PDFDocument* document,
|
||||||
const PDFFontCache* fontCache,
|
const PDFFontCache* fontCache,
|
||||||
const PDFOptionalContentActivity* optionalContentActivity) :
|
const PDFOptionalContentActivity* optionalContentActivity) :
|
||||||
PDFPageContentProcessor(page, document, fontCache, optionalContentActivity),
|
PDFPageContentProcessor(page, document, fontCache, optionalContentActivity, pagePointToDevicePointMatrix),
|
||||||
m_painter(painter),
|
m_painter(painter),
|
||||||
m_features(features),
|
m_features(features),
|
||||||
m_pagePointToDevicePointMatrix(pagePointToDevicePointMatrix)
|
m_pagePointToDevicePointMatrix(pagePointToDevicePointMatrix)
|
||||||
@ -47,6 +47,7 @@ PDFPainter::~PDFPainter()
|
|||||||
|
|
||||||
void PDFPainter::performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule)
|
void PDFPainter::performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule)
|
||||||
{
|
{
|
||||||
|
// TODO: Implement Pattern features (shading/tiling)
|
||||||
if (isContentSuppressed())
|
if (isContentSuppressed())
|
||||||
{
|
{
|
||||||
// Content is suppressed, do not paint anything
|
// Content is suppressed, do not paint anything
|
||||||
|
189
PdfForQtLib/sources/pdfpattern.cpp
Normal file
189
PdfForQtLib/sources/pdfpattern.cpp
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// 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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "pdfpattern.h"
|
||||||
|
#include "pdfdocument.h"
|
||||||
|
#include "pdfexception.h"
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
PatternType PDFShadingPattern::getType() const
|
||||||
|
{
|
||||||
|
return PatternType::Shading;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShadingType PDFAxialShading::getShadingType() const
|
||||||
|
{
|
||||||
|
return ShadingType::Axial;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFPatternPtr PDFPattern::createPattern(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFObject& object)
|
||||||
|
{
|
||||||
|
const PDFObject& dereferencedObject = document->getObject(object);
|
||||||
|
if (dereferencedObject.isDictionary())
|
||||||
|
{
|
||||||
|
PDFPatternPtr result;
|
||||||
|
|
||||||
|
const PDFDictionary* patternDictionary = dereferencedObject.getDictionary();
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
|
||||||
|
if (loader.readNameFromDictionary(patternDictionary, "Type") != "Pattern")
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
||||||
|
}
|
||||||
|
|
||||||
|
const PatternType patternType = static_cast<PatternType>(loader.readIntegerFromDictionary(patternDictionary, "PatternType", static_cast<PDFInteger>(PatternType::Invalid)));
|
||||||
|
switch (patternType)
|
||||||
|
{
|
||||||
|
case PatternType::Tiling:
|
||||||
|
{
|
||||||
|
// TODO: Implement tiling pattern
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Tiling pattern not implemented."));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PatternType::Shading:
|
||||||
|
{
|
||||||
|
PDFObject patternGraphicState = document->getObject(patternDictionary->get("ExtGState"));
|
||||||
|
QMatrix matrix = loader.readMatrixFromDictionary(patternDictionary, "Matrix", QMatrix());
|
||||||
|
return createShadingPattern(colorSpaceDictionary, document, patternDictionary->get("Shading"), matrix, patternGraphicState, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid pattern."));
|
||||||
|
return PDFPatternPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDictionary,
|
||||||
|
const PDFDocument* document,
|
||||||
|
const PDFObject& shadingObject,
|
||||||
|
const QMatrix& matrix,
|
||||||
|
const PDFObject& patternGraphicState,
|
||||||
|
bool ignoreBackgroundColor)
|
||||||
|
{
|
||||||
|
const PDFObject& dereferencedShadingObject = document->getObject(shadingObject);
|
||||||
|
if (!dereferencedShadingObject.isDictionary())
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid shading."));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
const PDFDictionary* shadingDictionary = dereferencedShadingObject.getDictionary();
|
||||||
|
|
||||||
|
// Parse common data for all shadings
|
||||||
|
PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(colorSpaceDictionary, document, shadingDictionary->get("ColorSpace"));
|
||||||
|
QColor backgroundColor;
|
||||||
|
if (!ignoreBackgroundColor)
|
||||||
|
{
|
||||||
|
std::vector<PDFReal> backgroundColorValues = loader.readNumberArrayFromDictionary(shadingDictionary, "Background");
|
||||||
|
if (!backgroundColorValues.empty())
|
||||||
|
{
|
||||||
|
backgroundColor = colorSpace->getCheckedColor(PDFAbstractColorSpace::convertToColor(backgroundColorValues));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QRectF boundingBox = loader.readRectangle(shadingDictionary->get("BBox"), QRectF());
|
||||||
|
bool antialias = loader.readBooleanFromDictionary(shadingDictionary, "AntiAlias", false);
|
||||||
|
const PDFObject& extendObject = document->getObject(shadingDictionary->get("Extend"));
|
||||||
|
bool extendStart = false;
|
||||||
|
bool extendEnd = false;
|
||||||
|
if (extendObject.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* array = extendObject.getArray();
|
||||||
|
if (array->getCount() != 2)
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid shading pattern extends. Expected 2, but %1 provided.").arg(array->getCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
extendStart = loader.readBoolean(array->getItem(0), false);
|
||||||
|
extendEnd = loader.readBoolean(array->getItem(1), false);
|
||||||
|
}
|
||||||
|
std::vector<PDFFunctionPtr> functions;
|
||||||
|
const PDFObject& functionsObject = document->getObject(shadingDictionary->get("Function"));
|
||||||
|
if (functionsObject.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* functionsArray = functionsObject.getArray();
|
||||||
|
functions.reserve(functionsArray->getCount());
|
||||||
|
for (size_t i = 0, functionCount = functionsArray->getCount(); i < functionCount; ++i)
|
||||||
|
{
|
||||||
|
functions.push_back(PDFFunction::createFunction(document, functionsArray->getItem(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!functionsObject.isNull())
|
||||||
|
{
|
||||||
|
functions.push_back(PDFFunction::createFunction(document, functionsObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShadingType shadingType = static_cast<ShadingType>(loader.readIntegerFromDictionary(shadingDictionary, "ShadingType", static_cast<PDFInteger>(ShadingType::Invalid)));
|
||||||
|
switch (shadingType)
|
||||||
|
{
|
||||||
|
case ShadingType::Axial:
|
||||||
|
{
|
||||||
|
PDFAxialShading* axialShading = new PDFAxialShading();
|
||||||
|
PDFPatternPtr result(axialShading);
|
||||||
|
|
||||||
|
std::vector<PDFReal> coordinates = loader.readNumberArrayFromDictionary(shadingDictionary, "Coords");
|
||||||
|
if (coordinates.size() != 4)
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid axial shading pattern coordinates. Expected 4, but %1 provided.").arg(coordinates.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PDFReal> domain = loader.readNumberArrayFromDictionary(shadingDictionary, "Domain");
|
||||||
|
if (domain.empty())
|
||||||
|
{
|
||||||
|
domain = { 0.0, 1.0 };
|
||||||
|
}
|
||||||
|
if (domain.size() != 2)
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid axial shading pattern domain. Expected 2, but %1 provided.").arg(domain.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load items for axial shading
|
||||||
|
axialShading->m_antiAlias = antialias;
|
||||||
|
axialShading->m_backgroundColor = backgroundColor;
|
||||||
|
axialShading->m_colorSpace = colorSpace;
|
||||||
|
axialShading->m_boundingBox = boundingBox;
|
||||||
|
axialShading->m_domainStart = domain[0];
|
||||||
|
axialShading->m_domainEnd = domain[1];
|
||||||
|
axialShading->m_startPoint = QPointF(coordinates[0], coordinates[1]);
|
||||||
|
axialShading->m_endPoint = QPointF(coordinates[2], coordinates[3]);
|
||||||
|
axialShading->m_extendStart = extendStart;
|
||||||
|
axialShading->m_extendEnd = extendEnd;
|
||||||
|
axialShading->m_functions = qMove(functions);
|
||||||
|
axialShading->m_matrix = matrix;
|
||||||
|
axialShading->m_patternGraphicState = patternGraphicState;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid shading pattern type (%1).").arg(static_cast<PDFInteger>(shadingType)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid shading."));
|
||||||
|
return PDFPatternPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pdf
|
158
PdfForQtLib/sources/pdfpattern.h
Normal file
158
PdfForQtLib/sources/pdfpattern.h
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// 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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef PDFPATTERN_H
|
||||||
|
#define PDFPATTERN_H
|
||||||
|
|
||||||
|
#include "pdfobject.h"
|
||||||
|
#include "pdffunction.h"
|
||||||
|
#include "pdfcolorspaces.h"
|
||||||
|
|
||||||
|
#include <QMatrix>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
class PDFPattern;
|
||||||
|
|
||||||
|
using PDFPatternPtr = std::shared_ptr<PDFPattern>;
|
||||||
|
|
||||||
|
enum class PatternType
|
||||||
|
{
|
||||||
|
Invalid = 0,
|
||||||
|
Tiling = 1,
|
||||||
|
Shading = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ShadingType
|
||||||
|
{
|
||||||
|
Invalid = 0,
|
||||||
|
Function = 1,
|
||||||
|
Axial = 2,
|
||||||
|
Radial = 3,
|
||||||
|
FreeFormGouradTriangle = 4,
|
||||||
|
LatticeFormGouradTriangle = 5,
|
||||||
|
CoonsPatchMesh = 6,
|
||||||
|
TensorProductPatchMesh = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Represents tiling/shading pattern
|
||||||
|
class PDFPattern
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFPattern() = default;
|
||||||
|
virtual ~PDFPattern() = default;
|
||||||
|
|
||||||
|
virtual PatternType getType() const = 0;
|
||||||
|
|
||||||
|
/// Returns bounding box in the shadings target coordinate system (not in
|
||||||
|
/// pattern coordinate system).
|
||||||
|
const QRectF& getBoundingBox() const { return m_boundingBox; }
|
||||||
|
|
||||||
|
/// Returns transformation matrix from pattern space to the default
|
||||||
|
/// target space.
|
||||||
|
const QMatrix& getMatrix() const { return m_matrix; }
|
||||||
|
|
||||||
|
/// Create pattern from the object. If error occurs, exception is thrown
|
||||||
|
/// \param colorSpaceDictionary Color space dictionary
|
||||||
|
/// \param document Document, owning the pdf object
|
||||||
|
/// \param object Object defining the pattern
|
||||||
|
static PDFPatternPtr createPattern(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFObject& object);
|
||||||
|
|
||||||
|
/// Create shading pattern from the object. If error occurs, exception is thrown
|
||||||
|
/// \param colorSpaceDictionary Color space dictionary
|
||||||
|
/// \param document Document, owning the pdf object
|
||||||
|
/// \param object Object defining the shading
|
||||||
|
/// \param matrix Matrix converting reference coordinate system to the device coordinate system
|
||||||
|
/// \param patternGraphicState Pattern graphic state
|
||||||
|
/// \param ignoreBackgroundColor If set, then ignores background color, even if it is present
|
||||||
|
static PDFPatternPtr createShadingPattern(const PDFDictionary* colorSpaceDictionary,
|
||||||
|
const PDFDocument* document,
|
||||||
|
const PDFObject& shadingObject,
|
||||||
|
const QMatrix& matrix,
|
||||||
|
const PDFObject& patternGraphicState,
|
||||||
|
bool ignoreBackgroundColor);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QRectF m_boundingBox;
|
||||||
|
QMatrix m_matrix;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Shading pattern - smooth color distribution along the pattern's space
|
||||||
|
class PDFShadingPattern : public PDFPattern
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFShadingPattern() = default;
|
||||||
|
|
||||||
|
virtual PatternType getType() const override;
|
||||||
|
virtual ShadingType getShadingType() const = 0;
|
||||||
|
|
||||||
|
/// Returns patterns graphic state. This state must be applied before
|
||||||
|
/// the shading pattern is painted to the target device.
|
||||||
|
const PDFObject& getPatternGraphicState() const { return m_patternGraphicState; }
|
||||||
|
|
||||||
|
/// Returns color space of the pattern.
|
||||||
|
const PDFAbstractColorSpace* getColorSpace() const;
|
||||||
|
|
||||||
|
/// Returns patterns background color (if pattern has background color).
|
||||||
|
/// If pattern has not background color, then invalid color is returned.
|
||||||
|
const QColor& getBackgroundColor() const { return m_backgroundColor; }
|
||||||
|
|
||||||
|
/// Returns true, if shading pattern should be anti-aliased
|
||||||
|
bool isAntialiasing() const { return m_antiAlias; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class PDFPattern;
|
||||||
|
|
||||||
|
PDFObject m_patternGraphicState;
|
||||||
|
PDFColorSpacePointer m_colorSpace;
|
||||||
|
QColor m_backgroundColor;
|
||||||
|
bool m_antiAlias = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFSingleDimensionShading : public PDFShadingPattern
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFSingleDimensionShading() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class PDFPattern;
|
||||||
|
|
||||||
|
std::vector<PDFFunctionPtr> m_functions;
|
||||||
|
QPointF m_startPoint;
|
||||||
|
QPointF m_endPoint;
|
||||||
|
PDFReal m_domainStart = 0.0;
|
||||||
|
PDFReal m_domainEnd = 1.0;
|
||||||
|
bool m_extendStart = false;
|
||||||
|
bool m_extendEnd = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFAxialShading : public PDFSingleDimensionShading
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFAxialShading() = default;
|
||||||
|
|
||||||
|
virtual ShadingType getShadingType() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class PDFPattern;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pdf
|
||||||
|
|
||||||
|
#endif // PDFPATTERN_H
|
@ -48,12 +48,10 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& recta
|
|||||||
const PDFPage* page = catalog->getPage(pageIndex);
|
const PDFPage* page = catalog->getPage(pageIndex);
|
||||||
Q_ASSERT(page);
|
Q_ASSERT(page);
|
||||||
|
|
||||||
QRectF mediaBox = page->getMediaBox();
|
QRectF mediaBox = page->getRotatedMediaBox();
|
||||||
const PageRotation rotation = page->getPageRotation();
|
|
||||||
mediaBox = page->getRotatedBox(mediaBox, rotation);
|
|
||||||
|
|
||||||
QMatrix matrix;
|
QMatrix matrix;
|
||||||
switch (rotation)
|
switch (page->getPageRotation())
|
||||||
{
|
{
|
||||||
case PageRotation::None:
|
case PageRotation::None:
|
||||||
{
|
{
|
||||||
|
@ -101,6 +101,33 @@ private:
|
|||||||
Value m_bitsInBuffer;
|
Value m_bitsInBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Simple class guard, for properly saving/restoring new/old value. In the constructor,
|
||||||
|
/// new value is stored in the pointer (old one is being saved), and in the destructor,
|
||||||
|
/// old value is restored. This object assumes, that value is not a null pointer.
|
||||||
|
template<typename Value>
|
||||||
|
class PDFTemporaryValueChange
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
/// \param value Value pointer (must not be a null pointer)
|
||||||
|
/// \param newValue New value to be set to the pointer
|
||||||
|
explicit inline PDFTemporaryValueChange(Value* valuePointer, Value newValue) :
|
||||||
|
m_oldValue(qMove(*valuePointer)),
|
||||||
|
m_value(valuePointer)
|
||||||
|
{
|
||||||
|
*valuePointer = qMove(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~PDFTemporaryValueChange()
|
||||||
|
{
|
||||||
|
*m_value = qMove(m_oldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Value m_oldValue;
|
||||||
|
Value* m_value;
|
||||||
|
};
|
||||||
|
|
||||||
/// Performs linear mapping of value x in interval [x_min, x_max] to the interval [y_min, y_max].
|
/// Performs linear mapping of value x in interval [x_min, x_max] to the interval [y_min, y_max].
|
||||||
/// \param x Value to be linearly remapped from interval [x_min, x_max] to the interval [y_min, y_max].
|
/// \param x Value to be linearly remapped from interval [x_min, x_max] to the interval [y_min, y_max].
|
||||||
/// \param x_min Start of the input interval
|
/// \param x_min Start of the input interval
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
|
|
||||||
QT += testlib
|
QT += testlib
|
||||||
QT -= gui
|
|
||||||
|
|
||||||
CONFIG += qt console warn_on depend_includepath testcase
|
CONFIG += qt console warn_on depend_includepath testcase
|
||||||
CONFIG -= app_bundle
|
CONFIG -= app_bundle
|
||||||
|
Reference in New Issue
Block a user