Font cache

This commit is contained in:
Jakub Melka 2019-04-12 19:17:19 +02:00
parent 51b2ccacec
commit 90767ddfa5
10 changed files with 163 additions and 22 deletions

View File

@ -30,7 +30,13 @@ PDFDrawSpaceController::PDFDrawSpaceController(QObject* parent) :
m_document(nullptr), m_document(nullptr),
m_pageLayoutMode(PageLayout::OneColumn), m_pageLayoutMode(PageLayout::OneColumn),
m_verticalSpacingMM(5.0), m_verticalSpacingMM(5.0),
m_horizontalSpacingMM(1.0) m_horizontalSpacingMM(1.0),
m_fontCache(FONT_CACHE_LIMIT, REALIZED_FONT_CACHE_LIMIT)
{
}
PDFDrawSpaceController::~PDFDrawSpaceController()
{ {
} }
@ -40,6 +46,7 @@ void PDFDrawSpaceController::setDocument(const PDFDocument* document)
if (document != m_document) if (document != m_document)
{ {
m_document = document; m_document = document;
m_fontCache.setDocument(document);
recalculate(); recalculate();
} }
} }
@ -343,6 +350,11 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update); connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update);
} }
PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
{
}
void PDFDrawWidgetProxy::setDocument(const PDFDocument* document) void PDFDrawWidgetProxy::setDocument(const PDFDocument* document)
{ {
m_controller->setDocument(document); m_controller->setDocument(document);
@ -533,7 +545,7 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
// Clear the page space by white color // Clear the page space by white color
painter->fillRect(placedRect, Qt::white); painter->fillRect(placedRect, Qt::white);
PDFRenderer renderer(m_controller->getDocument()); PDFRenderer renderer(m_controller->getDocument(), m_controller->getFontCache());
QList<PDFRenderError> errors = renderer.render(painter, placedRect, item.pageIndex); QList<PDFRenderError> errors = renderer.render(painter, placedRect, item.pageIndex);
if (!errors.empty()) if (!errors.empty())

View File

@ -21,6 +21,7 @@
#include "pdfglobal.h" #include "pdfglobal.h"
#include "pdfdocument.h" #include "pdfdocument.h"
#include "pdfrenderer.h" #include "pdfrenderer.h"
#include "pdffont.h"
#include <QRectF> #include <QRectF>
#include <QObject> #include <QObject>
@ -43,6 +44,7 @@ class PDFDrawSpaceController : public QObject
public: public:
explicit PDFDrawSpaceController(QObject* parent); explicit PDFDrawSpaceController(QObject* parent);
virtual ~PDFDrawSpaceController() override;
/// Sets the document and recalculates the draw space. Document can be nullptr, /// Sets the document and recalculates the draw space. Document can be nullptr,
/// in that case, draw space is cleared. /// in that case, draw space is cleared.
@ -88,6 +90,9 @@ public:
/// Returns the document /// Returns the document
const PDFDocument* getDocument() const { return m_document; } const PDFDocument* getDocument() const { return m_document; }
/// Returns the font cache
const PDFFontCache* getFontCache() const { return &m_fontCache; }
signals: signals:
void drawSpaceChanged(); void drawSpaceChanged();
@ -109,6 +114,9 @@ private:
using BlockItems = std::vector<LayoutBlock>; using BlockItems = std::vector<LayoutBlock>;
static constexpr size_t FONT_CACHE_LIMIT = 32;
static constexpr size_t REALIZED_FONT_CACHE_LIMIT = 128;
const PDFDocument* m_document; const PDFDocument* m_document;
PageLayout m_pageLayoutMode; PageLayout m_pageLayoutMode;
@ -116,6 +124,9 @@ private:
BlockItems m_blockItems; BlockItems m_blockItems;
PDFReal m_verticalSpacingMM; PDFReal m_verticalSpacingMM;
PDFReal m_horizontalSpacingMM; PDFReal m_horizontalSpacingMM;
/// Font cache
PDFFontCache m_fontCache;
}; };
/// This is a proxy class to draw space controller using widget. We have two spaces, pixel space /// This is a proxy class to draw space controller using widget. We have two spaces, pixel space
@ -126,6 +137,7 @@ class PDFFORQTLIBSHARED_EXPORT PDFDrawWidgetProxy : public QObject
public: public:
explicit PDFDrawWidgetProxy(QObject* parent); explicit PDFDrawWidgetProxy(QObject* parent);
virtual ~PDFDrawWidgetProxy() override;
/// Sets the document and updates the draw space. Document can be nullptr, /// Sets the document and updates the draw space. Document can be nullptr,
/// in that case, draw space is cleared. /// in that case, draw space is cleared.

