mirror of https://github.com/JakubMelka/PDF4QT.git
Color spaces, beginning of painting
This commit is contained in:
parent
0a28869c94
commit
9264ea70c1
|
@ -47,7 +47,9 @@ SOURCES += \
|
|||
sources/pdfpage.cpp \
|
||||
sources/pdfstreamfilters.cpp \
|
||||
sources/pdfdrawspacecontroller.cpp \
|
||||
sources/pdfdrawwidget.cpp
|
||||
sources/pdfdrawwidget.cpp \
|
||||
sources/pdfcolorspaces.cpp \
|
||||
sources/pdfrenderer.cpp
|
||||
|
||||
HEADERS += \
|
||||
sources/pdfobject.h \
|
||||
|
@ -65,7 +67,10 @@ HEADERS += \
|
|||
sources/pdfpage.h \
|
||||
sources/pdfstreamfilters.h \
|
||||
sources/pdfdrawspacecontroller.h \
|
||||
sources/pdfdrawwidget.h
|
||||
sources/pdfdrawwidget.h \
|
||||
sources/pdfflatarray.h \
|
||||
sources/pdfcolorspaces.h \
|
||||
sources/pdfrenderer.h
|
||||
|
||||
unix {
|
||||
target.path = /usr/lib
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// 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 "pdfcolorspaces.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
QColor PDFDeviceGrayColorSpace::getColor(const PDFColor& color) const
|
||||
{
|
||||
Q_ASSERT(color.size() == getColorComponentCount());
|
||||
|
||||
PDFColorComponent component = clip01(color[0]);
|
||||
|
||||
QColor result(QColor::Rgb);
|
||||
result.setRgbF(component, component, component, 1.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t PDFDeviceGrayColorSpace::getColorComponentCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
QColor PDFDeviceRGBColorSpace::getColor(const PDFColor& color) const
|
||||
{
|
||||
Q_ASSERT(color.size() == getColorComponentCount());
|
||||
|
||||
PDFColorComponent r = clip01(color[0]);
|
||||
PDFColorComponent g = clip01(color[1]);
|
||||
PDFColorComponent b = clip01(color[2]);
|
||||
|
||||
QColor result(QColor::Rgb);
|
||||
result.setRgbF(r, g, b, 1.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t PDFDeviceRGBColorSpace::getColorComponentCount() const
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
QColor PDFDeviceCMYKColorSpace::getColor(const PDFColor& color) const
|
||||
{
|
||||
Q_ASSERT(color.size() == getColorComponentCount());
|
||||
|
||||
PDFColorComponent c = clip01(color[0]);
|
||||
PDFColorComponent m = clip01(color[1]);
|
||||
PDFColorComponent y = clip01(color[2]);
|
||||
PDFColorComponent k = clip01(color[3]);
|
||||
|
||||
QColor result(QColor::Cmyk);
|
||||
result.setCmykF(c, m, y, k, 1.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t PDFDeviceCMYKColorSpace::getColorComponentCount() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
|
@ -0,0 +1,78 @@
|
|||
// 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 PDFCOLORSPACES_H
|
||||
#define PDFCOLORSPACES_H
|
||||
|
||||
#include "pdfflatarray.h"
|
||||
|
||||
#include <QColor>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
using PDFColorComponent = float;
|
||||
using PDFColor = PDFFlatArray<PDFColorComponent, 4>;
|
||||
|
||||
/// Represents PDF's color space
|
||||
class PDFAbstractColorSpace
|
||||
{
|
||||
public:
|
||||
explicit PDFAbstractColorSpace() = default;
|
||||
virtual ~PDFAbstractColorSpace() = default;
|
||||
|
||||
virtual QColor getColor(const PDFColor& color) const = 0;
|
||||
virtual size_t getColorComponentCount() const = 0;
|
||||
|
||||
protected:
|
||||
/// Clips the color component to range [0, 1]
|
||||
static constexpr PDFColorComponent clip01(PDFColorComponent component) { return qBound<PDFColorComponent>(0.0, component, 1.0); }
|
||||
};
|
||||
|
||||
class PDFDeviceGrayColorSpace : public PDFAbstractColorSpace
|
||||
{
|
||||
public:
|
||||
explicit PDFDeviceGrayColorSpace() = default;
|
||||
virtual ~PDFDeviceGrayColorSpace() = default;
|
||||
|
||||
virtual QColor getColor(const PDFColor& color) const override;
|
||||
virtual size_t getColorComponentCount() const override;
|
||||
};
|
||||
|
||||
class PDFDeviceRGBColorSpace : public PDFAbstractColorSpace
|
||||
{
|
||||
public:
|
||||
explicit PDFDeviceRGBColorSpace() = default;
|
||||
virtual ~PDFDeviceRGBColorSpace() = default;
|
||||
|
||||
virtual QColor getColor(const PDFColor& color) const override;
|
||||
virtual size_t getColorComponentCount() const override;
|
||||
};
|
||||
|
||||
class PDFDeviceCMYKColorSpace : public PDFAbstractColorSpace
|
||||
{
|
||||
public:
|
||||
explicit PDFDeviceCMYKColorSpace() = default;
|
||||
virtual ~PDFDeviceCMYKColorSpace() = default;
|
||||
|
||||
virtual QColor getColor(const PDFColor& color) const override;
|
||||
virtual size_t getColorComponentCount() const override;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFCOLORSPACES_H
|
|
@ -19,6 +19,8 @@
|
|||
#include "pdfdocument.h"
|
||||
#include "pdfparser.h"
|
||||
#include "pdfencoding.h"
|
||||
#include "pdfstreamfilters.h"
|
||||
#include "pdfconstants.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
@ -38,6 +40,96 @@ static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_TRUE = "True";
|
|||
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_FALSE = "False";
|
||||
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_UNKNOWN = "Unknown";
|
||||
|
||||
QByteArray PDFDocument::getDecodedStream(const PDFStream* stream) const
|
||||
{
|
||||
const PDFDictionary* dictionary = stream->getDictionary();
|
||||
|
||||
// Retrieve filters
|
||||
PDFObject filters;
|
||||
if (dictionary->hasKey(PDF_STREAM_DICT_FILTER))
|
||||
{
|
||||
filters = getObject(dictionary->get(PDF_STREAM_DICT_FILTER));
|
||||
}
|
||||
else if (dictionary->hasKey(PDF_STREAM_DICT_FILE_FILTER))
|
||||
{
|
||||
filters = getObject(dictionary->get(PDF_STREAM_DICT_FILE_FILTER));
|
||||
}
|
||||
|
||||
// Retrieve filter parameters
|
||||
PDFObject filterParameters;
|
||||
if (dictionary->hasKey(PDF_STREAM_DICT_DECODE_PARMS))
|
||||
{
|
||||
filterParameters = getObject(dictionary->get(PDF_STREAM_DICT_DECODE_PARMS));
|
||||
}
|
||||
else if (dictionary->hasKey(PDF_STREAM_DICT_FDECODE_PARMS))
|
||||
{
|
||||
filterParameters = getObject(dictionary->get(PDF_STREAM_DICT_FDECODE_PARMS));
|
||||
}
|
||||
|
||||
std::vector<const PDFStreamFilter*> filterObjects;
|
||||
std::vector<PDFObject> filterParameterObjects;
|
||||
|
||||
if (filters.isName())
|
||||
{
|
||||
filterObjects.push_back(PDFStreamFilterStorage::getFilter(filters.getString()));
|
||||
}
|
||||
else if (filters.isArray())
|
||||
{
|
||||
const PDFArray* filterArray = filters.getArray();
|
||||
const size_t filterCount = filterArray->getCount();
|
||||
for (size_t i = 0; i < filterCount; ++i)
|
||||
{
|
||||
const PDFObject& object = getObject(filterArray->getItem(i));
|
||||
if (object.isName())
|
||||
{
|
||||
filterObjects.push_back(PDFStreamFilterStorage::getFilter(object.getString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!filters.isNull())
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
if (filterParameters.isArray())
|
||||
{
|
||||
const PDFArray* filterParameterArray = filterParameters.getArray();
|
||||
const size_t filterParameterCount = filterParameterArray->getCount();
|
||||
for (size_t i = 0; i < filterParameterCount; ++i)
|
||||
{
|
||||
const PDFObject& object = getObject(filterParameterArray->getItem(i));
|
||||
filterParameterObjects.push_back(object);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
filterParameterObjects.push_back(filterParameters);
|
||||
}
|
||||
|
||||
filterParameterObjects.resize(filterObjects.size());
|
||||
std::reverse(filterObjects.begin(), filterObjects.end());
|
||||
std::reverse(filterParameterObjects.begin(), filterParameterObjects.end());
|
||||
|
||||
QByteArray result = *stream->getContent();
|
||||
|
||||
for (size_t i = 0, count = filterObjects.size(); i < count; ++i)
|
||||
{
|
||||
const PDFStreamFilter* streamFilter = filterObjects[i];
|
||||
const PDFObject& streamFilterParameters = filterParameterObjects[i];
|
||||
|
||||
if (streamFilter)
|
||||
{
|
||||
result = streamFilter->apply(result, this, streamFilterParameters);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PDFDocument::init()
|
||||
{
|
||||
initInfo();
|
||||
|
|
|
@ -176,6 +176,11 @@ public:
|
|||
/// Returns the document catalog
|
||||
const PDFCatalog* getCatalog() const { return &m_catalog; }
|
||||
|
||||
/// Returns the decoded stream. If stream data cannot be decoded,
|
||||
/// then empty byte array is returned.
|
||||
/// \param stream Stream to be decoded
|
||||
QByteArray getDecodedStream(const PDFStream* stream) const;
|
||||
|
||||
private:
|
||||
friend class PDFDocumentReader;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfdrawwidget.h"
|
||||
#include "pdfrenderer.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
|
@ -536,6 +537,9 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,9 @@ public:
|
|||
/// \param blockIndex Index of the block
|
||||
LayoutItems getLayoutItems(size_t blockIndex) const;
|
||||
|
||||
/// Returns the document
|
||||
const PDFDocument* getDocument() const { return m_document; }
|
||||
|
||||
signals:
|
||||
void drawSpaceChanged();
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
// 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 PDFFLATARRAY_H
|
||||
#define PDFFLATARRAY_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
/// This represents a fast array, consisting of "fast" block of fixed size \p FlatSize,
|
||||
/// and "slow" block of variable size. Usually, this array is used when vast majority
|
||||
/// of usage size is below FlatSize, only minority is above FlatSize. Typical example
|
||||
/// of use of this class:
|
||||
///
|
||||
/// We have colors in PDF, which can have usually 1, 3 or 4 color components. But in some
|
||||
/// rare cases, we have much more components, for example for DeviceN color spaces.
|
||||
/// For this reason, we will set FlatSize to 4 (so Gray, RGB and CMYK colors will not
|
||||
/// use slow "variable" part).
|
||||
template<typename T, size_t FlatSize>
|
||||
class PDFFlatArray
|
||||
{
|
||||
public:
|
||||
explicit PDFFlatArray() :
|
||||
m_flatBlock(),
|
||||
m_flatBlockEndIterator(m_flatBlock.begin()),
|
||||
m_variableBlock()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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_variableBlock()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Returns the size of the array
|
||||
size_t size() const { return getFlatBlockSize() + m_variableBlock.size(); }
|
||||
|
||||
/// Returns true, if array is empty
|
||||
bool empty() const { return size() == 0; }
|
||||
|
||||
template<size_t index>
|
||||
const T& get() const
|
||||
{
|
||||
if constexpr (index < FlatSize)
|
||||
{
|
||||
return m_flatBlock[size];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_variableBlock[size - FlatSize];
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t index>
|
||||
T& get()
|
||||
{
|
||||
if constexpr (index < FlatSize)
|
||||
{
|
||||
return m_flatBlock[size];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_variableBlock[size - FlatSize];
|
||||
}
|
||||
}
|
||||
|
||||
const T& operator[] (size_t index) const
|
||||
{
|
||||
Q_ASSERT(index < size());
|
||||
|
||||
if (index < FlatSize)
|
||||
{
|
||||
return m_flatBlock[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_variableBlock[index - FlatSize];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t getFlatBlockSize() const { return std::distance(m_flatBlock.cbegin(), std::array<T, FlatSize>::const_iterator(m_flatBlockEndIterator)); }
|
||||
|
||||
std::array<T, FlatSize> m_flatBlock;
|
||||
typename std::array<T, FlatSize>::iterator m_flatBlockEndIterator; ///< Pointer to the end of flat block
|
||||
std::vector<T> m_variableBlock;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFFLATARRAY_H
|
|
@ -82,6 +82,7 @@ public:
|
|||
inline const QRectF& getTrimBox() const { return m_trimBox; }
|
||||
inline const QRectF& getArtBox() const { return m_artBox; }
|
||||
inline PageRotation getPageRotation() const { return m_pageRotation; }
|
||||
|
||||
inline const PDFObject& getResources() const { return m_resources; }
|
||||
inline const PDFObject& getContents() const { return m_contents; }
|
||||
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
// 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 "pdfrenderer.h"
|
||||
#include "pdfdocument.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFRenderer::PDFRenderer(const PDFDocument* document) :
|
||||
m_document(document),
|
||||
m_features(Antialasing | TextAntialiasing)
|
||||
{
|
||||
Q_ASSERT(document);
|
||||
}
|
||||
|
||||
QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& rectangle, size_t pageIndex) 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);
|
||||
|
||||
PDFPageContentProcessor processor(page, m_document);
|
||||
return processor.processContents();
|
||||
}
|
||||
|
||||
PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document) :
|
||||
m_page(page),
|
||||
m_document(document)
|
||||
{
|
||||
Q_ASSERT(page);
|
||||
Q_ASSERT(document);
|
||||
}
|
||||
|
||||
QList<PDFRenderError> PDFPageContentProcessor::processContents()
|
||||
{
|
||||
const PDFObject& contents = m_page->getContents();
|
||||
|
||||
if (contents.isArray())
|
||||
{
|
||||
const PDFArray* array = contents.getArray();
|
||||
const size_t count = array->getCount();
|
||||
|
||||
QList<PDFRenderError> errors;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const PDFObject& streamObject = m_document->getObject(array->getItem(i));
|
||||
if (streamObject.isStream())
|
||||
{
|
||||
errors.append(processContentStream(streamObject.getStream()));
|
||||
}
|
||||
else
|
||||
{
|
||||
errors.append(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Invalid page contents.")));
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(errors);
|
||||
}
|
||||
else if (contents.isStream())
|
||||
{
|
||||
return processContentStream(contents.getStream());
|
||||
}
|
||||
else
|
||||
{
|
||||
return { PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Invalid page contents.")) };
|
||||
}
|
||||
}
|
||||
|
||||
QList<PDFRenderError> PDFPageContentProcessor::processContentStream(const PDFStream* stream)
|
||||
{
|
||||
QByteArray content = m_document->getDecodedStream(stream);
|
||||
|
||||
return QList<PDFRenderError>();
|
||||
}
|
||||
|
||||
PDFPageContentProcessor::PDFPageContentProcessorState::PDFPageContentProcessorState() :
|
||||
m_currentTransformationMatrix(),
|
||||
m_fillColorSpace(),
|
||||
m_strokeColorSpace(),
|
||||
m_fillColor(Qt::black),
|
||||
m_strokeColor(Qt::black),
|
||||
m_lineWidth(1.0),
|
||||
m_lineCapStyle(Qt::FlatCap),
|
||||
m_lineJoinStyle(Qt::MiterJoin),
|
||||
m_mitterLimit(10.0),
|
||||
m_renderingIntent(),
|
||||
m_flatness(1.0),
|
||||
m_smoothness(0.01)
|
||||
{
|
||||
m_fillColorSpace.reset(new PDFDeviceGrayColorSpace);
|
||||
m_strokeColorSpace = m_fillColorSpace;
|
||||
}
|
||||
|
||||
PDFPageContentProcessor::PDFPageContentProcessorState::~PDFPageContentProcessorState()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
} // namespace pdf
|
|
@ -0,0 +1,129 @@
|
|||
// 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 PDFRENDERER_H
|
||||
#define PDFRENDERER_H
|
||||
|
||||
#include "pdfpage.h"
|
||||
#include "pdfcolorspaces.h"
|
||||
|
||||
#include <QMatrix>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
enum RenderErrorType
|
||||
{
|
||||
Error,
|
||||
NotImplemented
|
||||
};
|
||||
|
||||
struct PDFRenderError
|
||||
{
|
||||
explicit PDFRenderError() = default;
|
||||
explicit PDFRenderError(RenderErrorType type, QString message) :
|
||||
type(type),
|
||||
message(std::move(message))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
RenderErrorType type = RenderErrorType::Error;
|
||||
QString message;
|
||||
};
|
||||
|
||||
/// Renders the PDF page on the painter, or onto an image.
|
||||
class PDFRenderer
|
||||
{
|
||||
public:
|
||||
explicit PDFRenderer(const PDFDocument* document);
|
||||
|
||||
enum Feature
|
||||
{
|
||||
Antialasing, ///< 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)
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(Features, Feature)
|
||||
|
||||
/// Paints desired page onto the painter. Page is painted in the rectangle using best-fit method.
|
||||
/// 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.
|
||||
/// \param painter Painter
|
||||
/// \param rectangle Paint area for the page
|
||||
/// \param pageIndex Index of the page to be painted
|
||||
QList<PDFRenderError> render(QPainter* painter, const QRectF& rectangle, size_t pageIndex) const;
|
||||
|
||||
private:
|
||||
const PDFDocument* m_document;
|
||||
Features m_features;
|
||||
};
|
||||
|
||||
/// Process the contents of the page.
|
||||
class PDFPageContentProcessor
|
||||
{
|
||||
public:
|
||||
explicit PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document);
|
||||
|
||||
/// Process the contents of the page
|
||||
QList<PDFRenderError> processContents();
|
||||
|
||||
protected:
|
||||
/// Process the content stream
|
||||
QList<PDFRenderError> processContentStream(const PDFStream* stream);
|
||||
|
||||
/// Represents graphic state of the PDF (holding current graphic state parameters).
|
||||
/// Please see PDF Reference 1.7, Chapter 4.3 "Graphic State"
|
||||
class PDFPageContentProcessorState
|
||||
{
|
||||
public:
|
||||
explicit PDFPageContentProcessorState();
|
||||
~PDFPageContentProcessorState();
|
||||
|
||||
private:
|
||||
QMatrix m_currentTransformationMatrix;
|
||||
QSharedPointer<PDFAbstractColorSpace> m_fillColorSpace;
|
||||
QSharedPointer<PDFAbstractColorSpace> m_strokeColorSpace;
|
||||
QColor m_fillColor;
|
||||
QColor m_strokeColor;
|
||||
PDFReal m_lineWidth;
|
||||
Qt::PenCapStyle m_lineCapStyle;
|
||||
Qt::PenJoinStyle m_lineJoinStyle;
|
||||
PDFReal m_mitterLimit;
|
||||
QByteArray m_renderingIntent;
|
||||
PDFReal m_flatness;
|
||||
PDFReal m_smoothness;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
const PDFPage* m_page;
|
||||
const PDFDocument* m_document;
|
||||
|
||||
/// Stack with current graphic states
|
||||
std::stack<PDFPageContentProcessorState> m_stack;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(pdf::PDFRenderer::Features)
|
||||
|
||||
#endif // PDFRENDERER_H
|
|
@ -401,4 +401,46 @@ QByteArray PDFRunLengthDecodeFilter::apply(const QByteArray& data, const PDFDocu
|
|||
return result;
|
||||
}
|
||||
|
||||
const PDFStreamFilter* PDFStreamFilterStorage::getFilter(const QByteArray& filterName)
|
||||
{
|
||||
const PDFStreamFilterStorage* instance = getInstance();
|
||||
auto it = instance->m_filters.find(filterName);
|
||||
if (it != instance->m_filters.cend())
|
||||
{
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
auto itNameDecoded = instance->m_abbreviations.find(filterName);
|
||||
if (itNameDecoded != instance->m_abbreviations.cend())
|
||||
{
|
||||
return getFilter(itNameDecoded->second);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PDFStreamFilterStorage::PDFStreamFilterStorage()
|
||||
{
|
||||
// Initialize map with the filters
|
||||
m_filters["ASCIIHexDecode"] = std::make_unique<PDFAsciiHexDecodeFilter>();
|
||||
m_filters["ASCII85Decode"] = std::make_unique<PDFAscii85DecodeFilter>();
|
||||
m_filters["LZWDecode"] = std::make_unique<PDFLzwDecodeFilter>();
|
||||
m_filters["FlateDecode"] = std::make_unique<PDFFlateDecodeFilter>();
|
||||
m_filters["RunLengthDecode"] = std::make_unique<PDFRunLengthDecodeFilter>();
|
||||
|
||||
m_abbreviations["AHx"] = "ASCIIHexDecode";
|
||||
m_abbreviations["A85"] = "ASCII85Decode";
|
||||
m_abbreviations["LZW"] = "LZWDecode";
|
||||
m_abbreviations["Fl"] = "FlateDecode";
|
||||
m_abbreviations["RL"] = "RunLengthDecode";
|
||||
m_abbreviations["CCF"] = "CCITFaxDecode";
|
||||
m_abbreviations["DCT"] = "DCTDecode";
|
||||
}
|
||||
|
||||
const PDFStreamFilterStorage* PDFStreamFilterStorage::getInstance()
|
||||
{
|
||||
static PDFStreamFilterStorage instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
|
|
@ -22,9 +22,35 @@
|
|||
|
||||
#include <QByteArray>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFDocument;
|
||||
class PDFStreamFilter;
|
||||
|
||||
/// Storage for stream filters. Can retrieve stream filters by name. Using singleton
|
||||
/// design pattern. Use static methods to retrieve filters.
|
||||
class PDFStreamFilterStorage
|
||||
{
|
||||
public:
|
||||
/// Retrieves filter by filter name. If filter with that name doesn't exist,
|
||||
/// then nullptr is returned. This function is thread safe.
|
||||
/// \param filterName Name of the filter to be retrieved.
|
||||
static const PDFStreamFilter* getFilter(const QByteArray& filterName);
|
||||
|
||||
private:
|
||||
explicit PDFStreamFilterStorage();
|
||||
|
||||
static const PDFStreamFilterStorage* getInstance();
|
||||
|
||||
/// Maps names to the instances of the stream filters
|
||||
std::map<QByteArray, std::unique_ptr<PDFStreamFilter>> m_filters;
|
||||
|
||||
/// Filter stream names can be specified in simplified (shorter) form.
|
||||
/// This map maps shorter form to the longer form.
|
||||
std::map<QByteArray, QByteArray> m_abbreviations;
|
||||
};
|
||||
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFStreamFilter
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue