Simple rendering

This commit is contained in:
Jakub Melka 2019-02-24 17:48:37 +01:00
parent ffc56d38e1
commit 60bb835a4e
11 changed files with 1855 additions and 1449 deletions

View File

@ -49,7 +49,9 @@ SOURCES += \
sources/pdfdrawspacecontroller.cpp \
sources/pdfdrawwidget.cpp \
sources/pdfcolorspaces.cpp \
sources/pdfrenderer.cpp
sources/pdfrenderer.cpp \
sources/pdfpagecontentprocessor.cpp \
sources/pdfpainter.cpp
HEADERS += \
sources/pdfobject.h \
@ -71,7 +73,9 @@ HEADERS += \
sources/pdfflatarray.h \
sources/pdfcolorspaces.h \
sources/pdfrenderer.h \
sources/pdfrenderer_impl.h
sources/pdfpagecontentprocessor.h \
sources/pdfpainter.h \
sources/pdfutils.h
unix {
target.path = /usr/lib

View File

@ -221,6 +221,10 @@ PDFColorSpacePointer PDFAbstractColorSpace::createDeviceColorSpaceByNameImpl(con
return PDFColorSpacePointer(new PDFDeviceCMYKColorSpace());
}
}
else if (colorSpaceDictionary && colorSpaceDictionary->hasKey(name))
{
return createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(colorSpaceDictionary->get(name)), recursion);
}
throw PDFParserException(PDFTranslationContext::tr("Invalid color space."));
return PDFColorSpacePointer();

View File

@ -533,11 +533,6 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
// Clear the page space by white color
painter->fillRect(placedRect, Qt::white);
QFont font = m_widget->font();
font.setPixelSize(placedRect.height() * 0.75);
painter->setFont(font);
painter->drawText(placedRect, Qt::AlignCenter, QString::number(item.pageIndex + 1));
PDFRenderer renderer(m_controller->getDocument());
QList<PDFRenderError> errors = renderer.render(painter, placedRect, item.pageIndex);
}

View File

@ -43,7 +43,7 @@ class PDFFlatArray
public:
explicit PDFFlatArray() :
m_flatBlock(),
m_flatBlockEndIterator(m_flatBlock.begin()),
m_flatBlockItemCount(0),
m_variableBlock()
{
@ -52,7 +52,7 @@ public:
template<typename... Arguments, typename std::enable_if<sizeof...(Arguments) <= FlatSize, int>::type = 0>
explicit inline PDFFlatArray(Arguments... arguments) :
m_flatBlock({ arguments... }),
m_flatBlockEndIterator(std::next(m_flatBlock.begin(), sizeof...(Arguments))),
m_flatBlockItemCount(sizeof...(Arguments)),
m_variableBlock()
{
@ -120,15 +120,15 @@ public:
void clear()
{
m_flatBlockEndIterator = m_flatBlock.begin();
m_flatBlockItemCount = 0;
m_variableBlock.clear();
}
void push_back(T object)
{
if (m_flatBlockEndIterator != m_flatBlock.cend())
if (m_flatBlockItemCount < m_flatBlock.size())
{
*m_flatBlockEndIterator++ = std::move(object);
m_flatBlock[m_flatBlockItemCount++] = std::move(object);
}
else
{
@ -137,10 +137,10 @@ public:
}
private:
size_t getFlatBlockSize() const { return std::distance(m_flatBlock.cbegin(), std::array<T, FlatSize>::const_iterator(m_flatBlockEndIterator)); }
size_t getFlatBlockSize() const { return m_flatBlockItemCount; }
std::array<T, FlatSize> m_flatBlock;
typename std::array<T, FlatSize>::iterator m_flatBlockEndIterator; ///< Pointer to the end of flat block
size_t m_flatBlockItemCount; ///< Number of items in the flat block
std::vector<T> m_variableBlock;
};

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,8 @@
// 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 PDFRENDERER_IMPL_H
#define PDFRENDERER_IMPL_H
#ifndef PDFPAGECONTENTPROCESSOR_H
#define PDFPAGECONTENTPROCESSOR_H
#include "pdfrenderer.h"
#include "pdfparser.h"
@ -54,6 +54,7 @@ class PDFPageContentProcessor
{
public:
explicit PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document);
virtual ~PDFPageContentProcessor();
enum class Operator
{
@ -189,6 +190,9 @@ protected:
inline bool operator==(const PDFLineDashPattern& other) const { return m_dashArray == other.m_dashArray && m_dashOffset == other.m_dashOffset; }
inline bool operator!=(const PDFLineDashPattern& other) const { return !(*this == other); }
/// Is line solid? Function returns true, if yes.
bool isSolid() const { return m_dashArray.empty(); }
private:
std::vector<PDFReal> m_dashArray;
PDFReal m_dashOffset = 0.0;
@ -223,7 +227,8 @@ protected:
StateLineDashPattern = 0x0200,
StateRenderingIntent = 0x0400,
StateFlatness = 0x0800,
StateSmoothness = 0x1000
StateSmoothness = 0x1000,
StateAll = 0xFFFF
};
Q_DECLARE_FLAGS(StateFlags, StateFlag)
@ -322,6 +327,13 @@ protected:
/// \param order If this function is called before the operation, or after the operation.
virtual void performRestoreGraphicState(ProcessOrder order);
/// Returns current graphic state
const PDFPageContentProcessorState* getGraphicState() const { return &m_graphicState; }
/// Adds error to the error list
/// \param error Error message
void addError(const QString& error) { m_errorList.append(PDFRenderError(RenderErrorType::Error, error)); }
private:
/// Process the content stream
void processContentStream(const PDFStream* stream);
@ -490,4 +502,4 @@ private:
} // namespace pdf
#endif // PDFRENDERER_IMPL_H
#endif // PDFPAGECONTENTPROCESSOR_H

View File

@ -0,0 +1,182 @@
// 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 "pdfpainter.h"
#include <QPainter>
namespace pdf
{
PDFPainter::PDFPainter(QPainter* painter, PDFRenderer::Features features, QMatrix pagePointToDevicePointMatrix, const PDFPage* page, const PDFDocument* document) :
PDFPageContentProcessor(page, document),
m_painter(painter),
m_features(features),
m_pagePointToDevicePointMatrix(pagePointToDevicePointMatrix)
{
Q_ASSERT(painter);
Q_ASSERT(pagePointToDevicePointMatrix.isInvertible());
m_painter->save();
}
PDFPainter::~PDFPainter()
{
m_painter->restore();
}
void PDFPainter::performPathPainting(const QPainterPath& path, bool stroke, bool fill, Qt::FillRule fillRule)
{
if ((!stroke && !fill) || path.isEmpty())
{
// No operation requested - either path is empty, or neither stroking or filling
return;
}
if (stroke)
{
m_painter->setPen(getCurrentPen());
}
else
{
m_painter->setPen(Qt::NoPen);
}
if (fill)
{
m_painter->setBrush(getCurrentBrush());
}
else
{
m_painter->setBrush(Qt::NoBrush);
}
m_painter->setRenderHint(QPainter::Antialiasing, m_features.testFlag(PDFRenderer::Antialiasing));
Q_ASSERT(path.fillRule() == fillRule);
m_painter->drawPath(path);
}
void PDFPainter::performClipping(const QPainterPath& path, Qt::FillRule fillRule)
{
Q_ASSERT(path.fillRule() == fillRule);
if (m_painter->hasClipping())
{
m_painter->setClipPath(path, Qt::IntersectClip);
}
else
{
addError(PDFTranslationContext::tr("The paint device doesn't support clipping. Path was not clipped."));
}
}
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(m_pagePointToDevicePointMatrix * state.getCurrentTransformationMatrix(), 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))
{
m_currentPen.dirty();
}
if (flags.testFlag(PDFPageContentProcessorState::StateFillColor))
{
m_currentBrush.dirty();
}
}
void PDFPainter::performSaveGraphicState(ProcessOrder order)
{
if (order == ProcessOrder::AfterOperation)
{
m_painter->save();
}
}
void PDFPainter::performRestoreGraphicState(ProcessOrder order)
{
if (order == ProcessOrder::BeforeOperation)
{
m_painter->restore();
}
}
QPen PDFPainter::getCurrentPenImpl() const
{
const PDFPageContentProcessorState* graphicState = getGraphicState();
const QColor& color = graphicState->getStrokeColor();
if (color.isValid())
{
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 PDFPainter::getCurrentBrushImpl() const
{
const PDFPageContentProcessorState* graphicState = getGraphicState();
const QColor& color = graphicState->getFillColor();
if (color.isValid())
{
return QBrush(color, Qt::SolidPattern);
}
else
{
return QBrush(Qt::NoBrush);
}
}
} // namespace pdf

View File

@ -0,0 +1,69 @@
// 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 PDFPAINTER_H
#define PDFPAINTER_H
#include "pdfutils.h"
#include "pdfrenderer.h"
#include "pdfpagecontentprocessor.h"
#include <QPen>
#include <QBrush>
namespace pdf
{
/// 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.
class PDFPainter : public PDFPageContentProcessor
{
public:
/// Constructs new PDFPainter object, with default parameters.
explicit PDFPainter(QPainter* painter, PDFRenderer::Features features, QMatrix pagePointToDevicePointMatrix, const PDFPage* page, const PDFDocument* document);
virtual ~PDFPainter() override;
protected:
virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, Qt::FillRule fillRule) override;
virtual void performClipping(const QPainterPath& path, Qt::FillRule fillRule) override;
virtual void performUpdateGraphicsState(const PDFPageContentProcessorState& state) override;
virtual void performSaveGraphicState(ProcessOrder order) override;
virtual void performRestoreGraphicState(ProcessOrder order) override;
private:
/// Returns current pen
const QPen& getCurrentPen() { return m_currentPen.get(this, &PDFPainter::getCurrentPenImpl); }
/// Returns current brush
const QBrush& getCurrentBrush() { return m_currentBrush.get(this, &PDFPainter::getCurrentBrushImpl); }
/// Returns current pen (implementation)
QPen getCurrentPenImpl() const;
/// Returns current brush (implementation)
QBrush getCurrentBrushImpl() const;
QPainter* m_painter;
PDFRenderer::Features m_features;
QMatrix m_pagePointToDevicePointMatrix;
PDFCachedItem<QPen> m_currentPen;
PDFCachedItem<QBrush> m_currentBrush;
};
} // namespace pdf
#endif // PDFPAINTER_H

File diff suppressed because it is too large Load Diff

View File

@ -53,9 +53,9 @@ public:
enum Feature
{
Antialiasing, ///< Antialiasing for lines, shapes, etc.
TextAntialiasing, ///< Antialiasing for drawing text
SmoothImages ///< Adjust images to the device space using smooth transformation (slower, but better performance quality)
Antialiasing = 0x0001, ///< Antialiasing for lines, shapes, etc.
TextAntialiasing = 0x0002, ///< Antialiasing for drawing text
SmoothImages = 0x0004 ///< Adjust images to the device space using smooth transformation (slower, but better performance quality)
};
Q_DECLARE_FLAGS(Features, Feature)
@ -68,6 +68,11 @@ public:
/// \param pageIndex Index of the page to be painted
QList<PDFRenderError> render(QPainter* painter, const QRectF& rectangle, size_t pageIndex) const;
/// Paints desired page onto the painter. Page is painted using \p matrix, which maps page coordinates
/// to the device coordinates. If the page doesn't exist, then error is returned. No exception is thrown.
/// 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;
private:
const PDFDocument* m_document;
Features m_features;

View File

@ -0,0 +1,69 @@
// 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 PDFUTILS_H
#define PDFUTILS_H
namespace pdf
{
/// Class for easy storing of cached item. This class is not thread safe,
/// and for this reason, access function are not constant (they can modify the
/// object).
template<typename T>
class PDFCachedItem
{
public:
explicit inline PDFCachedItem() :
m_dirty(true),
m_object()
{
}
/// Returns the cached object. If object is dirty, then cached object is refreshed.
/// \param holder Holder object, which owns the cached item
/// \param function Refresh function
template<typename H>
inline const T& get(const H* holder, T(H::* function)(void) const)
{
if (m_dirty)
{
m_object = (holder->*function)();
m_dirty = false;
}
return m_object;
}
/// Invalidates the cached item, so it must be refreshed from the cache next time,
/// if it is accessed.
inline void dirty()
{
m_dirty = true;
m_object = T();
}
private:
bool m_dirty;
T m_object;
};
} // namespace pdf
#endif // PDFUTILS_H