diff --git a/Pdf4QtLib/sources/pdfpagecontentprocessor.cpp b/Pdf4QtLib/sources/pdfpagecontentprocessor.cpp index 343d69a..6da979b 100644 --- a/Pdf4QtLib/sources/pdfpagecontentprocessor.cpp +++ b/Pdf4QtLib/sources/pdfpagecontentprocessor.cpp @@ -359,6 +359,19 @@ void PDFPageContentProcessor::performPathPainting(const QPainterPath& path, bool Q_UNUSED(fillRule); } +bool PDFPageContentProcessor::performPathPaintingUsingShading(const QPainterPath& path, bool stroke, bool fill, const PDFShadingPattern* shadingPattern) +{ + Q_UNUSED(path); + Q_UNUSED(shadingPattern); + + return false; +} + +void PDFPageContentProcessor::performFinishPathPainting() +{ + +} + void PDFPageContentProcessor::performClipping(const QPainterPath& path, Qt::FillRule fillRule) { Q_UNUSED(path); @@ -820,21 +833,24 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix(); settings.initResolution(); - PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this); - - // Now, merge the current path to the mesh clipping path - QPainterPath boundingPath = mesh.getBoundingPath(); - if (boundingPath.isEmpty()) + if (!performPathPaintingUsingShading(path, false, true, shadingPattern)) { - boundingPath = getCurrentWorldMatrix().map(path); - } - else - { - boundingPath = boundingPath.intersected(path); - } - mesh.setBoundingPath(boundingPath); + PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this); - performMeshPainting(mesh); + // Now, merge the current path to the mesh clipping path + QPainterPath boundingPath = mesh.getBoundingPath(); + if (boundingPath.isEmpty()) + { + boundingPath = getCurrentWorldMatrix().map(path); + } + else + { + boundingPath = boundingPath.intersected(path); + } + mesh.setBoundingPath(boundingPath); + + performMeshPainting(mesh); + } } break; } @@ -917,8 +933,6 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool settings.userSpaceToDeviceSpaceMatrix = getPatternBaseMatrix(); settings.initResolution(); - PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this); - // We must stroke the path. QPainterPathStroker stroker; stroker.setCapStyle(m_graphicState.getLineCapStyle()); @@ -934,18 +948,23 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool } QPainterPath strokedPath = stroker.createStroke(path); - QPainterPath boundingPath = mesh.getBoundingPath(); - if (boundingPath.isEmpty()) + if (!performPathPaintingUsingShading(strokedPath, true, false, shadingPattern)) { - boundingPath = getCurrentWorldMatrix().map(strokedPath); - } - else - { - boundingPath = boundingPath.intersected(strokedPath); - } - mesh.setBoundingPath(boundingPath); + PDFMesh mesh = shadingPattern->createMesh(settings, m_CMS, m_graphicState.getRenderingIntent(), this); - performMeshPainting(mesh); + QPainterPath boundingPath = mesh.getBoundingPath(); + if (boundingPath.isEmpty()) + { + boundingPath = getCurrentWorldMatrix().map(strokedPath); + } + else + { + boundingPath = boundingPath.intersected(strokedPath); + } + mesh.setBoundingPath(boundingPath); + + performMeshPainting(mesh); + } } break; } @@ -971,6 +990,8 @@ void PDFPageContentProcessor::processPathPainting(const QPainterPath& path, bool { performPathPainting(path, stroke, fill, text, fillRule); } + + performFinishPathPainting(); } void PDFPageContentProcessor::processTillingPatternPainting(const PDFTilingPattern* tilingPattern, diff --git a/Pdf4QtLib/sources/pdfpagecontentprocessor.h b/Pdf4QtLib/sources/pdfpagecontentprocessor.h index 0f40cd6..6db08e6 100644 --- a/Pdf4QtLib/sources/pdfpagecontentprocessor.h +++ b/Pdf4QtLib/sources/pdfpagecontentprocessor.h @@ -41,6 +41,7 @@ class PDFCMS; class PDFMesh; class PDFImage; class PDFTilingPattern; +class PDFShadingPattern; class PDFOptionalContentActivity; static constexpr const char* PDF_RESOURCE_EXTGSTATE = "ExtGState"; @@ -505,6 +506,17 @@ protected: /// \param fillRule Fill rule used in the fill mode virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule); + /// This function is used, when we want to implement custom fill using shading. If path is successfully + /// filled by shading, then true should be returned. + /// \param path Path to be filled + /// \param stroke Is path actually stroked? + /// \param fill Is path actually filled? + /// \param shadingPattern Shading pattern + virtual bool performPathPaintingUsingShading(const QPainterPath& path, bool stroke, bool fill, const PDFShadingPattern* shadingPattern); + + /// This function is called after path paintig is finished + virtual void performFinishPathPainting(); + /// 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); diff --git a/Pdf4QtLib/sources/pdfpattern.cpp b/Pdf4QtLib/sources/pdfpattern.cpp index f53ff43..b48bb80 100644 --- a/Pdf4QtLib/sources/pdfpattern.cpp +++ b/Pdf4QtLib/sources/pdfpattern.cpp @@ -21,6 +21,7 @@ #include "pdfutils.h" #include "pdfcolorspaces.h" #include "pdfexecutionpolicy.h" +#include "pdfconstants.h" #include @@ -34,11 +35,23 @@ PatternType PDFShadingPattern::getType() const return PatternType::Shading; } +const PDFAbstractColorSpace* PDFShadingPattern::getColorSpace() const +{ + return m_colorSpace.data(); +} + QMatrix PDFShadingPattern::getPatternSpaceToDeviceSpaceMatrix(const PDFMeshQualitySettings& settings) const { return m_matrix * settings.userSpaceToDeviceSpaceMatrix; } +PDFShadingSampler* PDFShadingPattern::createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const +{ + Q_UNUSED(userSpaceToDeviceSpaceMatrix); + + return nullptr; +} + ShadingType PDFAxialShading::getShadingType() const { return ShadingType::Axial; @@ -171,6 +184,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi } QColor backgroundColor; + PDFColor originalBackgroundColor; if (!ignoreBackgroundColor) { std::vector backgroundColorValues = loader.readNumberArrayFromDictionary(shadingDictionary, "Background"); @@ -178,6 +192,12 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi { backgroundColor = colorSpace->getCheckedColor(PDFAbstractColorSpace::convertToColor(backgroundColorValues), cms, intent, reporter); } + + originalBackgroundColor.resize(backgroundColorValues.size()); + for (size_t i = 0; i < backgroundColorValues.size(); ++i) + { + originalBackgroundColor[i] = backgroundColorValues[i]; + } } QRectF boundingBox = loader.readRectangle(shadingDictionary->get("BBox"), QRectF()); bool antialias = loader.readBooleanFromDictionary(shadingDictionary, "AntiAlias", false); @@ -240,6 +260,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi // Load items for function shading functionShading->m_antiAlias = antialias; functionShading->m_backgroundColor = backgroundColor; + functionShading->m_originalBackgroundColor = qMove(originalBackgroundColor); functionShading->m_colorSpace = colorSpace; functionShading->m_boundingBox = boundingBox; functionShading->m_domain = QRectF(functionDomain[0], functionDomain[2], functionDomain[1] - functionDomain[0], functionDomain[3] - functionDomain[2]); @@ -281,6 +302,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi // Load items for axial shading axialShading->m_antiAlias = antialias; axialShading->m_backgroundColor = backgroundColor; + axialShading->m_originalBackgroundColor = qMove(originalBackgroundColor); axialShading->m_colorSpace = colorSpace; axialShading->m_boundingBox = boundingBox; axialShading->m_domainStart = domain[0]; @@ -331,6 +353,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi // Load items for axial shading radialShading->m_antiAlias = antialias; radialShading->m_backgroundColor = backgroundColor; + radialShading->m_originalBackgroundColor = qMove(originalBackgroundColor); radialShading->m_colorSpace = colorSpace; radialShading->m_boundingBox = boundingBox; radialShading->m_domainStart = domain[0]; @@ -422,6 +445,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi type4567Shading->m_antiAlias = antialias; type4567Shading->m_backgroundColor = backgroundColor; + type4567Shading->m_originalBackgroundColor = qMove(originalBackgroundColor); type4567Shading->m_colorSpace = colorSpace; type4567Shading->m_matrix = matrix; type4567Shading->m_patternGraphicState = patternGraphicState; @@ -1011,6 +1035,158 @@ PDFMesh PDFAxialShading::createMesh(const PDFMeshQualitySettings& settings, cons return mesh; } +class PDFAxialShadingSampler : public PDFShadingSampler +{ +public: + PDFAxialShadingSampler(const PDFAxialShading* axialShadingPattern, QMatrix userSpaceToDeviceSpaceMatrix) : + PDFShadingSampler(axialShadingPattern), + m_axialShadingPattern(axialShadingPattern), + m_xStart(0.0), + m_xEnd(0.0), + m_tAtStart(0.0), + m_tAtEnd(0.0), + m_tMin(0.0), + m_tMax(0.0) + { + QMatrix patternSpaceToDeviceSpace = axialShadingPattern->getMatrix() * userSpaceToDeviceSpaceMatrix; + + QPointF p1 = patternSpaceToDeviceSpace.map(axialShadingPattern->getStartPoint()); + QPointF p2 = patternSpaceToDeviceSpace.map(axialShadingPattern->getEndPoint()); + + // Strategy: for simplification, we rotate the line clockwise so we will + // get the shading axis equal to the x-axis. Then we will determine the shading + // area and create mesh according the settings. + QLineF line(p1, p2); + const double angle = line.angleTo(QLineF(0, 0, 1, 0)); + + // Matrix p1p2LCS is local coordinate system of line p1-p2. It transforms + // points on the line to the global coordinate system. So, point (0, 0) will + // map onto p1 and point (length(p1-p2), 0) will map onto p2. + QMatrix p1p2LCS; + p1p2LCS.translate(p1.x(), p1.y()); + p1p2LCS.rotate(angle); + QMatrix p1p2GCS = p1p2LCS.inverted(); + + QPointF p1m = p1p2GCS.map(p1); + QPointF p2m = p1p2GCS.map(p2); + + Q_ASSERT(isZero(p1m.y())); + Q_ASSERT(isZero(p2m.y())); + Q_ASSERT(p1m.x() <= p2m.x()); + + m_xStart = p1m.x(); + m_xEnd = p2m.x(); + + m_tAtStart = axialShadingPattern->getDomainStart(); + m_tAtEnd = axialShadingPattern->getDomainEnd(); + m_tMin = qMin(m_tAtStart, m_tAtEnd); + m_tMax = qMax(m_tAtStart, m_tAtEnd); + } + + virtual bool sample(const QPointF& devicePoint, PDFColorBuffer outputBuffer, int limit) const override + { + Q_UNUSED(limit); + + if (!m_pattern->getColorSpace() || m_pattern->getColorSpace()->getColorComponentCount() != outputBuffer.size()) + { + // Invalid color space, or invalid color buffer + return false; + } + + QPointF mappedPoint = m_p1p2GCS.map(devicePoint); + const PDFReal x = mappedPoint.x(); + + PDFReal t = m_tAtStart; + + if (x < m_xStart) + { + if (!m_axialShadingPattern->isExtendStart()) + { + return fillBackgroundColor(outputBuffer); + } + + t = m_tAtStart; + } + else if (x > m_xEnd) + { + if (!m_axialShadingPattern->isExtendEnd()) + { + return fillBackgroundColor(outputBuffer); + } + + t = m_tAtEnd; + } + else + { + t = interpolate(x, m_xStart, m_xEnd, m_tAtStart, m_tAtEnd); + t = qBound(m_tMin, t, m_tMax); + } + + const auto& functions = m_axialShadingPattern->getFunctions(); + std::array colorBuffer = { }; + + if (colorBuffer.size() < outputBuffer.size()) + { + // Jakub Melka: Too much colors - we cant process it + return false; + } + + if (functions.size() == 1) + { + Q_ASSERT(outputBuffer.size() <= colorBuffer.size()); + PDFFunction::FunctionResult result = functions.front()->apply(&t, &t + 1, colorBuffer.data(), colorBuffer.data() + outputBuffer.size()); + + if (!result) + { + // Function call failed + return false; + } + } + else + { + if (functions.size() != outputBuffer.size()) + { + // Invalid number of functions + return false; + } + + Q_ASSERT(outputBuffer.size() <= colorBuffer.size()); + for (size_t i = 0, count = outputBuffer.size(); i < count; ++i) + { + PDFFunction::FunctionResult result = functions[i]->apply(&t, &t + 1, colorBuffer.data() + i, colorBuffer.data() + i + 1); + + if (!result) + { + // Function call failed + return false; + } + } + } + + for (size_t i = 0, count = outputBuffer.size(); i < count; ++i) + { + outputBuffer[i] = colorBuffer[i]; + } + + return true; + } + +private: + const PDFAxialShading* m_axialShadingPattern; + QMatrix m_p1p2GCS; + PDFReal m_xStart; + PDFReal m_xEnd; + PDFReal m_tAtStart; + PDFReal m_tAtEnd; + PDFReal m_tMin; + PDFReal m_tMax; +}; + +PDFShadingSampler* PDFAxialShading::createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const +{ + return new PDFAxialShadingSampler(this, userSpaceToDeviceSpaceMatrix); +} + void PDFMesh::paint(QPainter* painter, PDFReal alpha) const { if (m_triangles.empty()) @@ -2649,4 +2825,21 @@ PDFMesh PDFCoonsPatchShading::createMesh(const PDFMeshQualitySettings& settings, return mesh; } +bool PDFShadingSampler::fillBackgroundColor(PDFColorBuffer outputBuffer) const +{ + const auto& originalBackgroundColor = m_pattern->getOriginalBackgroundColor(); + + if (originalBackgroundColor.size() == outputBuffer.size()) + { + for (size_t i = 0; i < outputBuffer.size(); ++i) + { + outputBuffer[i] = originalBackgroundColor[i]; + } + + return true; + } + + return false; +} + } // namespace pdf diff --git a/Pdf4QtLib/sources/pdfpattern.h b/Pdf4QtLib/sources/pdfpattern.h index 19ecc04..7d18c20 100644 --- a/Pdf4QtLib/sources/pdfpattern.h +++ b/Pdf4QtLib/sources/pdfpattern.h @@ -264,6 +264,34 @@ private: QByteArray m_content; }; +/// Compute color of sample points from shading pattern. Some sampler implementation +/// uses numerical algorithms (such as Newton-Raphson method for type 6/7 shading), so +/// calculation can be very slow for some types of shadings. +class PDFShadingSampler +{ +public: + explicit inline PDFShadingSampler(const PDFShadingPattern* pattern) : + m_pattern(pattern) + { + + } + virtual ~PDFShadingSampler() = default; + + /// Try to compute color of the point in device space coordinates in the shading. If color + /// can't be computed, then false is returned, otherwise true is returned. + /// \param devicePoint Point in device space coordinates + /// \param outputBuffer Color output buffer (where computed color is stored) + /// \param limit Maximal number of the steps of numerical calculation algorithms (for type 6/7 shading only) + virtual bool sample(const QPointF& devicePoint, PDFColorBuffer outputBuffer, int limit) const = 0; + + /// Fill background color to the output buffer. If the background color is not filled, + /// or is invalid, then false is returned, otherwise true is returned. + bool fillBackgroundColor(PDFColorBuffer outputBuffer) const; + +protected: + const PDFShadingPattern* m_pattern; +}; + /// Shading pattern - smooth color distribution along the pattern's space class PDFShadingPattern : public PDFPattern { @@ -295,18 +323,28 @@ public: /// If pattern has not background color, then invalid color is returned. const QColor& getBackgroundColor() const { return m_backgroundColor; } + /// Returns original background color (in color space of the shading pattern) + const PDFColor& getOriginalBackgroundColor() const { return m_originalBackgroundColor; } + /// Returns true, if shading pattern should be anti-aliased bool isAntialiasing() const { return m_antiAlias; } /// Returns matrix transforming pattern space to device space QMatrix getPatternSpaceToDeviceSpaceMatrix(const PDFMeshQualitySettings& settings) const; + /// Create sampler which can compute shading colors in device space coordinates. If sampler can't + /// be created (or shading is invalid), then nullptr is returned. + /// \param userSpaceToDeviceSpaceMatrix Matrix, which transforms user space points + /// (user space is target space of the shading) to the device space of the paint device. + virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const; + protected: friend class PDFPattern; PDFObject m_patternGraphicState; PDFColorSpacePointer m_colorSpace; QColor m_backgroundColor; + PDFColor m_originalBackgroundColor; bool m_antiAlias = false; }; @@ -315,6 +353,14 @@ class PDFSingleDimensionShading : public PDFShadingPattern public: explicit PDFSingleDimensionShading() = default; + const std::vector& getFunctions() const { return m_functions; } + const QPointF& getStartPoint() const { return m_startPoint; } + const QPointF& getEndPoint() const { return m_endPoint; } + PDFReal getDomainStart() const { return m_domainStart; } + PDFReal getDomainEnd() const { return m_domainEnd; } + bool isExtendStart() const { return m_extendStart; } + bool isExtendEnd() const { return m_extendEnd; } + protected: friend class PDFPattern; @@ -350,6 +396,7 @@ public: virtual ShadingType getShadingType() const override; virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings, const PDFCMS* cms, RenderingIntent intent, PDFRenderErrorReporter* reporter) const override; + virtual PDFShadingSampler* createSampler(QMatrix userSpaceToDeviceSpaceMatrix) const; private: friend class PDFPattern; diff --git a/Pdf4QtLib/sources/pdftransparencyrenderer.cpp b/Pdf4QtLib/sources/pdftransparencyrenderer.cpp index a2203ff..8e9ae9b 100644 --- a/Pdf4QtLib/sources/pdftransparencyrenderer.cpp +++ b/Pdf4QtLib/sources/pdftransparencyrenderer.cpp @@ -20,6 +20,7 @@ #include "pdfcms.h" #include "pdfexecutionpolicy.h" #include "pdfimage.h" +#include "pdfpattern.h" namespace pdf { @@ -1831,6 +1832,98 @@ void PDFTransparencyRenderer::performPathPainting(const QPainterPath& path, bool flushDrawBuffer(); } +bool PDFTransparencyRenderer::performPathPaintingUsingShading(const QPainterPath& path, bool stroke, bool fill, const PDFShadingPattern* shadingPattern) +{ + if (path.isEmpty()) + { + // Path is empty + return true; + } + + QMatrix worldMatrix = getCurrentWorldMatrix(); + QPainterPath worldPath = worldMatrix.map(path); + QRect fillRect = getActualFillRect(worldPath.controlPointRect()); + + if (fillRect.isEmpty()) + { + // Jakub Melka: nothing to draw, rectangle is empty + return true; + } + + std::unique_ptr sampler(shadingPattern->createSampler(getPatternBaseMatrix())); + if (!sampler) + { + // Can't create sampler - this is error + reportRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Cannot create shading sampler.")); + return true; + } + + // Now, we have a sampler, so we create a texture, which we will later use + // as color source. + const PDFAbstractColorSpace* colorSpace = shadingPattern->getColorSpace(); + const size_t shadingColorComponentCount = colorSpace->getColorComponentCount(); + PDFFloatBitmapWithColorSpace texture(fillRect.width() + 1, fillRect.height() + 1, PDFPixelFormat::createFormat(shadingColorComponentCount, 0, true, shadingColorComponentCount == 4, false), colorSpace); + QPointF offset = fillRect.topLeft(); + + const PDFPixelFormat texturePixelFormat = texture.getPixelFormat(); + const uint8_t textureShapeChannel = texturePixelFormat.getShapeChannelIndex(); + const uint8_t textureOpacityChannel = texturePixelFormat.getOpacityChannelIndex(); + + if (fillRect.width() > fillRect.height()) + { + // Columns + PDFIntegerRange range(fillRect.left(), fillRect.right() + 1); + auto processEntry = [&, this](int x) + { + for (int y = fillRect.top(); y <= fillRect.bottom(); ++y) + { + PDFColorBuffer buffer = texture.getPixel(x, y); + bool isSampled = sampler->sample(QPointF(x, y) + offset, buffer.resized(shadingColorComponentCount), m_settings.shadingAlgorithmLimit); + const PDFColorComponent textureSampleShape = isSampled ? 1.0f : 0.0f; + buffer[textureShapeChannel] = textureSampleShape; + buffer[textureOpacityChannel] = textureSampleShape; + } + }; + PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), processEntry); + } + else + { + // Rows + PDFIntegerRange range(fillRect.top(), fillRect.bottom() + 1); + auto processEntry = [&, this](int y) + { + for (int x = fillRect.left(); x <= fillRect.right(); ++x) + { + PDFColorBuffer buffer = texture.getPixel(x, y); + bool isSampled = sampler->sample(QPointF(x, y) + offset, buffer.resized(shadingColorComponentCount), m_settings.shadingAlgorithmLimit); + const PDFColorComponent textureSampleShape = isSampled ? 1.0f : 0.0f; + buffer[textureShapeChannel] = textureSampleShape; + buffer[textureOpacityChannel] = textureSampleShape; + } + }; + PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Content, range.begin(), range.end(), processEntry); + } + + PDFPainterPathSampler clipSampler(m_painterStateStack.top().clipPath, m_settings.samplesCount, 1.0f, fillRect, m_settings.flags.testFlag(PDFTransparencyRendererSettings::PrecisePathSampler)); + PDFPainterPathSampler pathSampler(worldPath, m_settings.samplesCount, 0.0f, fillRect, m_settings.flags.testFlag(PDFTransparencyRendererSettings::PrecisePathSampler)); + + for (int x = fillRect.left(); x <= fillRect.right(); ++x) + { + for (int y = fillRect.top(); y <= fillRect.bottom(); ++y) + { + /* performPixelSampling(shapeStroking, opacityStroking, shapeChannel, opacityChannel, colorChannelStart, colorChannelEnd, x, y, strokeColor, clipSampler, pathSampler);*/ + } + } + + m_drawBuffer.modify(fillRect, fill, stroke); + return true; +} + +void PDFTransparencyRenderer::performFinishPathPainting() +{ + flushDrawBuffer(); +} + void PDFTransparencyRenderer::performClipping(const QPainterPath& path, Qt::FillRule fillRule) { Q_UNUSED(fillRule); diff --git a/Pdf4QtLib/sources/pdftransparencyrenderer.h b/Pdf4QtLib/sources/pdftransparencyrenderer.h index 4fb3acd..d262c9c 100644 --- a/Pdf4QtLib/sources/pdftransparencyrenderer.h +++ b/Pdf4QtLib/sources/pdftransparencyrenderer.h @@ -510,6 +510,10 @@ struct PDFTransparencyRendererSettings /// multithreaded painting is performed. int multithreadingPathSampleThreshold = 128; + /// Maximal number of steps performed in numerical algorithm + /// used when some shadings are being sampled. + int shadingAlgorithmLimit = 64; + enum Flag { None = 0x0000, @@ -596,6 +600,8 @@ public: QImage toImage(bool use16Bit, bool usePaper = false, PDFRGB paperColor = PDFRGB()) const; virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule) override; + virtual bool performPathPaintingUsingShading(const QPainterPath& path, bool stroke, bool fill, const PDFShadingPattern* shadingPattern); + virtual void performFinishPathPainting(); virtual void performClipping(const QPainterPath& path, Qt::FillRule fillRule) override; virtual void performUpdateGraphicsState(const PDFPageContentProcessorState& state) override; virtual void performSaveGraphicState(ProcessOrder order) override; diff --git a/Pdf4QtLib/sources/pdfutils.h b/Pdf4QtLib/sources/pdfutils.h index 3432d62..2a6f025 100644 --- a/Pdf4QtLib/sources/pdfutils.h +++ b/Pdf4QtLib/sources/pdfutils.h @@ -551,6 +551,12 @@ public: size_t size() const { return m_end - m_begin; } + PDFBuffer resized(size_t newSize) const + { + Q_ASSERT(newSize <= size()); + return PDFBuffer(m_begin, newSize); + } + private: value_ptr m_begin; value_ptr m_end; diff --git a/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewdialog.cpp b/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewdialog.cpp index 45e2962..8e36df0 100644 --- a/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewdialog.cpp +++ b/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewdialog.cpp @@ -282,7 +282,7 @@ OutputPreviewDialog::RenderedImage OutputPreviewDialog::renderPage(const pdf::PD &m_inkMapperForRendering, settings, pagePointToDevicePoint); renderer.beginPaint(imageSize); - renderer.processContents(); + result.errors = renderer.processContents(); renderer.endPaint(); QImage image = renderer.toImage(false, true, paperColor); diff --git a/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewdialog.h b/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewdialog.h index 1ac1067..d51c7cb 100644 --- a/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewdialog.h +++ b/Pdf4QtViewerPlugins/OutputPreviewPlugin/outputpreviewdialog.h @@ -62,6 +62,7 @@ private: struct RenderedImage { QImage image; + QList errors; }; void updatePageImage();