[poppler] poppler/CairoFontEngine.cc poppler/CairoFontEngine.h poppler/CairoOutputDev.cc poppler/CairoOutputDev.h

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Sat May 28 08:25:34 UTC 2022


 poppler/CairoFontEngine.cc |  421 ++++++++++++++++++---------------------------
 poppler/CairoFontEngine.h  |   44 +++-
 poppler/CairoOutputDev.cc  |    2 
 poppler/CairoOutputDev.h   |    2 
 4 files changed, 204 insertions(+), 265 deletions(-)

New commits:
commit f33cdde7e0e6a6352d724033cc9801c964487bc6
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat May 14 12:47:23 2022 +0930

    Refactor CairoFontEngine caching

diff --git a/poppler/CairoFontEngine.cc b/poppler/CairoFontEngine.cc
index 62c4bab6..ddf1b557 100644
--- a/poppler/CairoFontEngine.cc
+++ b/poppler/CairoFontEngine.cc
@@ -41,7 +41,6 @@
 #include <config.h>
 
 #include <cstring>
-#include <forward_list>
 #include <fstream>
 #include "CairoFontEngine.h"
 #include "CairoOutputDev.h"
@@ -58,15 +57,14 @@
 // CairoFont
 //------------------------------------------------------------------------
 
-CairoFont::CairoFont(Ref refA, cairo_font_face_t *cairo_font_faceA, int *codeToGIDA, unsigned int codeToGIDLenA, bool substituteA, bool printingA)
-    : ref(refA), cairo_font_face(cairo_font_faceA), codeToGID(codeToGIDA), codeToGIDLen(codeToGIDLenA), substitute(substituteA), printing(printingA)
+CairoFont::CairoFont(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector<int> &&codeToGIDA, bool substituteA, bool printingA) : ref(refA), cairo_font_face(cairo_font_faceA), substitute(substituteA), printing(printingA)
 {
+    codeToGID = std::move(codeToGIDA);
 }
 
 CairoFont::~CairoFont()
 {
     cairo_font_face_destroy(cairo_font_face);
-    gfree(codeToGID);
 }
 
 bool CairoFont::matches(Ref &other, bool printingA)
