mirror of https://github.com/JakubMelka/PDF4QT.git
Simple rendering
This commit is contained in:
parent
ffc56d38e1
commit
60bb835a4e
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue