mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-11 01:00:35 +01:00
Precompiled pages
This commit is contained in:
parent
1b7fba2f78
commit
0277a9f059
@ -19,6 +19,7 @@
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfdrawwidget.h"
|
||||
#include "pdfrenderer.h"
|
||||
#include "pdfpainter.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
@ -599,9 +600,27 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
|
||||
// Clear the page space by white color
|
||||
painter->fillRect(placedRect, Qt::white);
|
||||
|
||||
/*
|
||||
PDFRenderer renderer(m_controller->getDocument(), m_controller->getFontCache(), m_controller->getOptionalContentActivity(), m_features, m_meshQualitySettings);
|
||||
QList<PDFRenderError> errors = renderer.render(painter, placedRect, item.pageIndex);
|
||||
|
||||
if (!errors.empty())
|
||||
{
|
||||
emit renderingError(item.pageIndex, errors);
|
||||
}*/
|
||||
|
||||
PDFPrecompiledPage compiledPage;
|
||||
PDFRenderer renderer(m_controller->getDocument(), m_controller->getFontCache(), m_controller->getOptionalContentActivity(), m_features, m_meshQualitySettings);
|
||||
renderer.compile(&compiledPage, item.pageIndex);
|
||||
|
||||
if (compiledPage.isValid())
|
||||
{
|
||||
const PDFPage* page = m_controller->getDocument()->getCatalog()->getPage(item.pageIndex);
|
||||
QMatrix matrix = renderer.createPagePointToDevicePointMatrix(page, placedRect);
|
||||
compiledPage.draw(painter, page->getCropBox(), matrix, m_features);
|
||||
}
|
||||
|
||||
const QList<PDFRenderError>& errors = compiledPage.getErrors();
|
||||
if (!errors.empty())
|
||||
{
|
||||
emit renderingError(item.pageIndex, errors);
|
||||
|
@ -23,6 +23,210 @@
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFPainterBase::PDFPainterBase(PDFRenderer::Features features,
|
||||
const PDFPage* page,
|
||||
const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
QMatrix pagePointToDevicePointMatrix,
|
||||
const PDFMeshQualitySettings& meshQualitySettings) :
|
||||
BaseClass(page, document, fontCache, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings),
|
||||
m_features(features)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PDFPainterBase::performUpdateGraphicsState(const PDFPageContentProcessorState& state)
|
||||
{
|
||||
const PDFPageContentProcessorState::StateFlags flags = state.getStateFlags();
|
||||
|
||||
// If current transformation matrix has changed, then update it
|
||||
if (flags.testFlag(PDFPageContentProcessorState::StateCurrentTransformationMatrix))
|
||||
{
|
||||
setWorldMatrix(getCurrentWorldMatrix());
|
||||
}
|
||||
|
||||
if (flags.testFlag(PDFPageContentProcessorState::StateStrokeColor) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateLineWidth) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateLineCapStyle) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateLineJoinStyle) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateMitterLimit) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateLineDashPattern) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateAlphaStroking))
|
||||
{
|
||||
m_currentPen.dirty();
|
||||
}
|
||||
|
||||
if (flags.testFlag(PDFPageContentProcessorState::StateFillColor) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateAlphaFilling))
|
||||
{
|
||||
m_currentBrush.dirty();
|
||||
}
|
||||
|
||||
// If current blend mode has changed, then update it
|
||||
if (flags.testFlag(PDFPageContentProcessorState::StateBlendMode))
|
||||
{
|
||||
// Try to simulate transparency groups. Use only first composition mode,
|
||||
// outside the transparency groups (so we are on pages main transparency
|
||||
// groups).
|
||||
|
||||
const BlendMode blendMode = state.getBlendMode();
|
||||
if (canSetBlendMode(blendMode))
|
||||
{
|
||||
if (!PDFBlendModeInfo::isSupportedByQt(blendMode))
|
||||
{
|
||||
reportRenderErrorOnce(RenderErrorType::NotSupported, PDFTranslationContext::tr("Blend mode '%1' not supported.").arg(PDFBlendModeInfo::getBlendModeName(blendMode)));
|
||||
}
|
||||
|
||||
const QPainter::CompositionMode compositionMode = PDFBlendModeInfo::getCompositionModeFromBlendMode(blendMode);
|
||||
setCompositionMode(compositionMode);
|
||||
}
|
||||
else if (blendMode != BlendMode::Normal && blendMode != BlendMode::Compatible)
|
||||
{
|
||||
reportRenderErrorOnce(RenderErrorType::NotSupported, PDFTranslationContext::tr("Blend mode '%1' is in transparency group, which is not supported.").arg(PDFBlendModeInfo::getBlendModeName(blendMode)));
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::performUpdateGraphicsState(state);
|
||||
}
|
||||
|
||||
bool PDFPainterBase::isContentSuppressedByOC(PDFObjectReference ocgOrOcmd)
|
||||
{
|
||||
if (m_features.testFlag(PDFRenderer::IgnoreOptionalContent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return PDFPageContentProcessor::isContentSuppressedByOC(ocgOrOcmd);
|
||||
}
|
||||
|
||||
QPen PDFPainterBase::getCurrentPenImpl() const
|
||||
{
|
||||
const PDFPageContentProcessorState* graphicState = getGraphicState();
|
||||
QColor color = graphicState->getStrokeColor();
|
||||
if (color.isValid())
|
||||
{
|
||||
color.setAlphaF(getEffectiveStrokingAlpha());
|
||||
const PDFReal lineWidth = graphicState->getLineWidth();
|
||||
Qt::PenCapStyle penCapStyle = graphicState->getLineCapStyle();
|
||||
Qt::PenJoinStyle penJoinStyle = graphicState->getLineJoinStyle();
|
||||
const PDFLineDashPattern& lineDashPattern = graphicState->getLineDashPattern();
|
||||
const PDFReal mitterLimit = graphicState->getMitterLimit();
|
||||
|
||||
QPen pen(color);
|
||||
|
||||
pen.setWidthF(lineWidth);
|
||||
pen.setCapStyle(penCapStyle);
|
||||
pen.setJoinStyle(penJoinStyle);
|
||||
pen.setMiterLimit(mitterLimit);
|
||||
|
||||
if (lineDashPattern.isSolid())
|
||||
{
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
pen.setStyle(Qt::CustomDashLine);
|
||||
pen.setDashPattern(QVector<PDFReal>::fromStdVector(lineDashPattern.getDashArray()));
|
||||
pen.setDashOffset(lineDashPattern.getDashOffset());
|
||||
}
|
||||
|
||||
return pen;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QPen(Qt::NoPen);
|
||||
}
|
||||
}
|
||||
|
||||
QBrush PDFPainterBase::getCurrentBrushImpl() const
|
||||
{
|
||||
const PDFPageContentProcessorState* graphicState = getGraphicState();
|
||||
QColor color = graphicState->getFillColor();
|
||||
if (color.isValid())
|
||||
{
|
||||
color.setAlphaF(getEffectiveFillingAlpha());
|
||||
return QBrush(color, Qt::SolidPattern);
|
||||
}
|
||||
else
|
||||
{
|
||||
return QBrush(Qt::NoBrush);
|
||||
}
|
||||
}
|
||||
|
||||
PDFReal PDFPainterBase::getEffectiveStrokingAlpha() const
|
||||
{
|
||||
PDFReal alpha = getGraphicState()->getAlphaStroking();
|
||||
|
||||
auto it = m_transparencyGroupDataStack.crbegin();
|
||||
auto itEnd = m_transparencyGroupDataStack.crend();
|
||||
for (; it != itEnd; ++it)
|
||||
{
|
||||
const PDFTransparencyGroupPainterData& transparencyGroup = *it;
|
||||
alpha *= transparencyGroup.alphaStroke;
|
||||
|
||||
if (transparencyGroup.group.isolated)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
PDFReal PDFPainterBase::getEffectiveFillingAlpha() const
|
||||
{
|
||||
PDFReal alpha = getGraphicState()->getAlphaFilling();
|
||||
|
||||
auto it = m_transparencyGroupDataStack.crbegin();
|
||||
auto itEnd = m_transparencyGroupDataStack.crend();
|
||||
for (; it != itEnd; ++it)
|
||||
{
|
||||
const PDFTransparencyGroupPainterData& transparencyGroup = *it;
|
||||
alpha *= transparencyGroup.alphaFill;
|
||||
|
||||
if (transparencyGroup.group.isolated)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
bool PDFPainterBase::canSetBlendMode(BlendMode mode) const
|
||||
{
|
||||
// We will assume, that we can set blend mode, when
|
||||
// all other blend modes on transparency stack are normal,
|
||||
// or compatible. It should work.
|
||||
|
||||
Q_UNUSED(mode);
|
||||
return std::all_of(m_transparencyGroupDataStack.cbegin(), m_transparencyGroupDataStack.cend(), [](const PDFTransparencyGroupPainterData& group) { return group.blendMode == BlendMode::Normal || group.blendMode == BlendMode::Compatible; });
|
||||
}
|
||||
|
||||
void PDFPainterBase::performBeginTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup)
|
||||
{
|
||||
if (order == ProcessOrder::BeforeOperation)
|
||||
{
|
||||
PDFTransparencyGroupPainterData data;
|
||||
data.group = transparencyGroup;
|
||||
data.alphaFill = getGraphicState()->getAlphaFilling();
|
||||
data.alphaStroke = getGraphicState()->getAlphaStroking();
|
||||
data.blendMode = getGraphicState()->getBlendMode();
|
||||
m_transparencyGroupDataStack.emplace_back(qMove(data));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPainterBase::performEndTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup)
|
||||
{
|
||||
Q_UNUSED(transparencyGroup);
|
||||
|
||||
if (order == ProcessOrder::AfterOperation)
|
||||
{
|
||||
m_transparencyGroupDataStack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
PDFPainter::PDFPainter(QPainter* painter,
|
||||
PDFRenderer::Features features,
|
||||
QMatrix pagePointToDevicePointMatrix,
|
||||
@ -31,9 +235,8 @@ PDFPainter::PDFPainter(QPainter* painter,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
const PDFMeshQualitySettings& meshQualitySettings) :
|
||||
PDFPageContentProcessor(page, document, fontCache, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings),
|
||||
m_painter(painter),
|
||||
m_features(features)
|
||||
BaseClass(features, page, document, fontCache, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings),
|
||||
m_painter(painter)
|
||||
{
|
||||
Q_ASSERT(painter);
|
||||
Q_ASSERT(pagePointToDevicePointMatrix.isInvertible());
|
||||
@ -65,7 +268,7 @@ void PDFPainter::performPathPainting(const QPainterPath& path, bool stroke, bool
|
||||
Q_ASSERT(stroke || fill);
|
||||
|
||||
// Set antialiasing
|
||||
const bool antialiasing = (text && m_features.testFlag(PDFRenderer::TextAntialiasing)) || (!text && m_features.testFlag(PDFRenderer::Antialiasing));
|
||||
const bool antialiasing = (text && hasFeature(PDFRenderer::TextAntialiasing)) || (!text && hasFeature(PDFRenderer::Antialiasing));
|
||||
m_painter->setRenderHint(QPainter::Antialiasing, antialiasing);
|
||||
|
||||
if (stroke)
|
||||
@ -108,7 +311,7 @@ void PDFPainter::performImagePainting(const QImage& image)
|
||||
|
||||
QImage adjustedImage = image;
|
||||
|
||||
if (m_features.testFlag(PDFRenderer::SmoothImages))
|
||||
if (hasFeature(PDFRenderer::SmoothImages))
|
||||
{
|
||||
// Test, if we can use smooth images. We can use them under following conditions:
|
||||
// 1) Transformed rectangle is not skewed or deformed (so vectors (0, 1) and (1, 0) are orthogonal)
|
||||
@ -158,61 +361,6 @@ void PDFPainter::performMeshPainting(const PDFMesh& mesh)
|
||||
m_painter->restore();
|
||||
}
|
||||
|
||||
void PDFPainter::performUpdateGraphicsState(const PDFPageContentProcessorState& state)
|
||||
{
|
||||
const PDFPageContentProcessorState::StateFlags flags = state.getStateFlags();
|
||||
|
||||
// If current transformation matrix has changed, then update it
|
||||
if (flags.testFlag(PDFPageContentProcessorState::StateCurrentTransformationMatrix))
|
||||
{
|
||||
m_painter->setWorldMatrix(getCurrentWorldMatrix(), false);
|
||||
}
|
||||
|
||||
if (flags.testFlag(PDFPageContentProcessorState::StateStrokeColor) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateLineWidth) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateLineCapStyle) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateLineJoinStyle) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateMitterLimit) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateLineDashPattern) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateAlphaStroking))
|
||||
{
|
||||
m_currentPen.dirty();
|
||||
}
|
||||
|
||||
if (flags.testFlag(PDFPageContentProcessorState::StateFillColor) ||
|
||||
flags.testFlag(PDFPageContentProcessorState::StateAlphaFilling))
|
||||
{
|
||||
m_currentBrush.dirty();
|
||||
}
|
||||
|
||||
// If current blend mode has changed, then update it
|
||||
if (flags.testFlag(PDFPageContentProcessorState::StateBlendMode))
|
||||
{
|
||||
// Try to simulate transparency groups. Use only first composition mode,
|
||||
// outside the transparency groups (so we are on pages main transparency
|
||||
// groups).
|
||||
|
||||
const BlendMode blendMode = state.getBlendMode();
|
||||
if (canSetBlendMode(blendMode))
|
||||
{
|
||||
const QPainter::CompositionMode compositionMode = PDFBlendModeInfo::getCompositionModeFromBlendMode(blendMode);
|
||||
|
||||
if (!PDFBlendModeInfo::isSupportedByQt(blendMode))
|
||||
{
|
||||
reportRenderErrorOnce(RenderErrorType::NotSupported, PDFTranslationContext::tr("Blend mode '%1' not supported.").arg(PDFBlendModeInfo::getBlendModeName(blendMode)));
|
||||
}
|
||||
|
||||
m_painter->setCompositionMode(compositionMode);
|
||||
}
|
||||
else if (blendMode != BlendMode::Normal && blendMode != BlendMode::Compatible)
|
||||
{
|
||||
reportRenderErrorOnce(RenderErrorType::NotSupported, PDFTranslationContext::tr("Blend mode '%1' is in transparency group, which is not supported.").arg(PDFBlendModeInfo::getBlendModeName(blendMode)));
|
||||
}
|
||||
}
|
||||
|
||||
PDFPageContentProcessor::performUpdateGraphicsState(state);
|
||||
}
|
||||
|
||||
void PDFPainter::performSaveGraphicState(ProcessOrder order)
|
||||
{
|
||||
if (order == ProcessOrder::AfterOperation)
|
||||
@ -229,141 +377,242 @@ void PDFPainter::performRestoreGraphicState(ProcessOrder order)
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPainter::performBeginTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup)
|
||||
void PDFPainter::setWorldMatrix(const QMatrix& matrix)
|
||||
{
|
||||
m_painter->setWorldMatrix(matrix, false);
|
||||
}
|
||||
|
||||
void PDFPainter::setCompositionMode(QPainter::CompositionMode mode)
|
||||
{
|
||||
m_painter->setCompositionMode(mode);
|
||||
}
|
||||
|
||||
void PDFPrecompiledPageGenerator::performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule)
|
||||
{
|
||||
Q_ASSERT(stroke || fill);
|
||||
Q_ASSERT(path.fillRule() == fillRule);
|
||||
|
||||
QPen pen = stroke ? getCurrentPen() : QPen(Qt::NoPen);
|
||||
QBrush brush = fill ? getCurrentBrush() : QBrush(Qt::NoBrush);
|
||||
m_precompiledPage->addPath(qMove(pen), qMove(brush), path, text);
|
||||
}
|
||||
|
||||
void PDFPrecompiledPageGenerator::performClipping(const QPainterPath& path, Qt::FillRule fillRule)
|
||||
{
|
||||
Q_ASSERT(path.fillRule() == fillRule);
|
||||
m_precompiledPage->addClip(path);
|
||||
}
|
||||
|
||||
void PDFPrecompiledPageGenerator::performImagePainting(const QImage& image)
|
||||
{
|
||||
if (isContentSuppressed())
|
||||
{
|
||||
// Content is suppressed, do not paint anything
|
||||
return;
|
||||
}
|
||||
|
||||
m_precompiledPage->addImage(image);
|
||||
}
|
||||
|
||||
void PDFPrecompiledPageGenerator::performMeshPainting(const PDFMesh& mesh)
|
||||
{
|
||||
m_precompiledPage->addMesh(mesh, getEffectiveFillingAlpha());
|
||||
}
|
||||
|
||||
void PDFPrecompiledPageGenerator::performSaveGraphicState(PDFPageContentProcessor::ProcessOrder order)
|
||||
{
|
||||
if (order == ProcessOrder::AfterOperation)
|
||||
{
|
||||
m_precompiledPage->addSaveGraphicState();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPrecompiledPageGenerator::performRestoreGraphicState(PDFPageContentProcessor::ProcessOrder order)
|
||||
{
|
||||
if (order == ProcessOrder::BeforeOperation)
|
||||
{
|
||||
PDFTransparencyGroupPainterData data;
|
||||
data.group = transparencyGroup;
|
||||
data.alphaFill = getGraphicState()->getAlphaFilling();
|
||||
data.alphaStroke = getGraphicState()->getAlphaStroking();
|
||||
data.blendMode = getGraphicState()->getBlendMode();
|
||||
m_transparencyGroupDataStack.emplace_back(qMove(data));
|
||||
m_precompiledPage->addRestoreGraphicState();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPainter::performEndTransparencyGroup(PDFPageContentProcessor::ProcessOrder order, const PDFPageContentProcessor::PDFTransparencyGroup& transparencyGroup)
|
||||
void PDFPrecompiledPageGenerator::setWorldMatrix(const QMatrix& matrix)
|
||||
{
|
||||
Q_UNUSED(transparencyGroup);
|
||||
|
||||
if (order == ProcessOrder::AfterOperation)
|
||||
{
|
||||
m_transparencyGroupDataStack.pop_back();
|
||||
}
|
||||
m_precompiledPage->addSetWorldMatrix(matrix);
|
||||
}
|
||||
|
||||
bool PDFPainter::isContentSuppressedByOC(PDFObjectReference ocgOrOcmd)
|
||||
void PDFPrecompiledPageGenerator::setCompositionMode(QPainter::CompositionMode mode)
|
||||
{
|
||||
if (m_features.testFlag(PDFRenderer::IgnoreOptionalContent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return PDFPageContentProcessor::isContentSuppressedByOC(ocgOrOcmd);
|
||||
m_precompiledPage->addSetCompositionMode(mode);
|
||||
}
|
||||
|
||||
QPen PDFPainter::getCurrentPenImpl() const
|
||||
void PDFPrecompiledPage::draw(QPainter* painter, const QRectF& cropBox, const QMatrix& pagePointToDevicePointMatrix, PDFRenderer::Features features) const
|
||||
{
|
||||
const PDFPageContentProcessorState* graphicState = getGraphicState();
|
||||
QColor color = graphicState->getStrokeColor();
|
||||
if (color.isValid())
|
||||
Q_ASSERT(painter);
|
||||
Q_ASSERT(pagePointToDevicePointMatrix.isInvertible());
|
||||
|
||||
painter->save();
|
||||
|
||||
if (features.testFlag(PDFRenderer::ClipToCropBox))
|
||||
{
|
||||
color.setAlphaF(getEffectiveStrokingAlpha());
|
||||
const PDFReal lineWidth = graphicState->getLineWidth();
|
||||
Qt::PenCapStyle penCapStyle = graphicState->getLineCapStyle();
|
||||
Qt::PenJoinStyle penJoinStyle = graphicState->getLineJoinStyle();
|
||||
const PDFLineDashPattern& lineDashPattern = graphicState->getLineDashPattern();
|
||||
const PDFReal mitterLimit = graphicState->getMitterLimit();
|
||||
|
||||
QPen pen(color);
|
||||
|
||||
pen.setWidthF(lineWidth);
|
||||
pen.setCapStyle(penCapStyle);
|
||||
pen.setJoinStyle(penJoinStyle);
|
||||
pen.setMiterLimit(mitterLimit);
|
||||
|
||||
if (lineDashPattern.isSolid())
|
||||
if (cropBox.isValid())
|
||||
{
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
pen.setStyle(Qt::CustomDashLine);
|
||||
pen.setDashPattern(QVector<PDFReal>::fromStdVector(lineDashPattern.getDashArray()));
|
||||
pen.setDashOffset(lineDashPattern.getDashOffset());
|
||||
}
|
||||
|
||||
return pen;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QPen(Qt::NoPen);
|
||||
}
|
||||
}
|
||||
|
||||
QBrush PDFPainter::getCurrentBrushImpl() const
|
||||
{
|
||||
const PDFPageContentProcessorState* graphicState = getGraphicState();
|
||||
QColor color = graphicState->getFillColor();
|
||||
if (color.isValid())
|
||||
{
|
||||
color.setAlphaF(getEffectiveFillingAlpha());
|
||||
return QBrush(color, Qt::SolidPattern);
|
||||
}
|
||||
else
|
||||
{
|
||||
return QBrush(Qt::NoBrush);
|
||||
}
|
||||
}
|
||||
|
||||
PDFReal PDFPainter::getEffectiveStrokingAlpha() const
|
||||
{
|
||||
PDFReal alpha = getGraphicState()->getAlphaStroking();
|
||||
|
||||
auto it = m_transparencyGroupDataStack.crbegin();
|
||||
auto itEnd = m_transparencyGroupDataStack.crend();
|
||||
for (; it != itEnd; ++it)
|
||||
{
|
||||
const PDFTransparencyGroupPainterData& transparencyGroup = *it;
|
||||
alpha *= transparencyGroup.alphaStroke;
|
||||
|
||||
if (transparencyGroup.group.isolated)
|
||||
{
|
||||
break;
|
||||
QPainterPath path;
|
||||
path.addPolygon(pagePointToDevicePointMatrix.map(cropBox));
|
||||
painter->setClipPath(path, Qt::IntersectClip);
|
||||
}
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
painter->setRenderHint(QPainter::SmoothPixmapTransform, features.testFlag(PDFRenderer::SmoothImages));
|
||||
|
||||
PDFReal PDFPainter::getEffectiveFillingAlpha() const
|
||||
{
|
||||
PDFReal alpha = getGraphicState()->getAlphaFilling();
|
||||
|
||||
auto it = m_transparencyGroupDataStack.crbegin();
|
||||
auto itEnd = m_transparencyGroupDataStack.crend();
|
||||
for (; it != itEnd; ++it)
|
||||
// Process all instructions
|
||||
for (const Instruction& instruction : m_instructions)
|
||||
{
|
||||
const PDFTransparencyGroupPainterData& transparencyGroup = *it;
|
||||
alpha *= transparencyGroup.alphaFill;
|
||||
|
||||
if (transparencyGroup.group.isolated)
|
||||
switch (instruction.type)
|
||||
{
|
||||
break;
|
||||
case InstructionType::DrawPath:
|
||||
{
|
||||
const PathPaintData& data = m_paths[instruction.dataIndex];
|
||||
|
||||
// Set antialiasing
|
||||
const bool antialiasing = (data.isText && features.testFlag(PDFRenderer::TextAntialiasing)) || (!data.isText && features.testFlag(PDFRenderer::Antialiasing));
|
||||
painter->setRenderHint(QPainter::Antialiasing, antialiasing);
|
||||
painter->setPen(data.pen);
|
||||
painter->setBrush(data.brush);
|
||||
painter->drawPath(data.path);
|
||||
break;
|
||||
}
|
||||
|
||||
case InstructionType::DrawImage:
|
||||
{
|
||||
const ImageData& data = m_images[instruction.dataIndex];
|
||||
const QImage& image = data.image;
|
||||
|
||||
painter->save();
|
||||
|
||||
QMatrix imageTransform(1.0 / image.width(), 0, 0, 1.0 / image.height(), 0, 0);
|
||||
QMatrix worldMatrix = imageTransform * painter->worldMatrix();
|
||||
|
||||
// Jakub Melka: Because Qt uses opposite axis direction than PDF, then we must transform the y-axis
|
||||
// to the opposite (so the image is then unchanged)
|
||||
worldMatrix.translate(0, image.height());
|
||||
worldMatrix.scale(1, -1);
|
||||
|
||||
painter->setWorldMatrix(worldMatrix);
|
||||
painter->drawImage(0, 0, image);
|
||||
painter->restore();
|
||||
break;
|
||||
}
|
||||
|
||||
case InstructionType::DrawMesh:
|
||||
{
|
||||
const MeshPaintData& data = m_meshes[instruction.dataIndex];
|
||||
|
||||
painter->save();
|
||||
painter->setWorldMatrix(pagePointToDevicePointMatrix);
|
||||
data.mesh.paint(painter, data.alpha);
|
||||
painter->restore();
|
||||
break;
|
||||
}
|
||||
|
||||
case InstructionType::Clip:
|
||||
{
|
||||
painter->setClipPath(m_clips[instruction.dataIndex].clipPath, Qt::IntersectClip);
|
||||
break;
|
||||
}
|
||||
|
||||
case InstructionType::SaveGraphicState:
|
||||
{
|
||||
painter->save();
|
||||
break;
|
||||
}
|
||||
|
||||
case InstructionType::RestoreGraphicState:
|
||||
{
|
||||
painter->restore();
|
||||
break;
|
||||
}
|
||||
|
||||
case InstructionType::SetWorldMatrix:
|
||||
{
|
||||
painter->setWorldMatrix(m_matrices[instruction.dataIndex] * pagePointToDevicePointMatrix);
|
||||
break;
|
||||
}
|
||||
|
||||
case InstructionType::SetCompositionMode:
|
||||
{
|
||||
painter->setCompositionMode(m_compositionModes[instruction.dataIndex]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return alpha;
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
bool PDFPainter::canSetBlendMode(BlendMode mode) const
|
||||
void PDFPrecompiledPage::addPath(QPen pen, QBrush brush, QPainterPath path, bool isText)
|
||||
{
|
||||
// We will assume, that we can set blend mode, when
|
||||
// all other blend modes on transparency stack are normal,
|
||||
// or compatible. It should work.
|
||||
m_instructions.emplace_back(InstructionType::DrawPath, m_paths.size());
|
||||
m_paths.emplace_back(qMove(pen), qMove(brush), qMove(path), isText);
|
||||
}
|
||||
|
||||
Q_UNUSED(mode);
|
||||
return std::all_of(m_transparencyGroupDataStack.cbegin(), m_transparencyGroupDataStack.cend(), [](const PDFTransparencyGroupPainterData& group) { return group.blendMode == BlendMode::Normal || group.blendMode == BlendMode::Compatible; });
|
||||
void PDFPrecompiledPage::addClip(QPainterPath path)
|
||||
{
|
||||
m_instructions.emplace_back(InstructionType::Clip, m_clips.size());
|
||||
m_clips.emplace_back(qMove(path));
|
||||
}
|
||||
|
||||
void PDFPrecompiledPage::addImage(QImage image)
|
||||
{
|
||||
// Convert the image into format Format_ARGB32_Premultiplied for fast drawing.
|
||||
// If this format is used, then no image conversion is performed while drawing.
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied)
|
||||
{
|
||||
image.convertTo(QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
|
||||
m_instructions.emplace_back(InstructionType::DrawImage, m_images.size());
|
||||
m_images.emplace_back(qMove(image));
|
||||
}
|
||||
|
||||
void PDFPrecompiledPage::addMesh(PDFMesh mesh, PDFReal alpha)
|
||||
{
|
||||
m_instructions.emplace_back(InstructionType::DrawMesh, m_meshes.size());
|
||||
m_meshes.emplace_back(qMove(mesh), alpha);
|
||||
}
|
||||
|
||||
void PDFPrecompiledPage::addSetWorldMatrix(const QMatrix& matrix)
|
||||
{
|
||||
m_instructions.emplace_back(InstructionType::SetWorldMatrix, m_matrices.size());
|
||||
m_matrices.push_back(matrix);
|
||||
}
|
||||
|
||||
void PDFPrecompiledPage::addSetCompositionMode(QPainter::CompositionMode compositionMode)
|
||||
{
|
||||
m_instructions.emplace_back(InstructionType::SetCompositionMode, m_compositionModes.size());
|
||||
m_compositionModes.push_back(compositionMode);
|
||||
}
|
||||
|
||||
void PDFPrecompiledPage::optimize()
|
||||
{
|
||||
m_instructions.shrink_to_fit();
|
||||
m_paths.shrink_to_fit();
|
||||
m_clips.shrink_to_fit();
|
||||
m_images.shrink_to_fit();
|
||||
m_meshes.shrink_to_fit();
|
||||
m_matrices.shrink_to_fit();
|
||||
m_compositionModes.shrink_to_fit();
|
||||
}
|
||||
|
||||
void PDFPrecompiledPage::finalize(qint64 compilingTimeNS, QList<PDFRenderError> errors)
|
||||
{
|
||||
m_compilingTimeNS = compilingTimeNS;
|
||||
m_errors = qMove(errors);
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define PDFPAINTER_H
|
||||
|
||||
#include "pdfutils.h"
|
||||
#include "pdfpattern.h"
|
||||
#include "pdfrenderer.h"
|
||||
#include "pdfpagecontentprocessor.h"
|
||||
|
||||
@ -28,12 +29,76 @@
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
/// Base painter, encapsulating common functionality for all PDF painters (for example,
|
||||
/// direct painter, or painter, which generates list of graphic commands).
|
||||
class PDFPainterBase : public PDFPageContentProcessor
|
||||
{
|
||||
using BaseClass = PDFPageContentProcessor;
|
||||
|
||||
public:
|
||||
explicit PDFPainterBase(PDFRenderer::Features features,
|
||||
const PDFPage* page,
|
||||
const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
QMatrix pagePointToDevicePointMatrix,
|
||||
const PDFMeshQualitySettings& meshQualitySettings);
|
||||
|
||||
protected:
|
||||
virtual void performUpdateGraphicsState(const PDFPageContentProcessorState& state) override;
|
||||
virtual void performBeginTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup);
|
||||
virtual void performEndTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup);
|
||||
virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd) override;
|
||||
virtual void setWorldMatrix(const QMatrix& matrix) = 0;
|
||||
virtual void setCompositionMode(QPainter::CompositionMode mode) = 0;
|
||||
|
||||
/// Returns current pen
|
||||
const QPen& getCurrentPen() { return m_currentPen.get(this, &PDFPainterBase::getCurrentPenImpl); }
|
||||
|
||||
/// Returns current brush
|
||||
const QBrush& getCurrentBrush() { return m_currentBrush.get(this, &PDFPainterBase::getCurrentBrushImpl); }
|
||||
|
||||
/// Returns effective stroking alpha from transparency groups and current graphic state
|
||||
PDFReal getEffectiveStrokingAlpha() const;
|
||||
|
||||
/// Returns effective filling alpha from transparency groups and current graphic state
|
||||
PDFReal getEffectiveFillingAlpha() const;
|
||||
|
||||
/// Returns true, if blend mode can be set according the transparency group stack
|
||||
bool canSetBlendMode(BlendMode mode) const;
|
||||
|
||||
/// Returns, if feature is turned on
|
||||
bool hasFeature(PDFRenderer::Feature feature) const { return m_features.testFlag(feature); }
|
||||
|
||||
private:
|
||||
/// Returns current pen (implementation)
|
||||
QPen getCurrentPenImpl() const;
|
||||
|
||||
/// Returns current brush (implementation)
|
||||
QBrush getCurrentBrushImpl() const;
|
||||
|
||||
struct PDFTransparencyGroupPainterData
|
||||
{
|
||||
PDFTransparencyGroup group;
|
||||
PDFReal alphaStroke = 1.0;
|
||||
PDFReal alphaFill = 1.0;
|
||||
BlendMode blendMode = BlendMode::Normal;
|
||||
};
|
||||
|
||||
PDFRenderer::Features m_features;
|
||||
PDFCachedItem<QPen> m_currentPen;
|
||||
PDFCachedItem<QBrush> m_currentBrush;
|
||||
std::vector<PDFTransparencyGroupPainterData> m_transparencyGroupDataStack;
|
||||
};
|
||||
|
||||
/// Processor, which processes PDF's page commands on the QPainter. It works with QPainter
|
||||
/// and with transformation matrix, which translates page points to the device points.
|
||||
/// Only basic transparency is supported, advanced transparency, such as transparency groups,
|
||||
/// are not supported. Painter will try to emulate them so painting will not fail completely.
|
||||
class PDFPainter : public PDFPageContentProcessor
|
||||
class PDFPainter : public PDFPainterBase
|
||||
{
|
||||
using BaseClass = PDFPainterBase;
|
||||
|
||||
public:
|
||||
/// Constructs new PDFPainter object, with default parameters.
|
||||
/// \param painter Painter, on which page content is drawn
|
||||
@ -59,48 +124,182 @@ protected:
|
||||
virtual void performClipping(const QPainterPath& path, Qt::FillRule fillRule) override;
|
||||
virtual void performImagePainting(const QImage& image) override;
|
||||
virtual void performMeshPainting(const PDFMesh& mesh) override;
|
||||
virtual void performUpdateGraphicsState(const PDFPageContentProcessorState& state) override;
|
||||
virtual void performSaveGraphicState(ProcessOrder order) override;
|
||||
virtual void performRestoreGraphicState(ProcessOrder order) override;
|
||||
virtual void performBeginTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup);
|
||||
virtual void performEndTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup);
|
||||
virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd) override;
|
||||
virtual void setWorldMatrix(const QMatrix& matrix) override;
|
||||
virtual void setCompositionMode(QPainter::CompositionMode mode) override;
|
||||
|
||||
private:
|
||||
/// Returns current pen
|
||||
const QPen& getCurrentPen() { return m_currentPen.get(this, &PDFPainter::getCurrentPenImpl); }
|
||||
QPainter* m_painter;
|
||||
};
|
||||
|
||||
/// Returns current brush
|
||||
const QBrush& getCurrentBrush() { return m_currentBrush.get(this, &PDFPainter::getCurrentBrushImpl); }
|
||||
/// Precompiled page contains precompiled graphic instructions of a PDF page to draw it quickly
|
||||
/// on the target painter. It enables very fast drawing, because instructions are not decoded
|
||||
/// and interpreted from the PDF stream, but they are just "played" on the painter.
|
||||
class PDFPrecompiledPage
|
||||
{
|
||||
public:
|
||||
|
||||
/// Returns current pen (implementation)
|
||||
QPen getCurrentPenImpl() const;
|
||||
|
||||
/// Returns current brush (implementation)
|
||||
QBrush getCurrentBrushImpl() const;
|
||||
|
||||
/// Returns effective stroking alpha from transparency groups and current graphic state
|
||||
PDFReal getEffectiveStrokingAlpha() const;
|
||||
|
||||
/// Returns effective filling alpha from transparency groups and current graphic state
|
||||
PDFReal getEffectiveFillingAlpha() const;
|
||||
|
||||
/// Returns true, if blend mode can be set according the transparency group stack
|
||||
bool canSetBlendMode(BlendMode mode) const;
|
||||
|
||||
struct PDFTransparencyGroupPainterData
|
||||
enum class InstructionType
|
||||
{
|
||||
PDFTransparencyGroup group;
|
||||
PDFReal alphaStroke = 1.0;
|
||||
PDFReal alphaFill = 1.0;
|
||||
BlendMode blendMode = BlendMode::Normal;
|
||||
Invalid,
|
||||
DrawPath,
|
||||
DrawImage,
|
||||
DrawMesh,
|
||||
Clip,
|
||||
SaveGraphicState,
|
||||
RestoreGraphicState,
|
||||
SetWorldMatrix,
|
||||
SetCompositionMode
|
||||
};
|
||||
|
||||
QPainter* m_painter;
|
||||
PDFRenderer::Features m_features;
|
||||
PDFCachedItem<QPen> m_currentPen;
|
||||
PDFCachedItem<QBrush> m_currentBrush;
|
||||
std::vector<PDFTransparencyGroupPainterData> m_transparencyGroupDataStack;
|
||||
struct Instruction
|
||||
{
|
||||
inline Instruction() = default;
|
||||
inline Instruction(InstructionType type, size_t dataIndex) :
|
||||
type(type),
|
||||
dataIndex(dataIndex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
InstructionType type = InstructionType::Invalid;
|
||||
size_t dataIndex = 0;
|
||||
};
|
||||
|
||||
/// Paints page onto the painter using matrix
|
||||
/// \param painter Painter, onto which is page drawn
|
||||
/// \param cropBox Page's crop box
|
||||
/// \param pagePointToDevicePointMatrix Page point to device point transformation matrix
|
||||
/// \param features Renderer features
|
||||
void draw(QPainter* painter, const QRectF& cropBox, const QMatrix& pagePointToDevicePointMatrix, PDFRenderer::Features features) const;
|
||||
|
||||
void addPath(QPen pen, QBrush brush, QPainterPath path, bool isText);
|
||||
void addClip(QPainterPath path);
|
||||
void addImage(QImage image);
|
||||
void addMesh(PDFMesh mesh, PDFReal alpha);
|
||||
void addSaveGraphicState() { m_instructions.emplace_back(InstructionType::SaveGraphicState, 0); }
|
||||
void addRestoreGraphicState() { m_instructions.emplace_back(InstructionType::RestoreGraphicState, 0); }
|
||||
void addSetWorldMatrix(const QMatrix& matrix);
|
||||
void addSetCompositionMode(QPainter::CompositionMode compositionMode);
|
||||
|
||||
/// Optimizes page memory allocation to contain less space
|
||||
void optimize();
|
||||
|
||||
/// Finalizes precompiled page
|
||||
/// \param compilingTimeNS Compiling time in nanoseconds
|
||||
/// \param errors List of rendering errors
|
||||
void finalize(qint64 compilingTimeNS, QList<PDFRenderError> errors);
|
||||
|
||||
/// Returns compiling time in nanoseconds
|
||||
qint64 getCompilingTimeNS() const { return m_compilingTimeNS; }
|
||||
|
||||
/// Returns a list of rendering errors
|
||||
const QList<PDFRenderError>& getErrors() const { return m_errors; }
|
||||
|
||||
/// Returns true, if page is valid (i.e. has nonzero instruction count)
|
||||
bool isValid() const { return !m_instructions.empty(); }
|
||||
|
||||
private:
|
||||
struct PathPaintData
|
||||
{
|
||||
inline PathPaintData() = default;
|
||||
inline PathPaintData(QPen pen, QBrush brush, QPainterPath path, bool isText) :
|
||||
pen(qMove(pen)),
|
||||
brush(qMove(brush)),
|
||||
path(qMove(path)),
|
||||
isText(isText)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QPen pen;
|
||||
QBrush brush;
|
||||
QPainterPath path;
|
||||
bool isText = false;
|
||||
};
|
||||
|
||||
struct ClipData
|
||||
{
|
||||
inline ClipData() = default;
|
||||
inline ClipData(QPainterPath path) :
|
||||
clipPath(qMove(path))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QPainterPath clipPath;
|
||||
};
|
||||
|
||||
struct ImageData
|
||||
{
|
||||
inline ImageData() = default;
|
||||
inline ImageData(QImage image) :
|
||||
image(qMove(image))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QImage image;
|
||||
};
|
||||
|
||||
struct MeshPaintData
|
||||
{
|
||||
inline MeshPaintData() = default;
|
||||
inline MeshPaintData(PDFMesh mesh, PDFReal alpha) :
|
||||
mesh(qMove(mesh)),
|
||||
alpha(alpha)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PDFMesh mesh;
|
||||
PDFReal alpha = 1.0;
|
||||
};
|
||||
|
||||
qint64 m_compilingTimeNS = 0;
|
||||
std::vector<Instruction> m_instructions;
|
||||
std::vector<PathPaintData> m_paths;
|
||||
std::vector<ClipData> m_clips;
|
||||
std::vector<ImageData> m_images;
|
||||
std::vector<MeshPaintData> m_meshes;
|
||||
std::vector<QMatrix> m_matrices;
|
||||
std::vector<QPainter::CompositionMode> m_compositionModes;
|
||||
QList<PDFRenderError> m_errors;
|
||||
};
|
||||
|
||||
/// Processor, which processes PDF's page commands and writes them to the precompiled page.
|
||||
/// Precompiled page then can be used to execute these commands on QPainter.
|
||||
class PDFPrecompiledPageGenerator : public PDFPainterBase
|
||||
{
|
||||
using BaseClass = PDFPainterBase;
|
||||
|
||||
public:
|
||||
explicit inline PDFPrecompiledPageGenerator(PDFPrecompiledPage* precompiledPage,
|
||||
PDFRenderer::Features features,
|
||||
const PDFPage* page,
|
||||
const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
const PDFMeshQualitySettings& meshQualitySettings) :
|
||||
BaseClass(features, page, document, fontCache, optionalContentActivity, QMatrix(), meshQualitySettings),
|
||||
m_precompiledPage(precompiledPage)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule) override;
|
||||
virtual void performClipping(const QPainterPath& path, Qt::FillRule fillRule) override;
|
||||
virtual void performImagePainting(const QImage& image) override;
|
||||
virtual void performMeshPainting(const PDFMesh& mesh) override;
|
||||
virtual void performSaveGraphicState(ProcessOrder order) override;
|
||||
virtual void performRestoreGraphicState(ProcessOrder order) override;
|
||||
virtual void setWorldMatrix(const QMatrix& matrix) override;
|
||||
virtual void setCompositionMode(QPainter::CompositionMode mode) override;
|
||||
|
||||
private:
|
||||
PDFPrecompiledPage* m_precompiledPage;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "pdfpainter.h"
|
||||
#include "pdfdocument.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
@ -36,21 +38,8 @@ PDFRenderer::PDFRenderer(const PDFDocument* document,
|
||||
Q_ASSERT(document);
|
||||
}
|
||||
|
||||
QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& rectangle, size_t pageIndex) const
|
||||
QMatrix PDFRenderer::createPagePointToDevicePointMatrix(const PDFPage* page, const QRectF& rectangle) const
|
||||
{
|
||||
Q_UNUSED(painter);
|
||||
Q_UNUSED(rectangle);
|
||||
|
||||
const PDFCatalog* catalog = m_document->getCatalog();
|
||||
if (pageIndex >= catalog->getPageCount() || !catalog->getPage(pageIndex))
|
||||
{
|
||||
// Invalid page index
|
||||
return { PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Page %1 doesn't exist.").arg(pageIndex + 1)) };
|
||||
}
|
||||
|
||||
const PDFPage* page = catalog->getPage(pageIndex);
|
||||
Q_ASSERT(page);
|
||||
|
||||
QRectF mediaBox = page->getRotatedMediaBox();
|
||||
|
||||
QMatrix matrix;
|
||||
@ -94,14 +83,29 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& recta
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& rectangle, size_t pageIndex) const
|
||||
{
|
||||
const PDFCatalog* catalog = m_document->getCatalog();
|
||||
if (pageIndex >= catalog->getPageCount() || !catalog->getPage(pageIndex))
|
||||
{
|
||||
// Invalid page index
|
||||
return { PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Page %1 doesn't exist.").arg(pageIndex + 1)) };
|
||||
}
|
||||
|
||||
const PDFPage* page = catalog->getPage(pageIndex);
|
||||
Q_ASSERT(page);
|
||||
|
||||
QMatrix matrix = createPagePointToDevicePointMatrix(page, rectangle);
|
||||
|
||||
PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache, m_optionalContentActivity, m_meshQualitySettings);
|
||||
return processor.processContents();
|
||||
}
|
||||
|
||||
QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QMatrix& matrix, size_t pageIndex) const
|
||||
{
|
||||
Q_UNUSED(painter);
|
||||
|
||||
const PDFCatalog* catalog = m_document->getCatalog();
|
||||
if (pageIndex >= catalog->getPageCount() || !catalog->getPage(pageIndex))
|
||||
{
|
||||
@ -116,4 +120,27 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QMatrix& matr
|
||||
return processor.processContents();
|
||||
}
|
||||
|
||||
void PDFRenderer::compile(PDFPrecompiledPage* precompiledPage, size_t pageIndex) const
|
||||
{
|
||||
const PDFCatalog* catalog = m_document->getCatalog();
|
||||
if (pageIndex >= catalog->getPageCount() || !catalog->getPage(pageIndex))
|
||||
{
|
||||
// Invalid page index
|
||||
precompiledPage->finalize(0, { PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Page %1 doesn't exist.").arg(pageIndex + 1)) });
|
||||
return;
|
||||
}
|
||||
|
||||
const PDFPage* page = catalog->getPage(pageIndex);
|
||||
Q_ASSERT(page);
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
PDFPrecompiledPageGenerator generator(precompiledPage, m_features, page, m_document, m_fontCache, m_optionalContentActivity, m_meshQualitySettings);
|
||||
QList<PDFRenderError> errors = generator.processContents();
|
||||
precompiledPage->optimize();
|
||||
precompiledPage->finalize(timer.nsecsElapsed(), qMove(errors));
|
||||
timer.invalidate();
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -27,6 +27,7 @@ class QPainter;
|
||||
namespace pdf
|
||||
{
|
||||
class PDFFontCache;
|
||||
class PDFPrecompiledPage;
|
||||
class PDFOptionalContentActivity;
|
||||
|
||||
/// Renders the PDF page on the painter, or onto an image.
|
||||
@ -60,6 +61,19 @@ public:
|
||||
/// Rendering errors are reported and returned in the error list. If no error occured, empty list is returned.
|
||||
QList<PDFRenderError> render(QPainter* painter, const QMatrix& matrix, size_t pageIndex) const;
|
||||
|
||||
/// Compiles page (i.e. prepares compiled page). \p page should be empty page, onto which
|
||||
/// are graphics commands written. No exception is thrown. Rendering errors are reported and written
|
||||
/// to the compiled page.
|
||||
/// \param precompiledPage Precompiled page pointer
|
||||
/// \param pageIndex Index of page to be compiled
|
||||
void compile(PDFPrecompiledPage* precompiledPage, size_t pageIndex) const;
|
||||
|
||||
/// Creates page point to device point matrix for the given rectangle. It creates transformation
|
||||
/// from page's media box to the target rectangle.
|
||||
/// \param page Page, for which we want to create matrix
|
||||
/// \param rectangle Page rectangle, to which is page media box transformed
|
||||
QMatrix createPagePointToDevicePointMatrix(const PDFPage* page, const QRectF& rectangle) const;
|
||||
|
||||
/// Returns default renderer features
|
||||
static constexpr Features getDefaultFeatures() { return Antialiasing | TextAntialiasing | ClipToCropBox; }
|
||||
|
||||
@ -71,7 +85,6 @@ private:
|
||||
PDFMeshQualitySettings m_meshQualitySettings;
|
||||
};
|
||||
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(pdf::PDFRenderer::Features)
|
||||
|
Loading…
x
Reference in New Issue
Block a user