@@ -83,7 +81,7 @@ unsigned long CairoFont::getGlyph(CharCode code, const Unicode *u, int uLen)
 {
     FT_UInt gid;
 
-    if (codeToGID && code < codeToGIDLen) {
+    if (code < codeToGID.size()) {
         gid = (FT_UInt)codeToGID[code];
     } else {
         gid = (FT_UInt)code;
@@ -91,7 +89,7 @@ unsigned long CairoFont::getGlyph(CharCode code, const Unicode *u, int uLen)
     return gid;
 }
 
-double CairoFont::getSubstitutionCorrection(GfxFont *gfxFont)
+double CairoFont::getSubstitutionCorrection(const std::shared_ptr<GfxFont> &gfxFont)
 {
     double w1, w2, w3;
     CharCode code;
@@ -101,12 +99,12 @@ double CairoFont::getSubstitutionCorrection(GfxFont *gfxFont)
     // width of 'm' in the original font and the substituted font
     if (isSubstitute() && !gfxFont->isCIDFont()) {
         for (code = 0; code < 256; ++code) {
-            if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') {
+            if ((name = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') {
                 break;
             }
         }
         if (code < 256) {
-            w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
+            w1 = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getWidth(code);
             {
                 cairo_matrix_t m;
                 cairo_matrix_init_identity(&m);
@@ -122,7 +120,7 @@ double CairoFont::getSubstitutionCorrection(GfxFont *gfxFont)
                 cairo_font_options_destroy(options);
                 w2 = extents.x_advance;
             }
-            w3 = ((Gfx8BitFont *)gfxFont)->getWidth(0);
+            w3 = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getWidth(0);
             if (!gfxFont->isSymbolic() && w2 > 0 && w1 > w3) {
                 // if real font is substantially narrower than substituted
                 // font, reduce the font size accordingly
@@ -140,172 +138,88 @@ double CairoFont::getSubstitutionCorrection(GfxFont *gfxFont)
 // CairoFreeTypeFont
 //------------------------------------------------------------------------
 
-static cairo_user_data_key_t _ft_cairo_key;
+static cairo_user_data_key_t ft_cairo_key;
 
-static void _ft_done_face_uncached(void *closure)
-{
-    FT_Face face = (FT_Face)closure;
-    FT_Done_Face(face);
-}
-
-static bool _ft_new_face_uncached(FT_Library lib, const char *filename, FT_Face *face_out, cairo_font_face_t **font_face_out)
+// Font resources to be freed when cairo_font_face_t is destroyed
+struct FreeTypeFontResource
 {
     FT_Face face;
-    cairo_font_face_t *font_face;
-
-    if (FT_New_Face(lib, filename, 0, &face)) {
-        return false;
-    }
-
-    font_face = cairo_ft_font_face_create_for_ft_face(face, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP);
-    if (cairo_font_face_set_user_data(font_face, &_ft_cairo_key, face, _ft_done_face_uncached)) {
-        _ft_done_face_uncached(face);
-        cairo_font_face_destroy(font_face);
-        return false;
-    }
-
-    *face_out = face;
-    *font_face_out = font_face;
-    return true;
-}
-
-struct _ft_face_data
-{
-    _ft_face_data() = default;
-
-    _ft_face_data(FT_Library libA, const char *filenameA, Ref embFontIDA) : filename(filenameA ? filenameA : ""), embFontID(embFontIDA), lib(libA), face(nullptr), font_face(nullptr) { }
-
-    std::vector<unsigned char> bytes;
-
-    std::string filename;
-    Ref embFontID;
-
-    FT_Library lib;
-    FT_Face face;
-    cairo_font_face_t *font_face;
-};
-
-class _FtFaceDataProxy
-{
-    _ft_face_data *_data;
-
-public:
-    explicit _FtFaceDataProxy(_ft_face_data *data) : _data(data) { cairo_font_face_reference(_data->font_face); }
-    _FtFaceDataProxy(_FtFaceDataProxy &&) = delete;
-    ~_FtFaceDataProxy() { cairo_font_face_destroy(_data->font_face); }
-    explicit operator _ft_face_data *() { return _data; }
+    std::vector<unsigned char> font_data;
 };
 
-static thread_local std::forward_list<_FtFaceDataProxy> _local_open_faces;
-
-static bool operator==(_ft_face_data &a, _ft_face_data &b)
-{
-    if (a.lib != b.lib) {
-        return false;
-    }
-
-    if (a.embFontID != b.embFontID) {
-        return false;
-    }
-
-    return a.filename == b.filename;
-}
-
+// cairo callback for when cairo_font_face_t is destroyed
 static void _ft_done_face(void *closure)
 {
-    struct _ft_face_data *data = (struct _ft_face_data *)closure;
+    FreeTypeFontResource *resource = (FreeTypeFontResource *)closure;
 
-    FT_Done_Face(data->face);
-    delete data;
+    FT_Done_Face(resource->face);
+    delete resource;
 }
 
-static bool _ft_new_face(FT_Library lib, const char *filename, Ref embFontID, std::vector<unsigned char> &&font_data, FT_Face *face_out, cairo_font_face_t **font_face_out)
-{
-    struct _ft_face_data ft_face_data(lib, filename, embFontID);
+CairoFreeTypeFont::CairoFreeTypeFont(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector<int> &&codeToGIDA, bool substituteA) : CairoFont(refA, cairo_font_faceA, std::move(codeToGIDA), substituteA, true) { }
 
-    if (font_data.empty()) {
-        /* if we fail to open the file, just pass it to FreeType instead */
-        std::ifstream font_data_fstream(filename, std::ios::binary);
-        if (!font_data_fstream.is_open()) {
-            return _ft_new_face_uncached(lib, filename, face_out, font_face_out);
-        }
-    }
+CairoFreeTypeFont::~CairoFreeTypeFont() { }
 
-    /* check to see if this is a duplicate of any of the currently open fonts */
-    for (_FtFaceDataProxy &face_proxy : _local_open_faces) {
-        _ft_face_data *l = static_cast<_ft_face_data *>(face_proxy);
-        if ((*l) == ft_face_data) {
-            *face_out = l->face;
-            *font_face_out = cairo_font_face_reference(l->font_face);
-            return true;
-        }
-    }
+// Create a cairo_font_face_t for the given font filename OR font data.
+static std::optional<FreeTypeFontFace> createFreeTypeFontFace(FT_Library lib, const std::string &filename, std::vector<unsigned char> &&font_data)
+{
+    FreeTypeFontResource *resource = new FreeTypeFontResource;
+    FreeTypeFontFace font_face;
 
-    /* not a dup, open and insert into list */
     if (font_data.empty()) {
-        if (FT_New_Face(lib, filename, 0, &ft_face_data.face)) {
-            return false;
+        FT_Error err = FT_New_Face(lib, filename.c_str(), 0, &resource->face);
+        if (err) {
+            delete resource;
+            return {};
         }
     } else {
-        ft_face_data.bytes = std::move(font_data);
-        if (FT_New_Memory_Face(lib, (FT_Byte *)ft_face_data.bytes.data(), ft_face_data.bytes.size(), 0, &ft_face_data.face)) {
-            return false;
+        resource->font_data = std::move(font_data);
+        FT_Error err = FT_New_Memory_Face(lib, (FT_Byte *)resource->font_data.data(), resource->font_data.size(), 0, &resource->face);
+        if (err) {
+            delete resource;
+            return {};
         }
     }
 
-    struct _ft_face_data *l = new _ft_face_data;
-    *l = std::move(ft_face_data);
-
-    l->font_face = cairo_ft_font_face_create_for_ft_face(l->face, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP);
-    if (cairo_font_face_set_user_data(l->font_face, &_ft_cairo_key, l, _ft_done_face)) {
-        cairo_font_face_destroy(l->font_face);
-        _ft_done_face(l);
-        return false;
+    font_face.cairo_font_face = cairo_ft_font_face_create_for_ft_face(resource->face, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP);
+    if (cairo_font_face_set_user_data(font_face.cairo_font_face, &ft_cairo_key, resource, _ft_done_face)) {
+        cairo_font_face_destroy(font_face.cairo_font_face);
+        _ft_done_face(resource);
+        return {};
     }
 
-    _local_open_faces.remove_if([](_FtFaceDataProxy &face_proxy) {
-        _ft_face_data *data = static_cast<_ft_face_data *>(face_proxy);
-        return cairo_font_face_get_reference_count(data->font_face) == 1;
-    });
-    _local_open_faces.emplace_front(l);
-
-    *face_out = l->face;
-    *font_face_out = l->font_face;
-    return true;
+    font_face.face = resource->face;
+    return font_face;
 }
 
-CairoFreeTypeFont::CairoFreeTypeFont(Ref refA, cairo_font_face_t *cairo_font_faceA, int *codeToGIDA, unsigned int codeToGIDLenA, bool substituteA) : CairoFont(refA, cairo_font_faceA, codeToGIDA, codeToGIDLenA, substituteA, true) { }
+// Create a cairo_font_face_t for the given font filename OR font data. First checks if external font
+// is in the cache.
+std::optional<FreeTypeFontFace> CairoFreeTypeFont::getFreeTypeFontFace(CairoFontEngine *fontEngine, FT_Library lib, const std::string &filename, std::vector<unsigned char> &&font_data)
+{
+    if (font_data.empty()) {
+        return fontEngine->getExternalFontFace(lib, filename);
+    }
 
-CairoFreeTypeFont::~CairoFreeTypeFont() { }
+    return createFreeTypeFontFace(lib, filename, std::move(font_data));
+}
 
-CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, FT_Library lib, bool useCIDs)
+CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr<GfxFont> &gfxFont, XRef *xref, FT_Library lib, CairoFontEngine *fontEngine, bool useCIDs)
 {
-    const char *fileNameC;
+    std::string fileName;
     std::vector<unsigned char> font_data;
     int i, n;
-    GfxFontType fontType;
     std::optional<GfxFontLoc> fontLoc;
     char **enc;
     const char *name;
     FoFiType1C *ff1c;
-    Ref ref;
-    FT_Face face;
-    cairo_font_face_t *font_face;
-
-    int *codeToGID;
-    unsigned int codeToGIDLen;
-
-    codeToGID = nullptr;
-    codeToGIDLen = 0;
-    const GooString *fileName = nullptr;
-    fileNameC = nullptr;
-
+    std::optional<FreeTypeFontFace> font_face;
+    std::vector<int> codeToGID;
     bool substitute = false;
 
-    ref = *gfxFont->getID();
+    Ref ref = *gfxFont->getID();
     Ref embFontID = Ref::INVALID();
     gfxFont->getEmbeddedFontID(&embFontID);
-    fontType = gfxFont->getType();
+    GfxFontType fontType = gfxFont->getType();
 
     if (!(fontLoc = gfxFont->locateFont(xref, nullptr))) {
         error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)");
@@ -322,41 +236,37 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, FT_Li
 
         // external font
     } else { // gfxFontLocExternal
-        fileName = fontLoc->pathAsGooString();
+        fileName = fontLoc->path;
         fontType = fontLoc->fontType;
         substitute = true;
     }
 
-    if (fileName != nullptr) {
-        fileNameC = fileName->c_str();
-    }
-
     switch (fontType) {
     case fontType1:
     case fontType1C:
     case fontType1COT:
-        if (!_ft_new_face(lib, fileNameC, embFontID, std::move(font_data), &face, &font_face)) {
+        font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data));
+        if (!font_face) {
             error(errSyntaxError, -1, "could not create type1 face");
             goto err2;
         }
 
-        enc = ((Gfx8BitFont *)gfxFont)->getEncoding();
+        enc = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getEncoding();
 
-        codeToGID = (int *)gmallocn(256, sizeof(int));
-        codeToGIDLen = 256;
+        codeToGID.resize(256);
         for (i = 0; i < 256; ++i) {
             codeToGID[i] = 0;
             if ((name = enc[i])) {
-                codeToGID[i] = FT_Get_Name_Index(face, (char *)name);
+                codeToGID[i] = FT_Get_Name_Index(font_face->face, (char *)name);
                 if (codeToGID[i] == 0) {
                     Unicode u;
                     u = globalParams->mapNameToUnicodeText(name);
-                    codeToGID[i] = FT_Get_Char_Index(face, u);
+                    codeToGID[i] = FT_Get_Char_Index(font_face->face, u);
                 }
                 if (codeToGID[i] == 0) {
                     name = GfxFont::getAlternateName(name);
                     if (name) {
-                        codeToGID[i] = FT_Get_Name_Index(face, (char *)name);
+                        codeToGID[i] = FT_Get_Name_Index(font_face->face, (char *)name);
                     }
                 }
             }
@@ -364,27 +274,28 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, FT_Li
         break;
     case fontCIDType2:
     case fontCIDType2OT:
-        codeToGID = nullptr;
-        n = 0;
-        if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
-            n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+        if (std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID()) {
+            n = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGIDLen();
             if (n) {
-                codeToGID = (int *)gmallocn(n, sizeof(int));
-                memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int));
+                const int *src = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID();
+                codeToGID.reserve(n);
+                codeToGID.insert(codeToGID.begin(), src, src + n);
             }
         } else {
             std::unique_ptr<FoFiTrueType> ff;
             if (!font_data.empty()) {
                 ff = FoFiTrueType::make(font_data.data(), font_data.size());
             } else {
-                ff = FoFiTrueType::load(fileNameC);
+                ff = FoFiTrueType::load(fileName.c_str());
             }
             if (!ff) {
                 goto err2;
             }
-            codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff.get(), &n);
+            int *src = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCodeToGIDMap(ff.get(), &n);
+            codeToGID.reserve(n);
+            codeToGID.insert(codeToGID.begin(), src, src + n);
+            gfree(src);
         }
-        codeToGIDLen = n;
         /* Fall through */
     case fontTrueType:
     case fontTrueTypeOT: {
@@ -392,7 +303,7 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, FT_Li
         if (!font_data.empty()) {
             ff = FoFiTrueType::make(font_data.data(), font_data.size());
         } else {
-            ff = FoFiTrueType::load(fileNameC);
+            ff = FoFiTrueType::load(fileName.c_str());
         }
         if (!ff) {
             error(errSyntaxError, -1, "failed to load truetype font\n");
@@ -400,10 +311,13 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, FT_Li
         }
         /* This might be set already for the CIDType2 case */
         if (fontType == fontTrueType || fontType == fontTrueTypeOT) {
-            codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff.get());
-            codeToGIDLen = 256;
+            int *src = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getCodeToGIDMap(ff.get());
+            codeToGID.reserve(256);
+            codeToGID.insert(codeToGID.begin(), src, src + 256);
+            gfree(src);
         }
-        if (!_ft_new_face(lib, fileNameC, embFontID, std::move(font_data), &face, &font_face)) {
+        font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data));
+        if (!font_face) {
             error(errSyntaxError, -1, "could not create truetype face\n");
             goto err2;
         }
@@ -411,56 +325,58 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, FT_Li
     }
     case fontCIDType0:
     case fontCIDType0C:
-
-        codeToGID = nullptr;
-        codeToGIDLen = 0;
-
         if (!useCIDs) {
             if (!font_data.empty()) {
                 ff1c = FoFiType1C::make(font_data.data(), font_data.size());
             } else {
-                ff1c = FoFiType1C::load(fileNameC);
+                ff1c = FoFiType1C::load(fileName.c_str());
             }
             if (ff1c) {
-                codeToGID = ff1c->getCIDToGIDMap((int *)&codeToGIDLen);
+                int *src = ff1c->getCIDToGIDMap(&n);
+                codeToGID.reserve(n);
+                codeToGID.insert(codeToGID.begin(), src, src + n);
+                gfree(src);
                 delete ff1c;
             }
         }
 
-        if (!_ft_new_face(lib, fileNameC, embFontID, std::move(font_data), &face, &font_face)) {
+        font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data));
+        if (!font_face) {
             error(errSyntaxError, -1, "could not create cid face\n");
             goto err2;
         }
         break;
 
     case fontCIDType0COT:
-        codeToGID = nullptr;
-        n = 0;
-        if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
-            n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
+        if (std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID()) {
+            n = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGIDLen();
             if (n) {
-                codeToGID = (int *)gmallocn(n, sizeof(int));
-                memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int));
+                const int *src = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID();
+                codeToGID.reserve(n);
+                codeToGID.insert(codeToGID.begin(), src, src + n);
             }
         }
-        codeToGIDLen = n;
 
-        if (!codeToGID) {
+        if (codeToGID.empty()) {
             if (!useCIDs) {
                 std::unique_ptr<FoFiTrueType> ff;
                 if (!font_data.empty()) {
                     ff = FoFiTrueType::make(font_data.data(), font_data.size());
                 } else {
-                    ff = FoFiTrueType::load(fileNameC);
+                    ff = FoFiTrueType::load(fileName.c_str());
                 }
                 if (ff) {
                     if (ff->isOpenTypeCFF()) {
-                        codeToGID = ff->getCIDToGIDMap((int *)&codeToGIDLen);
+                        int *src = ff->getCIDToGIDMap(&n);
+                        codeToGID.reserve(n);
+                        codeToGID.insert(codeToGID.begin(), src, src + n);
+                        gfree(src);
                     }
                 }
             }
         }
-        if (!_ft_new_face(lib, fileNameC, embFontID, std::move(font_data), &face, &font_face)) {
+        font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data));
+        if (!font_face) {
             error(errSyntaxError, -1, "could not create cid (OT) face\n");
             goto err2;
         }
@@ -472,10 +388,9 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, FT_Li
         break;
     }
 
-    return new CairoFreeTypeFont(ref, font_face, codeToGID, codeToGIDLen, substitute);
+    return new CairoFreeTypeFont(ref, font_face->cairo_font_face, std::move(codeToGID), substitute);
 
 err2:
-    gfree(codeToGID);
     fprintf(stderr, "some font thing failed\n");
     return nullptr;
 }
@@ -488,9 +403,9 @@ static const cairo_user_data_key_t type3_font_key = { 0 };
 
 typedef struct _type3_font_info
 {
-    _type3_font_info(const std::shared_ptr<const GfxFont> &fontA, PDFDoc *docA, CairoFontEngine *fontEngineA, bool printingA, XRef *xrefA) : font(fontA), doc(docA), fontEngine(fontEngineA), printing(printingA), xref(xrefA) { }
+    _type3_font_info(const std::shared_ptr<GfxFont> &fontA, PDFDoc *docA, CairoFontEngine *fontEngineA, bool printingA, XRef *xrefA) : font(fontA), doc(docA), fontEngine(fontEngineA), printing(printingA), xref(xrefA) { }
 
-    std::shared_ptr<const GfxFont> font;
+    std::shared_ptr<GfxFont> font;
     PDFDoc *doc;
     CairoFontEngine *fontEngine;
     bool printing;
@@ -508,8 +423,7 @@ static cairo_status_t _init_type3_glyph(cairo_scaled_font_t *scaled_font, cairo_
     type3_font_info_t *info;
 
     info = (type3_font_info_t *)cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key);
-    std::shared_ptr<const GfxFont> font = info->font;
-    const double *mat = font->getFontBBox();
+    const double *mat = info->font->getFontBBox();
     extents->ascent = mat[3]; /* y2 */
     extents->descent = -mat[3]; /* -y1 */
     extents->height = extents->ascent + extents->descent;
@@ -534,9 +448,8 @@ static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsi
 
     info = (type3_font_info_t *)cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key);
 
-    std::shared_ptr<const GfxFont> font = info->font;
-    Dict *resDict = ((Gfx8BitFont *)font.get())->getResources();
-    charProcs = ((Gfx8BitFont *)(info->font.get()))->getCharProcs();
+    Dict *resDict = std::static_pointer_cast<Gfx8BitFont>(info->font)->getResources();
+    charProcs = std::static_pointer_cast<Gfx8BitFont>(info->font)->getCharProcs();
     if (!charProcs) {
         return CAIRO_STATUS_USER_FONT_ERROR;
     }
@@ -545,7 +458,7 @@ static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsi
         return CAIRO_STATUS_USER_FONT_ERROR;
     }
 
-    mat = font->getFontMatrix();
+    mat = info->font->getFontMatrix();
     matrix.xx = mat[0];
     matrix.yx = mat[1];
     matrix.xy = mat[2];
@@ -560,7 +473,7 @@ static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsi
     output_dev->setCairo(cr);
     output_dev->setPrinting(info->printing);
 
-    mat = font->getFontBBox();
+    mat = info->font->getFontBBox();
     box.x1 = mat[0];
     box.y1 = mat[1];
     box.x2 = mat[2];
@@ -614,18 +527,14 @@ static cairo_status_t _render_type3_noncolor_glyph(cairo_scaled_font_t *scaled_f
     return _render_type3_glyph(scaled_font, glyph, cr, metrics, false);
 }
 