View File

@ -91,7 +91,7 @@ private:
PDFReal m_pixelSize; PDFReal m_pixelSize;
/// Parent font /// Parent font
const PDFFont* m_parentFont; PDFFontPointer m_parentFont;
/// True, if font is embedded /// True, if font is embedded
bool m_isEmbedded; bool m_isEmbedded;
@ -253,7 +253,7 @@ bool PDFRealizedFont::isHorizontalWritingSystem() const
return !m_impl->m_isVertical; return !m_impl->m_isVertical;
} }
PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(const PDFFont* font, PDFReal pixelSize) PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(PDFFontPointer font, PDFReal pixelSize)
{ {
PDFRealizedFontPointer result; PDFRealizedFontPointer result;
std::unique_ptr<PDFRealizedFontImpl> implPtr(new PDFRealizedFontImpl()); std::unique_ptr<PDFRealizedFontImpl> implPtr(new PDFRealizedFontImpl());
@ -597,12 +597,12 @@ PDFSimpleFont::PDFSimpleFont(FontDescriptor fontDescriptor,
} }
PDFRealizedFontPointer PDFSimpleFont::getRealizedFont(PDFReal fontSize) const PDFRealizedFontPointer PDFSimpleFont::getRealizedFont(PDFFontPointer font, PDFReal fontSize) const
{ {
// TODO: Fix font creation to use also embedded fonts, font descriptor, etc. // TODO: Fix font creation to use also embedded fonts, font descriptor, etc.
// TODO: Remove QRawFont // TODO: Remove QRawFont
return PDFRealizedFont::createRealizedFont(this, fontSize); return PDFRealizedFont::createRealizedFont(font, fontSize);
/* /*
QRawFont rawFont; QRawFont rawFont;
@ -678,4 +678,69 @@ FontType PDFTrueTypeFont::getFontType() const
return FontType::TrueType; return FontType::TrueType;
} }
void PDFFontCache::setDocument(const PDFDocument* document)
{
QMutexLocker lock(&m_mutex);
if (m_document != document)
{
m_document = document;
m_fontCache.clear();
m_realizedFontCache.clear();
}
}
PDFFontPointer PDFFontCache::getFont(const PDFObject& fontObject) const
{
if (fontObject.isReference())
{
// Font is object reference. Look in the cache, if we have it, then return it.
QMutexLocker lock(&m_mutex);
PDFObjectReference reference = fontObject.getReference();
auto it = m_fontCache.find(reference);
if (it == m_fontCache.cend())
{
// We must create the font
PDFFontPointer font = PDFFont::createFont(fontObject, m_document);
if (m_fontCache.size() >= m_fontCacheLimit)
{
// We have exceeded the cache limit. Clear the cache.
m_fontCache.clear();
}
it = m_fontCache.insert(std::make_pair(reference, qMove(font))).first;
}
return it->second;
}
else
{
// Object is not a reference. Create font directly and return it.
return PDFFont::createFont(fontObject, m_document);
}
}
PDFRealizedFontPointer PDFFontCache::getRealizedFont(const PDFFontPointer& font, PDFReal size) const
{
Q_ASSERT(font);
QMutexLocker lock(&m_mutex);
auto it = m_realizedFontCache.find(std::make_pair(font, size));
if (it == m_realizedFontCache.cend())
{
// We must create the realized font
PDFRealizedFontPointer realizedFont = font->getRealizedFont(font, size);
if (m_realizedFontCache.size() >= m_realizedFontCacheLimit)
{
m_realizedFontCache.clear();
}
it = m_realizedFontCache.insert(std::make_pair(std::make_pair(font, size), qMove(realizedFont))).first;
}
return it->second;
}
} // namespace pdf } // namespace pdf

View File

@ -213,7 +213,7 @@ public:
/// Creates new realized font from the standard font. If font can't be created, /// Creates new realized font from the standard font. If font can't be created,
/// then exception is thrown. /// then exception is thrown.
static PDFRealizedFontPointer createRealizedFont(const PDFFont* font, PDFReal pixelSize); static PDFRealizedFontPointer createRealizedFont(PDFFontPointer font, PDFReal pixelSize);
private: private:
/// Constructs new realized font /// Constructs new realized font
@ -235,7 +235,7 @@ public:
/// Realizes the font (physical materialization of the font using pixel size, /// Realizes the font (physical materialization of the font using pixel size,
/// if font can't be realized, then empty QRawFont is returned). /// if font can't be realized, then empty QRawFont is returned).
/// \param fontSize Size of the font /// \param fontSize Size of the font
virtual PDFRealizedFontPointer getRealizedFont(PDFReal fontSize) const = 0; virtual PDFRealizedFontPointer getRealizedFont(PDFFontPointer font, PDFReal fontSize) const = 0;
/// Returns text using the font encoding /// Returns text using the font encoding
/// \param byteArray Byte array with encoded string /// \param byteArray Byte array with encoded string
@ -268,7 +268,7 @@ public:
encoding::EncodingTable encoding); encoding::EncodingTable encoding);
virtual ~PDFSimpleFont() override = default; virtual ~PDFSimpleFont() override = default;
virtual PDFRealizedFontPointer getRealizedFont(PDFReal fontSize) const override; virtual PDFRealizedFontPointer getRealizedFont(PDFFontPointer font, PDFReal fontSize) const override;
virtual QString getTextUsingEncoding(const QByteArray& byteArray) const override; virtual QString getTextUsingEncoding(const QByteArray& byteArray) const override;
protected: protected:
@ -312,6 +312,43 @@ public:
virtual FontType getFontType() const override; virtual FontType getFontType() const override;
}; };
/// Font cache which caches both fonts, and realized fonts. Cache has individual limit
/// for fonts, and realized fonts.
class PDFFontCache
{
public:
inline explicit PDFFontCache(size_t fontCacheLimit, size_t realizedFontCacheLimit) :
m_fontCacheLimit(fontCacheLimit),
m_realizedFontCacheLimit(realizedFontCacheLimit),
m_document(nullptr)
{
}
/// Sets the document to the cache. Whole cache is cleared.
/// \param document Document to be setted
void setDocument(const PDFDocument* document);
/// Retrieves font from the cache. If font can't be accessed or created,
/// then exception is thrown.
/// \param fontObject Font object
PDFFontPointer getFont(const PDFObject& fontObject) const;
/// Retrieves realized font from the cache. If realized font can't be accessed or created,
/// then exception is thrown.
/// \param font Font, which should be realized
/// \param size Size of the font (in pixels)
PDFRealizedFontPointer getRealizedFont(const PDFFontPointer& font, PDFReal size) const;
private:
const size_t m_fontCacheLimit;
const size_t m_realizedFontCacheLimit;
mutable QMutex m_mutex;
const PDFDocument* m_document;
mutable std::map<PDFObjectReference, PDFFontPointer> m_fontCache;
mutable std::map<std::pair<PDFFontPointer, PDFReal>, PDFRealizedFontPointer> m_realizedFontCache;
};
} // namespace pdf } // namespace pdf
#endif // PDFFONT_H #endif // PDFFONT_H

View File

@ -149,9 +149,10 @@ static constexpr const std::pair<const char*, PDFPageContentProcessor::Operator>
{ "EX", PDFPageContentProcessor::Operator::CompatibilityEnd } { "EX", PDFPageContentProcessor::Operator::CompatibilityEnd }
}; };
PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document) : PDFPageContentProcessor::PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document, const PDFFontCache* fontCache) :
m_page(page), m_page(page),
m_document(document), m_document(document),
m_fontCache(fontCache),
m_colorSpaceDictionary(nullptr), m_colorSpaceDictionary(nullptr),
m_fontDictionary(nullptr), m_fontDictionary(nullptr),
m_textBeginEndState(0) m_textBeginEndState(0)
@ -1532,7 +1533,7 @@ void PDFPageContentProcessor::operatorTextSetFontAndFontSize(PDFOperandName font
{ {
try try
{ {
PDFFontPointer font = PDFFont::createFont(m_fontDictionary->get(fontName.name), m_document); PDFFontPointer font = m_fontCache->getFont(m_fontDictionary->get(fontName.name));
m_graphicState.setTextFont(qMove(font)); m_graphicState.setTextFont(qMove(font));
m_graphicState.setTextFontSize(fontSize); m_graphicState.setTextFontSize(fontSize);
@ -1759,7 +1760,6 @@ void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
// Calculate text rendering matrix // Calculate text rendering matrix
QMatrix adjustMatrix(horizontalScaling, 0.0, 0.0, 1.0, 0.0, textRise); QMatrix adjustMatrix(horizontalScaling, 0.0, 0.0, 1.0, 0.0, textRise);
QMatrix textMatrix = m_graphicState.getTextMatrix(); QMatrix textMatrix = m_graphicState.getTextMatrix();
QMatrix fontMatrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
size_t characterIndex = 0; size_t characterIndex = 0;
for (const TextSequenceItem& item : textSequence.items) for (const TextSequenceItem& item : textSequence.items)
@ -1787,7 +1787,7 @@ void PDFPageContentProcessor::drawText(const TextSequence& textSequence)
// Then get the glyph path and paint it // Then get the glyph path and paint it
if (item.glyph) if (item.glyph)
{ {
QPainterPath glyphPath = fontMatrix.map(*item.glyph); const QPainterPath& glyphPath = *item.glyph;
if (!glyphPath.isEmpty()) if (!glyphPath.isEmpty())
{ {
QMatrix textRenderingMatrix = textMatrix * adjustMatrix; QMatrix textRenderingMatrix = textMatrix * adjustMatrix;
@ -1866,7 +1866,7 @@ PDFRealizedFontPointer PDFPageContentProcessor::getRealizedFontImpl() const
{ {
if (m_graphicState.getTextFont()) if (m_graphicState.getTextFont())
{ {
return m_graphicState.getTextFont()->getRealizedFont(m_graphicState.getTextFontSize()); return m_fontCache->getRealizedFont(m_graphicState.getTextFont(), m_graphicState.getTextFontSize());
} }
return PDFRealizedFontPointer(); return PDFRealizedFontPointer();

View File

@ -56,7 +56,7 @@ private:
class PDFPageContentProcessor class PDFPageContentProcessor
{ {
public: public:
explicit PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document); explicit PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document, const PDFFontCache* fontCache);
virtual ~PDFPageContentProcessor(); virtual ~PDFPageContentProcessor();
enum class Operator enum class Operator
@ -578,6 +578,7 @@ private:
const PDFPage* m_page; const PDFPage* m_page;
const PDFDocument* m_document; const PDFDocument* m_document;
const PDFFontCache* m_fontCache;
const PDFDictionary* m_colorSpaceDictionary; const PDFDictionary* m_colorSpaceDictionary;
const PDFDictionary* m_fontDictionary; const PDFDictionary* m_fontDictionary;

View File

@ -23,8 +23,8 @@ namespace pdf
{ {
PDFPainter::PDFPainter(QPainter* painter, PDFRenderer::Features features, QMatrix pagePointToDevicePointMatrix, const PDFPage* page, const PDFDocument* document) : PDFPainter::PDFPainter(QPainter* painter, PDFRenderer::Features features, QMatrix pagePointToDevicePointMatrix, const PDFPage* page, const PDFDocument* document, const PDFFontCache* fontCache) :
PDFPageContentProcessor(page, document), PDFPageContentProcessor(page, document, fontCache),
m_painter(painter), m_painter(painter),
m_features(features), m_features(features),
m_pagePointToDevicePointMatrix(pagePointToDevicePointMatrix) m_pagePointToDevicePointMatrix(pagePointToDevicePointMatrix)

View File

@ -34,7 +34,18 @@ class PDFPainter : public PDFPageContentProcessor
{ {
public: public:
/// Constructs new PDFPainter object, with default parameters. /// Constructs new PDFPainter object, with default parameters.
explicit PDFPainter(QPainter* painter, PDFRenderer::Features features, QMatrix pagePointToDevicePointMatrix, const PDFPage* page, const PDFDocument* document); /// \param painter Painter, on which page content is drawn
/// \param features Features of the painter
/// \param pagePointToDevicePointMatrix Matrix, which translates page points to device points
/// \param page Page, which will be drawn
/// \param document Document owning the page
/// \param fontCache Font cache
explicit PDFPainter(QPainter* painter,
PDFRenderer::Features features,
QMatrix pagePointToDevicePointMatrix,
const PDFPage* page,
const PDFDocument* document,
const PDFFontCache* fontCache);
virtual ~PDFPainter() override; virtual ~PDFPainter() override;
protected: protected:

View File

@ -22,8 +22,9 @@
namespace pdf namespace pdf
{ {
PDFRenderer::PDFRenderer(const PDFDocument* document) : PDFRenderer::PDFRenderer(const PDFDocument* document, const PDFFontCache* fontCache) :
m_document(document), m_document(document),
m_fontCache(fontCache),
m_features(Antialiasing | TextAntialiasing) m_features(Antialiasing | TextAntialiasing)
{ {
Q_ASSERT(document); Q_ASSERT(document);
@ -54,7 +55,7 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QRectF& recta
matrix.translate(rectangle.left(), rectangle.bottom()); matrix.translate(rectangle.left(), rectangle.bottom());
matrix.scale(rectangle.width() / mediaBox.width(), -rectangle.height() / mediaBox.height()); matrix.scale(rectangle.width() / mediaBox.width(), -rectangle.height() / mediaBox.height());
PDFPainter processor(painter, m_features, matrix, page, m_document); PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache);
return processor.processContents(); return processor.processContents();
} }
@ -72,7 +73,7 @@ QList<PDFRenderError> PDFRenderer::render(QPainter* painter, const QMatrix& matr
const PDFPage* page = catalog->getPage(pageIndex); const PDFPage* page = catalog->getPage(pageIndex);
Q_ASSERT(page); Q_ASSERT(page);
PDFPainter processor(painter, m_features, matrix, page, m_document); PDFPainter processor(painter, m_features, matrix, page, m_document, m_fontCache);
return processor.processContents(); return processor.processContents();
} }

View File

@ -24,6 +24,7 @@ class QPainter;
namespace pdf namespace pdf
{ {
class PDFFontCache;
enum RenderErrorType enum RenderErrorType
{ {
@ -49,7 +50,7 @@ struct PDFRenderError
class PDFRenderer class PDFRenderer
{ {
public: public:
explicit PDFRenderer(const PDFDocument* document); explicit PDFRenderer(const PDFDocument* document, const PDFFontCache* fontCache);
enum Feature enum Feature
{ {
@ -75,6 +76,7 @@ public:
private: private:
const PDFDocument* m_document; const PDFDocument* m_document;
const PDFFontCache* m_fontCache;
Features m_features; Features m_features;
}; };