From 59ad9afadd92d5f79338bf036fa7f86c2ca6b03e Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Thu, 26 Sep 2019 19:14:04 +0200 Subject: [PATCH] Uncolored tiling patterns, tiling patterns colors --- PdfForQtLib/sources/pdfcolorspaces.cpp | 12 ++- PdfForQtLib/sources/pdfcolorspaces.h | 19 ++++- .../sources/pdfpagecontentprocessor.cpp | 82 +++++++++++++++---- PdfForQtLib/sources/pdfpagecontentprocessor.h | 7 +- PdfForQtLib/sources/pdfpainter.cpp | 2 + PdfForQtLib/sources/pdfpattern.cpp | 2 +- PdfForQtLib/sources/pdfpattern.h | 12 +-- 7 files changed, 104 insertions(+), 32 deletions(-) diff --git a/PdfForQtLib/sources/pdfcolorspaces.cpp b/PdfForQtLib/sources/pdfcolorspaces.cpp index 056dcf0..1763c1a 100644 --- a/PdfForQtLib/sources/pdfcolorspaces.cpp +++ b/PdfForQtLib/sources/pdfcolorspaces.cpp @@ -241,7 +241,7 @@ 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(static_cast(getColorComponentCount()), static_cast(color.size()))); + throw PDFParserException(PDFTranslationContext::tr("Invalid number of color components. Expected number is %1, actual number is %2.").arg(static_cast(getColorComponentCount())).arg(static_cast(color.size()))); } return getColor(color); @@ -352,7 +352,13 @@ PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictio if (name == COLOR_SPACE_NAME_PATTERN) { - return PDFColorSpacePointer(new PDFPatternColorSpace(std::make_shared())); + PDFColorSpacePointer uncoloredPatternColorSpace; + if (count == 2) + { + uncoloredPatternColorSpace = createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(array->getItem(1)), recursion); + } + + return PDFColorSpacePointer(new PDFPatternColorSpace(std::make_shared(), qMove(uncoloredPatternColorSpace), PDFColor())); } if (dictionary) @@ -413,7 +419,7 @@ PDFColorSpacePointer PDFAbstractColorSpace::createDeviceColorSpaceByNameImpl(con if (name == COLOR_SPACE_NAME_PATTERN) { - return PDFColorSpacePointer(new PDFPatternColorSpace(std::make_shared())); + return PDFColorSpacePointer(new PDFPatternColorSpace(std::make_shared(), nullptr, PDFColor())); } if (name == COLOR_SPACE_NAME_DEVICE_GRAY || name == COLOR_SPACE_NAME_ABBREVIATION_DEVICE_GRAY) diff --git a/PdfForQtLib/sources/pdfcolorspaces.h b/PdfForQtLib/sources/pdfcolorspaces.h index bb4b7ba..8126d05 100644 --- a/PdfForQtLib/sources/pdfcolorspaces.h +++ b/PdfForQtLib/sources/pdfcolorspaces.h @@ -34,6 +34,7 @@ class PDFPattern; class PDFDocument; class PDFDictionary; class PDFAbstractColorSpace; +class PDFPatternColorSpace; using PDFColorComponent = float; using PDFColor = PDFFlatArray; @@ -211,7 +212,7 @@ public: virtual QColor getColor(const PDFColor& color) const = 0; virtual size_t getColorComponentCount() const = 0; virtual QImage getImage(const PDFImageData& imageData) const; - virtual const PDFPattern* getPattern() const { return nullptr; } + virtual const PDFPatternColorSpace* asPatternColorSpace() const { return nullptr; } /// 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. @@ -592,17 +593,29 @@ private: class PDFPatternColorSpace : public PDFAbstractColorSpace { public: - explicit PDFPatternColorSpace(std::shared_ptr&& pattern) : m_pattern(qMove(pattern)) { } + explicit PDFPatternColorSpace(std::shared_ptr&& pattern, PDFColorSpacePointer&& uncoloredPatternColorSpace, PDFColor uncoloredPatternColor) : + m_pattern(qMove(pattern)), + m_uncoloredPatternColorSpace(qMove(uncoloredPatternColorSpace)), + m_uncoloredPatternColor(qMove(uncoloredPatternColor)) + { + + } + virtual ~PDFPatternColorSpace() override = default; virtual QColor getDefaultColor() const override; virtual QColor getColor(const PDFColor& color) const override; virtual size_t getColorComponentCount() const override; + virtual const PDFPatternColorSpace* asPatternColorSpace() const override { return this; } - virtual const PDFPattern* getPattern() const override { return m_pattern.get(); } + const PDFPattern* getPattern() const { return m_pattern.get(); } + PDFColorSpacePointer getUncoloredPatternColorSpace() const { return m_uncoloredPatternColorSpace; } + PDFColor getUncoloredPatternColor() const { return m_uncoloredPatternColor; } private: std::shared_ptr m_pattern; + PDFColorSpacePointer m_uncoloredPatternColorSpace; + PDFColor m_uncoloredPatternColor; }; } // namespace pdf diff --git a/PdfForQtLib/sources/pdfpagecontentprocessor.cpp b/PdfForQtLib/sources/pdfpagecontentprocessor.cpp index 93eb9a4..51e9261 100644 --- a/PdfForQtLib/sources/pdfpagecontentprocessor.cpp +++ b/PdfForQtLib/sources/pdfpagecontentprocessor.cpp @@ -594,15 +594,15 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool if (fill) { - const PDFPattern* pattern = getGraphicState()->getFillColorSpace()->getPattern(); - if (pattern) + if (const PDFPatternColorSpace* patternColorSpace = getGraphicState()->getFillColorSpace()->asPatternColorSpace()) { + const PDFPattern* pattern = patternColorSpace->getPattern(); switch (pattern->getType()) { case PatternType::Tiling: { const PDFTilingPattern* tilingPattern = pattern->getTilingPattern(); - processTillingPatternPainting(tilingPattern, path); + processTillingPatternPainting(tilingPattern, path, patternColorSpace->getUncoloredPatternColorSpace(), patternColorSpace->getUncoloredPatternColor()); break; } @@ -653,9 +653,9 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool if (stroke) { - const PDFPattern* pattern = getGraphicState()->getStrokeColorSpace()->getPattern(); - if (pattern) + if (const PDFPatternColorSpace* patternColorSpace = getGraphicState()->getFillColorSpace()->asPatternColorSpace()) { + const PDFPattern* pattern = patternColorSpace->getPattern(); switch (pattern->getType()) { case PatternType::Tiling: @@ -676,7 +676,7 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool stroker.setDashOffset(lineDashPattern.getDashOffset()); } QPainterPath strokedPath = stroker.createStroke(path); - processTillingPatternPainting(tilingPattern, strokedPath); + processTillingPatternPainting(tilingPattern, strokedPath, patternColorSpace->getUncoloredPatternColorSpace(), patternColorSpace->getUncoloredPatternColor()); break; } @@ -745,7 +745,10 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool } } -void PDFPageContentProcessor::processTillingPatternPainting(const PDFTilingPattern* tilingPattern, const QPainterPath& path) +void PDFPageContentProcessor::processTillingPatternPainting(const PDFTilingPattern* tilingPattern, + const QPainterPath& path, + PDFColorSpacePointer uncoloredPatternColorSpace, + PDFColor uncoloredPatternColor) { PDFPageContentProcessorStateGuard guard(this); performClipping(path, path.fillRule()); @@ -764,6 +767,33 @@ void PDFPageContentProcessor::processTillingPatternPainting(const PDFTilingPatte QMatrix matrix = patternMatrix * m_pagePointToDevicePointMatrix.inverted(); QMatrix pathTransformationMatrix = m_graphicState.getCurrentTransformationMatrix() * matrix.inverted(); m_graphicState.setCurrentTransformationMatrix(matrix); + + // Initialize colors for uncolored color space pattern + if (tilingPattern->getPaintingType() == PDFTilingPattern::PaintType::Uncolored) + { + if (!uncoloredPatternColorSpace) + { + throw PDFRendererException(RenderErrorType::Error, PDFTranslationContext::tr("Uncolored tiling pattern has not underlying color space.")); + } + + m_graphicState.setStrokeColorSpace(uncoloredPatternColorSpace); + m_graphicState.setFillColorSpace(uncoloredPatternColorSpace); + + QColor color = uncoloredPatternColorSpace->getCheckedColor(uncoloredPatternColor); + m_graphicState.setStrokeColor(color); + m_graphicState.setFillColor(color); + } + else + { + // Jakub Melka: According the specification, we set default color space and default color + m_graphicState.setStrokeColorSpace(m_deviceGrayColorSpace); + m_graphicState.setFillColorSpace(m_deviceGrayColorSpace); + + QColor color = m_deviceGrayColorSpace->getDefaultColor(); + m_graphicState.setStrokeColor(color); + m_graphicState.setFillColor(color); + } + updateGraphicState(); // Tiling parameters @@ -1951,17 +1981,25 @@ void PDFPageContentProcessor::operatorColorSetStrokingColorN() // but default operator can use them (with exception of Pattern color space). For pattern color space, // we treat this differently. const PDFAbstractColorSpace* colorSpace = m_graphicState.getStrokeColorSpace(); - if (colorSpace->getPattern()) + if (const PDFPatternColorSpace* patternColorSpace = colorSpace->asPatternColorSpace()) { - if (m_operands.size() > 0) + const size_t operandCount = m_operands.size(); + if (operandCount > 0) { - // TODO: Implement tiling pattern colors - PDFOperandName name = readOperand(m_operands.size() - 1); + PDFColorSpacePointer uncoloredColorSpace = patternColorSpace->getUncoloredPatternColorSpace(); + PDFColor uncoloredPatternColor; + + for (size_t i = 0; i < operandCount - 1; ++i) + { + uncoloredPatternColor.push_back(readOperand(i)); + } + + PDFOperandName name = readOperand(operandCount - 1); if (m_patternDictionary && m_patternDictionary->hasKey(name.name)) { // Create the pattern PDFPatternPtr pattern = PDFPattern::createPattern(m_colorSpaceDictionary, m_document, m_patternDictionary->get(name.name)); - m_graphicState.setStrokeColorSpace(QSharedPointer(new PDFPatternColorSpace(qMove(pattern)))); + m_graphicState.setStrokeColorSpace(PDFColorSpacePointer(new PDFPatternColorSpace(qMove(pattern), qMove(uncoloredColorSpace), qMove(uncoloredPatternColor)))); updateGraphicState(); return; } @@ -2009,17 +2047,25 @@ void PDFPageContentProcessor::operatorColorSetFillingColorN() // but default operator can use them (with exception of Pattern color space). For pattern color space, // we treat this differently. const PDFAbstractColorSpace* colorSpace = m_graphicState.getFillColorSpace(); - if (colorSpace->getPattern()) + if (const PDFPatternColorSpace* patternColorSpace = colorSpace->asPatternColorSpace()) { - if (m_operands.size() > 0) + const size_t operandCount = m_operands.size(); + if (operandCount > 0) { - // TODO: Implement tiling pattern colors - PDFOperandName name = readOperand(m_operands.size() - 1); + PDFColorSpacePointer uncoloredColorSpace = patternColorSpace->getUncoloredPatternColorSpace(); + PDFColor uncoloredPatternColor; + + for (size_t i = 0; i < operandCount - 1; ++i) + { + uncoloredPatternColor.push_back(readOperand(i)); + } + + PDFOperandName name = readOperand(operandCount - 1); if (m_patternDictionary && m_patternDictionary->hasKey(name.name)) { // Create the pattern PDFPatternPtr pattern = PDFPattern::createPattern(m_colorSpaceDictionary, m_document, m_patternDictionary->get(name.name)); - m_graphicState.setFillColorSpace(QSharedPointer(new PDFPatternColorSpace(qMove(pattern)))); + m_graphicState.setFillColorSpace(QSharedPointer(new PDFPatternColorSpace(qMove(pattern), qMove(uncoloredColorSpace), qMove(uncoloredPatternColor)))); updateGraphicState(); return; } @@ -2359,7 +2405,7 @@ void PDFPageContentProcessor::operatorShadingPaintShape(PDFPageContentProcessor: // 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)))); + m_graphicState.setFillColorSpace(PDFColorSpacePointer(new PDFPatternColorSpace(qMove(pattern), nullptr, PDFColor()))); updateGraphicState(); Q_ASSERT(matrix.isInvertible()); diff --git a/PdfForQtLib/sources/pdfpagecontentprocessor.h b/PdfForQtLib/sources/pdfpagecontentprocessor.h index ce826cb..6986235 100644 --- a/PdfForQtLib/sources/pdfpagecontentprocessor.h +++ b/PdfForQtLib/sources/pdfpagecontentprocessor.h @@ -468,7 +468,12 @@ private: /// Performs tiling pattern painting /// \param tilingPattern Tiling pattern to be painted /// \param path Clipping path - void processTillingPatternPainting(const PDFTilingPattern* tilingPattern, const QPainterPath& path); + /// \param uncoloredPatternColorSpace Color space for uncolored color patterns + /// \param uncoloredPatternColor Uncolored color pattern color + void processTillingPatternPainting(const PDFTilingPattern* tilingPattern, + const QPainterPath& path, + PDFColorSpacePointer uncoloredPatternColorSpace, + PDFColor uncoloredPatternColor); enum class MarkedContentKind { diff --git a/PdfForQtLib/sources/pdfpainter.cpp b/PdfForQtLib/sources/pdfpainter.cpp index d19c644..08c280e 100644 --- a/PdfForQtLib/sources/pdfpainter.cpp +++ b/PdfForQtLib/sources/pdfpainter.cpp @@ -50,6 +50,8 @@ PDFPainter::PDFPainter(QPainter* painter, m_painter->setClipPath(path, Qt::IntersectClip); } } + + m_painter->setRenderHint(QPainter::SmoothPixmapTransform, features.testFlag(PDFRenderer::SmoothImages)); } PDFPainter::~PDFPainter() diff --git a/PdfForQtLib/sources/pdfpattern.cpp b/PdfForQtLib/sources/pdfpattern.cpp index 89b9948..0cbb4de 100644 --- a/PdfForQtLib/sources/pdfpattern.cpp +++ b/PdfForQtLib/sources/pdfpattern.cpp @@ -156,7 +156,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi // Parse common data for all shadings PDFColorSpacePointer colorSpace = PDFAbstractColorSpace::createColorSpace(colorSpaceDictionary, document, document->getObject(shadingDictionary->get("ColorSpace"))); - if (colorSpace->getPattern()) + if (colorSpace->asPatternColorSpace()) { throw PDFParserException(PDFTranslationContext::tr("Pattern color space is not valid for shading patterns.")); } diff --git a/PdfForQtLib/sources/pdfpattern.h b/PdfForQtLib/sources/pdfpattern.h index 2c2dac6..ddf0ae7 100644 --- a/PdfForQtLib/sources/pdfpattern.h +++ b/PdfForQtLib/sources/pdfpattern.h @@ -250,16 +250,16 @@ public: enum class PaintType { - Colored, - Uncolored, - Invalid + Colored = 1, + Uncolored = 2, + Invalid = 3 }; enum class TilingType { - ConstantSpacing, - NoDistortion, - ConstantSpacingAndFasterTiling, + ConstantSpacing = 1, + NoDistortion = 2, + ConstantSpacingAndFasterTiling = 3, Invalid };