mirror of
				https://github.com/JakubMelka/PDF4QT.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	Font cache
This commit is contained in:
		| @@ -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()) | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user