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_pageLayoutMode(PageLayout::OneColumn),
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)
{
m_document = document;
m_fontCache.setDocument(document);
recalculate();
}
}
@ -343,6 +350,11 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update);
}
PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
{
}
void PDFDrawWidgetProxy::setDocument(const PDFDocument* document)
{
m_controller->setDocument(document);
@ -533,7 +545,7 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
// Clear the page space by white color
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);
if (!errors.empty())

View File

@ -21,6 +21,7 @@
#include "pdfglobal.h"
#include "pdfdocument.h"
#include "pdfrenderer.h"
#include "pdffont.h"
#include <QRectF>
#include <QObject>
@ -43,6 +44,7 @@ class PDFDrawSpaceController : public QObject
public:
explicit PDFDrawSpaceController(QObject* parent);
virtual ~PDFDrawSpaceController() override;
/// Sets the document and recalculates the draw space. Document can be nullptr,
/// in that case, draw space is cleared.
@ -88,6 +90,9 @@ public:
/// Returns the document
const PDFDocument* getDocument() const { return m_document; }
/// Returns the font cache
const PDFFontCache* getFontCache() const { return &m_fontCache; }
signals:
void drawSpaceChanged();
@ -109,6 +114,9 @@ private:
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;
PageLayout m_pageLayoutMode;
@ -116,6 +124,9 @@ private:
BlockItems m_blockItems;
PDFReal m_verticalSpacingMM;
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
@ -126,6 +137,7 @@ class PDFFORQTLIBSHARED_EXPORT PDFDrawWidgetProxy : public QObject
public:
explicit PDFDrawWidgetProxy(QObject* parent);
virtual ~PDFDrawWidgetProxy() override;
/// Sets the document and updates the draw space. Document can be nullptr,
/// in that case, draw space is cleared.

View File

@ -91,7 +91,7 @@ private:
PDFReal m_pixelSize;
/// Parent font
const PDFFont* m_parentFont;
PDFFontPointer m_parentFont;
/// True, if font is embedded
bool m_isEmbedded;
@ -253,7 +253,7 @@ bool PDFRealizedFont::isHorizontalWritingSystem() const
return !m_impl->m_isVertical;
}
PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(const PDFFont* font, PDFReal pixelSize)
PDFRealizedFontPointer PDFRealizedFont::createRealizedFont(PDFFontPointer font, PDFReal pixelSize)
{
PDFRealizedFontPointer result;
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: Remove QRawFont
return PDFRealizedFont::createRealizedFont(this, fontSize);
return PDFRealizedFont::createRealizedFont(font, fontSize);
/*
QRawFont rawFont;
@ -678,4 +678,69 @@ FontType PDFTrueTypeFont::getFontType() const
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

View File

@ -213,7 +213,7 @@ public:
/// Creates new realized font from the standard font. If font can't be created,
/// then exception is thrown.
static PDFRealizedFontPointer createRealizedFont(const PDFFont* font, PDFReal pixelSize);
static PDFRealizedFontPointer createRealizedFont(PDFFontPointer font, PDFReal pixelSize);
private:
/// Constructs new realized font
@ -235,7 +235,7 @@ public:
/// Realizes the font (physical materialization of the font using pixel size,
/// if font can't be realized, then empty QRawFont is returned).
/// \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
/// \param byteArray Byte array with encoded string
@ -268,7 +268,7 @@ public:
encoding::EncodingTable encoding);
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;
protected:
@ -312,6 +312,43 @@ public:
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
#endif // PDFFONT_H

View File

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

View File

@ -56,7 +56,7 @@ private:
class PDFPageContentProcessor
{
public:
explicit PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document);
explicit PDFPageContentProcessor(const PDFPage* page, const PDFDocument* document, const PDFFontCache* fontCache);
virtual ~PDFPageContentProcessor();
enum class Operator
@ -578,6 +578,7 @@ private:
const PDFPage* m_page;
const PDFDocument* m_document;
const PDFFontCache* m_fontCache;
const PDFDictionary* m_colorSpaceDictionary;
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) :
PDFPageContentProcessor(page, document),
PDFPainter::PDFPainter(QPainter* painter, PDFRenderer::Features features, QMatrix pagePointToDevicePointMatrix, const PDFPage* page, const PDFDocument* document, const PDFFontCache* fontCache) :
PDFPageContentProcessor(page, document, fontCache),
m_painter(painter),
m_features(features),
m_pagePointToDevicePointMatrix(pagePointToDevicePointMatrix)

View File

@ -34,7 +34,18 @@ 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);
/// \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;
protected:

View File

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

View File

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