-CairoType3Font *CairoType3Font::create(const std::shared_ptr<const GfxFont> &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref)
+CairoType3Font *CairoType3Font::create(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref)
 {
-    cairo_font_face_t *font_face;
-    Ref ref;
-    int *codeToGID;
-    unsigned int codeToGIDLen;
-    int i, j;
+    std::vector<int> codeToGID;
     char *name;
 
-    Dict *charProcs = ((Gfx8BitFont *)gfxFont.get())->getCharProcs();
-    ref = *gfxFont->getID();
-    font_face = cairo_user_font_face_create();
+    Dict *charProcs = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getCharProcs();
+    Ref ref = *gfxFont->getID();
+    cairo_font_face_t *font_face = cairo_user_font_face_create();
     cairo_user_font_face_set_init_func(font_face, _init_type3_glyph);
 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 6)
     // When both callbacks are set, Cairo will call the color glyph
@@ -638,13 +547,12 @@ CairoType3Font *CairoType3Font::create(const std::shared_ptr<const GfxFont> &gfx
 
     cairo_font_face_set_user_data(font_face, &type3_font_key, (void *)info, _free_type3_font_info);
 
-    char **enc = ((Gfx8BitFont *)gfxFont.get())->getEncoding();
-    codeToGID = (int *)gmallocn(256, sizeof(int));
-    codeToGIDLen = 256;
-    for (i = 0; i < 256; ++i) {
+    char **enc = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getEncoding();
+    codeToGID.resize(256);
+    for (int i = 0; i < 256; ++i) {
         codeToGID[i] = 0;
         if (charProcs && (name = enc[i])) {
-            for (j = 0; j < charProcs->getLength(); j++) {
+            for (int j = 0; j < charProcs->getLength(); j++) {
                 if (strcmp(name, charProcs->getKey(j)) == 0) {
                     codeToGID[i] = j;
                 }
@@ -652,10 +560,10 @@ CairoType3Font *CairoType3Font::create(const std::shared_ptr<const GfxFont> &gfx
         }
     }
 
-    return new CairoType3Font(ref, font_face, codeToGID, codeToGIDLen, printing, xref);
+    return new CairoType3Font(ref, font_face, std::move(codeToGID), printing, xref);
 }
 
-CairoType3Font::CairoType3Font(Ref refA, cairo_font_face_t *cairo_font_faceA, int *codeToGIDA, unsigned int codeToGIDLenA, bool printingA, XRef *xref) : CairoFont(refA, cairo_font_faceA, codeToGIDA, codeToGIDLenA, false, printingA) { }
+CairoType3Font::CairoType3Font(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector<int> &&codeToGIDA, bool printingA, XRef *xref) : CairoFont(refA, cairo_font_faceA, std::move(codeToGIDA), false, printingA) { }
 
 CairoType3Font::~CairoType3Font() { }
 
@@ -668,16 +576,13 @@ bool CairoType3Font::matches(Ref &other, bool printingA)
 // CairoFontEngine
 //------------------------------------------------------------------------
 
-#define fontEngineLocker() const std::scoped_lock locker(mutex)
+std::unordered_map<std::string, FreeTypeFontFace> CairoFontEngine::fontFileCache;
+std::recursive_mutex CairoFontEngine::fontFileCacheMutex;
 
 CairoFontEngine::CairoFontEngine(FT_Library libA)
 {
-    int i;
-
     lib = libA;
-    for (i = 0; i < cairoFontCacheSize; ++i) {
-        fontCache[i] = nullptr;
-    }
+    fontCache.reserve(cairoFontCacheSize);
 
     FT_Int major, minor, patch;
     // as of FT 2.1.8, CID fonts are indexed by CID instead of GID
@@ -685,52 +590,68 @@ CairoFontEngine::CairoFontEngine(FT_Library libA)
     useCIDs = major > 2 || (major == 2 && (minor > 1 || (minor == 1 && patch > 7)));
 }
 
-CairoFontEngine::~CairoFontEngine()
-{
-    int i;
-
-    for (i = 0; i < cairoFontCacheSize; ++i) {
-        if (fontCache[i]) {
-            delete fontCache[i];
-        }
-    }
-}
+CairoFontEngine::~CairoFontEngine() { }
 
-CairoFont *CairoFontEngine::getFont(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, bool printing, XRef *xref)
+std::shared_ptr<CairoFont> CairoFontEngine::getFont(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, bool printing, XRef *xref)
 {
-    int i, j;
-    Ref ref;
-    CairoFont *font;
-    GfxFontType fontType;
-
-    fontEngineLocker();
-    ref = *gfxFont->getID();
-
-    for (i = 0; i < cairoFontCacheSize; ++i) {
-        font = fontCache[i];
-        if (font && font->matches(ref, printing)) {
-            for (j = i; j > 0; --j) {
-                fontCache[j] = fontCache[j - 1];
+    std::scoped_lock lock(mutex);
+    Ref ref = *gfxFont->getID();
+    std::shared_ptr<CairoFont> font;
+
+    // Check if font is in the MRU cache, and move it to the end if it is.
+    for (auto it = fontCache.rbegin(); it != fontCache.rend(); ++it) {
+        if ((*it)->matches(ref, printing)) {
+            font = *it;
+            // move it to the end
+            if (it != fontCache.rbegin()) {
+                // https://stackoverflow.com/questions/1830158/how-to-call-erase-with-a-reverse-iterator
+                fontCache.erase(std::next(it).base());
+                fontCache.push_back(font);
             }
-            fontCache[0] = font;
             return font;
         }
     }
 
-    fontType = gfxFont->getType();
+    GfxFontType fontType = gfxFont->getType();
     if (fontType == fontType3) {
-        font = CairoType3Font::create(gfxFont, doc, this, printing, xref);
+        font = std::shared_ptr<CairoFont>(CairoType3Font::create(gfxFont, doc, this, printing, xref));
     } else {
-        font = CairoFreeTypeFont::create(gfxFont.get(), xref, lib, useCIDs);
+        font = std::shared_ptr<CairoFont>(CairoFreeTypeFont::create(gfxFont, xref, lib, this, useCIDs));
     }
 
-    // XXX: if font is null should we still insert it into the cache?
-    if (fontCache[cairoFontCacheSize - 1]) {
-        delete fontCache[cairoFontCacheSize - 1];
-    }
-    for (j = cairoFontCacheSize - 1; j > 0; --j) {
-        fontCache[j] = fontCache[j - 1];
+    if (font) {
+        if (fontCache.size() == cairoFontCacheSize) {
+            fontCache.erase(fontCache.begin());
+        }
+        fontCache.push_back(font);
     }
-    fontCache[0] = font;
     return font;
 }
+
+std::optional<FreeTypeFontFace> CairoFontEngine::getExternalFontFace(FT_Library ftlib, const std::string &filename)
+{
+    std::scoped_lock lock(fontFileCacheMutex);
+
+    auto it = fontFileCache.find(filename);
+    if (it != fontFileCache.end()) {
+        return it->second;
+    }
+
+    std::optional<FreeTypeFontFace> font_face = createFreeTypeFontFace(ftlib, filename, {});
+    if (font_face) {
+        cairo_font_face_reference(font_face->cairo_font_face);
+        fontFileCache[filename] = *font_face;
+    }
+
+    it = fontFileCache.begin();
+    while (it != fontFileCache.end()) {
+        if (cairo_font_face_get_reference_count(it->second.cairo_font_face) == 1) {
+            cairo_font_face_destroy(it->second.cairo_font_face);
+            it = fontFileCache.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    return font_face;
+}
diff --git a/poppler/CairoFontEngine.h b/poppler/CairoFontEngine.h
index 385406c9..56cc8825 100644
--- a/poppler/CairoFontEngine.h
+++ b/poppler/CairoFontEngine.h
@@ -31,7 +31,10 @@
 #ifndef CAIROFONTENGINE_H
 #define CAIROFONTENGINE_H
 
+#include <memory>
 #include <mutex>
+#include <unordered_map>
+#include <vector>
 
 #include "poppler-config.h"
 #include <cairo-ft.h>
@@ -44,7 +47,7 @@ class CairoFontEngine;
 class CairoFont
 {
 public:
-    CairoFont(Ref refA, cairo_font_face_t *cairo_font_faceA, int *codeToGIDA, unsigned int codeToGIDLenA, bool substituteA, bool printingA);
+    CairoFont(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector<int> &&codeToGIDA, bool substituteA, bool printingA);
     virtual ~CairoFont();
     CairoFont(const CairoFont &) = delete;
     CairoFont &operator=(const CairoFont &other) = delete;
@@ -52,7 +55,7 @@ public:
     virtual bool matches(Ref &other, bool printing);
     cairo_font_face_t *getFontFace();
     unsigned long getGlyph(CharCode code, const Unicode *u, int uLen);
-    double getSubstitutionCorrection(GfxFont *gfxFont);
+    double getSubstitutionCorrection(const std::shared_ptr<GfxFont> &gfxFont);
 
     bool isSubstitute() { return substitute; }
 
@@ -60,8 +63,7 @@ protected:
     Ref ref;
     cairo_font_face_t *cairo_font_face;
 
-    int *codeToGID;
-    unsigned int codeToGIDLen;
+    std::vector<int> codeToGID;
 
     bool substitute;
     bool printing;
@@ -69,14 +71,22 @@ protected:
 
 //------------------------------------------------------------------------
 
+struct FreeTypeFontFace
+{
+    FT_Face face;
+    cairo_font_face_t *cairo_font_face;
+};
+
 class CairoFreeTypeFont : public CairoFont
 {
 public:
-    static CairoFreeTypeFont *create(GfxFont *gfxFont, XRef *xref, FT_Library lib, bool useCIDs);
+    static CairoFreeTypeFont *create(const std::shared_ptr<GfxFont> &gfxFont, XRef *xref, FT_Library lib, CairoFontEngine *fontEngine, bool useCIDs);
     ~CairoFreeTypeFont() override;
 
 private:
-    CairoFreeTypeFont(Ref ref, cairo_font_face_t *cairo_font_face, int *codeToGID, unsigned int codeToGIDLen, bool substitute);
+    CairoFreeTypeFont(Ref ref, cairo_font_face_t *cairo_font_face, std::vector<int> &&codeToGID, bool substitute);
+
+    static std::optional<FreeTypeFontFace> getFreeTypeFontFace(CairoFontEngine *fontEngine, FT_Library lib, const std::string &filename, std::vector<unsigned char> &&data);
 };
 
 //------------------------------------------------------------------------
@@ -84,19 +94,17 @@ private:
 class CairoType3Font : public CairoFont
 {
 public:
-    static CairoType3Font *create(const std::shared_ptr<const GfxFont> &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref);
+    static CairoType3Font *create(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref);
     ~CairoType3Font() override;
 
     bool matches(Ref &other, bool printing) override;
 
 private:
-    CairoType3Font(Ref ref, cairo_font_face_t *cairo_font_face, int *codeToGID, unsigned int codeToGIDLen, bool printing, XRef *xref);
+    CairoType3Font(Ref ref, cairo_font_face_t *cairo_font_face, std::vector<int> &&codeToGIDA, bool printing, XRef *xref);
 };
 
 //------------------------------------------------------------------------
 
-#define cairoFontCacheSize 64
-
 //------------------------------------------------------------------------
 // CairoFontEngine
 //------------------------------------------------------------------------
@@ -110,13 +118,23 @@ public:
     CairoFontEngine(const CairoFontEngine &) = delete;
     CairoFontEngine &operator=(const CairoFontEngine &other) = delete;
 
-    CairoFont *getFont(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, bool printing, XRef *xref);
+    std::shared_ptr<CairoFont> getFont(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, bool printing, XRef *xref);
+
+    static std::optional<FreeTypeFontFace> getExternalFontFace(FT_Library ftlib, const std::string &filename);
 
 private:
-    CairoFont *fontCache[cairoFontCacheSize];
     FT_Library lib;
     bool useCIDs;
-    mutable std::recursive_mutex mutex;
+    mutable std::mutex mutex;
+
+    // Cache of CairoFont for current document
+    // Most recently used is at the end of the vector.
+    static const size_t cairoFontCacheSize = 64;
+    std::vector<std::shared_ptr<CairoFont>> fontCache;
+
+    // Global cache of cairo_font_face_t for external font files.
+    static std::unordered_map<std::string, FreeTypeFontFace> fontFileCache;
+    static std::recursive_mutex fontFileCacheMutex;
 };
 
 #endif
diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index 5437687d..ef3be8e2 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -761,7 +761,7 @@ void CairoOutputDev::updateFont(GfxState *state)
     /* NOTE: adjusting by a constant is hack. The correct solution
      * is probably to use user-fonts and compute the scale on a per
      * glyph basis instead of for the entire font */
-    double w = currentFont->getSubstitutionCorrection(state->getFont().get());
+    double w = currentFont->getSubstitutionCorrection(state->getFont());
     matrix.xx = m[0] * fontSize * state->getHorizScaling() * w;
     matrix.yx = m[1] * fontSize * state->getHorizScaling() * w;
     matrix.xy = -m[2] * fontSize;
diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h
index 8f64668f..791e9bc3 100644
--- a/poppler/CairoOutputDev.h
+++ b/poppler/CairoOutputDev.h
@@ -277,7 +277,7 @@ protected:
     bool stroke_adjust;
     bool adjusted_stroke_width;
     bool align_stroke_coords;
-    CairoFont *currentFont;
+    std::shared_ptr<CairoFont> currentFont;
     XRef *xref;
 
     struct StrokePathClip


More information about the poppler mailing list