Spaces:
Sleeping
Sleeping
//======================================================================== | |
// | |
// CairoFontEngine.cc | |
// | |
// Copyright 2003 Glyph & Cog, LLC | |
// Copyright 2004 Red Hat, Inc | |
// | |
//======================================================================== | |
//======================================================================== | |
// | |
// Modified under the Poppler project - http://poppler.freedesktop.org | |
// | |
// All changes made under the Poppler project to this file are licensed | |
// under GPL version 2 or later | |
// | |
// Copyright (C) 2005-2007 Jeff Muizelaar <[email protected]> | |
// Copyright (C) 2005, 2006 Kristian Høgsberg <[email protected]> | |
// Copyright (C) 2005 Martin Kretzschmar <[email protected]> | |
// Copyright (C) 2005, 2009, 2012, 2013, 2015, 2017-2019, 2021, 2022 Albert Astals Cid <[email protected]> | |
// Copyright (C) 2006, 2007, 2010, 2011 Carlos Garcia Campos <[email protected]> | |
// Copyright (C) 2007 Koji Otani <[email protected]> | |
// Copyright (C) 2008, 2009 Chris Wilson <[email protected]> | |
// Copyright (C) 2008, 2012, 2014, 2016, 2017, 2022, 2023 Adrian Johnson <[email protected]> | |
// Copyright (C) 2009 Darren Kenny <[email protected]> | |
// Copyright (C) 2010 Suzuki Toshiya <[email protected]> | |
// Copyright (C) 2010 Jan Kümmel <[email protected]> | |
// Copyright (C) 2012 Hib Eris <[email protected]> | |
// Copyright (C) 2013 Thomas Freitag <[email protected]> | |
// Copyright (C) 2015, 2016 Jason Crain <[email protected]> | |
// Copyright (C) 2018 Adam Reichold <[email protected]> | |
// Copyright (C) 2019 Christian Persch <[email protected]> | |
// Copyright (C) 2020 Michal <[email protected]> | |
// Copyright (C) 2021, 2022 Oliver Sander <[email protected]> | |
// Copyright (C) 2022 Marcel Fabian Krüger <[email protected]> | |
// Copyright (C) 2023 Pablo Correa Gómez <[email protected]> | |
// Copyright (C) 2023 Frederic Germain <[email protected]> | |
// Copyright (C) 2023 Ilia Kats <[email protected]> | |
// | |
// To see a description of the changes please see the Changelog file that | |
// came with your tarball or type make ChangeLog if you are building from git | |
// | |
//======================================================================== | |
//------------------------------------------------------------------------ | |
// CairoFont | |
//------------------------------------------------------------------------ | |
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); | |
} | |
bool CairoFont::matches(Ref &other, bool printingA) | |
{ | |
return (other == ref); | |
} | |
cairo_font_face_t *CairoFont::getFontFace() | |
{ | |
return cairo_font_face; | |
} | |
unsigned long CairoFont::getGlyph(CharCode code, const Unicode *u, int uLen) | |
{ | |
FT_UInt gid; | |
if (code < codeToGID.size()) { | |
gid = (FT_UInt)codeToGID[code]; | |
} else { | |
gid = (FT_UInt)code; | |
} | |
return gid; | |
} | |
double CairoFont::getSubstitutionCorrection(const std::shared_ptr<GfxFont> &gfxFont) | |
{ | |
double w1, w2, w3; | |
CharCode code; | |
const char *name; | |
// for substituted fonts: adjust the font matrix -- compare the | |
// width of 'm' in the original font and the substituted font | |
if (isSubstitute() && !gfxFont->isCIDFont()) { | |
for (code = 0; code < 256; ++code) { | |
if ((name = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') { | |
break; | |
} | |
} | |
if (code < 256) { | |
w1 = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getWidth(code); | |
{ | |
cairo_matrix_t m; | |
cairo_matrix_init_identity(&m); | |
cairo_font_options_t *options = cairo_font_options_create(); | |
cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); | |
cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF); | |
cairo_scaled_font_t *scaled_font = cairo_scaled_font_create(cairo_font_face, &m, &m, options); | |
cairo_text_extents_t extents; | |
cairo_scaled_font_text_extents(scaled_font, "m", &extents); | |
cairo_scaled_font_destroy(scaled_font); | |
cairo_font_options_destroy(options); | |
w2 = extents.x_advance; | |
} | |
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 | |
if (w1 > 0.01 && w1 < 0.9 * w2) { | |
w1 /= w2; | |
return w1; | |
} | |
} | |
} | |
} | |
return 1.0; | |
} | |
//------------------------------------------------------------------------ | |
// CairoFreeTypeFont | |
//------------------------------------------------------------------------ | |
static cairo_user_data_key_t ft_cairo_key; | |
// Font resources to be freed when cairo_font_face_t is destroyed | |
struct FreeTypeFontResource | |
{ | |
FT_Face face; | |
std::vector<unsigned char> font_data; | |
}; | |
// cairo callback for when cairo_font_face_t is destroyed | |
static void _ft_done_face(void *closure) | |
{ | |
FreeTypeFontResource *resource = (FreeTypeFontResource *)closure; | |
FT_Done_Face(resource->face); | |
delete resource; | |
} | |
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) { } | |
CairoFreeTypeFont::~CairoFreeTypeFont() { } | |
// 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; | |
if (font_data.empty()) { | |
FT_Error err = ft_new_face_from_file(lib, filename.c_str(), 0, &resource->face); | |
if (err) { | |
delete resource; | |
return {}; | |
} | |
} else { | |
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 {}; | |
} | |
} | |
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 {}; | |
} | |
font_face.face = resource->face; | |
return font_face; | |
} | |
// 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); | |
} | |
return createFreeTypeFontFace(lib, filename, std::move(font_data)); | |
} | |
CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr<GfxFont> &gfxFont, XRef *xref, FT_Library lib, CairoFontEngine *fontEngine, bool useCIDs) | |
{ | |
std::string fileName; | |
std::vector<unsigned char> font_data; | |
int i, n; | |
std::optional<GfxFontLoc> fontLoc; | |
char **enc; | |
const char *name; | |
FoFiType1C *ff1c; | |
std::optional<FreeTypeFontFace> font_face; | |
std::vector<int> codeToGID; | |
bool substitute = false; | |
Ref ref = *gfxFont->getID(); | |
Ref embFontID = Ref::INVALID(); | |
gfxFont->getEmbeddedFontID(&embFontID); | |
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)"); | |
goto err2; | |
} | |
// embedded font | |
if (fontLoc->locType == gfxFontLocEmbedded) { | |
auto fd = gfxFont->readEmbFontFile(xref); | |
if (!fd || fd->empty()) { | |
goto err2; | |
} | |
font_data = std::move(fd.value()); | |
// external font | |
} else { // gfxFontLocExternal | |
fileName = fontLoc->path; | |
fontType = fontLoc->fontType; | |
substitute = true; | |
} | |
switch (fontType) { | |
case fontType1: | |
case fontType1C: | |
case fontType1COT: | |
font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); | |
if (!font_face) { | |
error(errSyntaxError, -1, "could not create type1 face"); | |
goto err2; | |
} | |
enc = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getEncoding(); | |
codeToGID.resize(256); | |
for (i = 0; i < 256; ++i) { | |
codeToGID[i] = 0; | |
if ((name = enc[i])) { | |
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(font_face->face, u); | |
} | |
if (codeToGID[i] == 0) { | |
name = GfxFont::getAlternateName(name); | |
if (name) { | |
codeToGID[i] = FT_Get_Name_Index(font_face->face, (char *)name); | |
} | |
} | |
} | |
} | |
break; | |
case fontCIDType2: | |
case fontCIDType2OT: | |
if (std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID()) { | |
n = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGIDLen(); | |
if (n) { | |
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(fileName.c_str()); | |
} | |
if (!ff) { | |
goto err2; | |
} | |
int *src = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCodeToGIDMap(ff.get(), &n); | |
codeToGID.reserve(n); | |
codeToGID.insert(codeToGID.begin(), src, src + n); | |
gfree(src); | |
} | |
/* Fall through */ | |
case fontTrueType: | |
case fontTrueTypeOT: { | |
std::unique_ptr<FoFiTrueType> ff; | |
if (!font_data.empty()) { | |
ff = FoFiTrueType::make(font_data.data(), font_data.size()); | |
} else { | |
ff = FoFiTrueType::load(fileName.c_str()); | |
} | |
if (!ff) { | |
error(errSyntaxError, -1, "failed to load truetype font\n"); | |
goto err2; | |
} | |
/* This might be set already for the CIDType2 case */ | |
if (fontType == fontTrueType || fontType == fontTrueTypeOT) { | |
int *src = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getCodeToGIDMap(ff.get()); | |
codeToGID.reserve(256); | |
codeToGID.insert(codeToGID.begin(), src, src + 256); | |
gfree(src); | |
} | |
font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); | |
if (!font_face) { | |
error(errSyntaxError, -1, "could not create truetype face\n"); | |
goto err2; | |
} | |
break; | |
} | |
case fontCIDType0: | |
case fontCIDType0C: | |
if (!useCIDs) { | |
if (!font_data.empty()) { | |
ff1c = FoFiType1C::make(font_data.data(), font_data.size()); | |
} else { | |
ff1c = FoFiType1C::load(fileName.c_str()); | |
} | |
if (ff1c) { | |
int *src = ff1c->getCIDToGIDMap(&n); | |
codeToGID.reserve(n); | |
codeToGID.insert(codeToGID.begin(), src, src + n); | |
gfree(src); | |
delete ff1c; | |
} | |
} | |
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: | |
if (std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID()) { | |
n = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGIDLen(); | |
if (n) { | |
const int *src = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID(); | |
codeToGID.reserve(n); | |
codeToGID.insert(codeToGID.begin(), src, src + n); | |
} | |
} | |
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(fileName.c_str()); | |
} | |
if (ff) { | |
if (ff->isOpenTypeCFF()) { | |
int *src = ff->getCIDToGIDMap(&n); | |
codeToGID.reserve(n); | |
codeToGID.insert(codeToGID.begin(), src, src + n); | |
gfree(src); | |
} | |
} | |
} | |
} | |
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; | |
} | |
break; | |
default: | |
fprintf(stderr, "font type %d not handled\n", (int)fontType); | |
goto err2; | |
break; | |
} | |
return new CairoFreeTypeFont(ref, font_face->cairo_font_face, std::move(codeToGID), substitute); | |
err2: | |
fprintf(stderr, "some font thing failed\n"); | |
return nullptr; | |
} | |
//------------------------------------------------------------------------ | |
// CairoType3Font | |
//------------------------------------------------------------------------ | |
static const cairo_user_data_key_t type3_font_key = { 0 }; | |
typedef struct _type3_font_info | |
{ | |
_type3_font_info(const std::shared_ptr<GfxFont> &fontA, PDFDoc *docA, CairoFontEngine *fontEngineA, CairoOutputDev *outputDevA, Gfx *gfxA) : font(fontA), doc(docA), fontEngine(fontEngineA), outputDev(outputDevA), gfx(gfxA) { } | |
std::shared_ptr<GfxFont> font; | |
PDFDoc *doc; | |
CairoFontEngine *fontEngine; | |
CairoOutputDev *outputDev; | |
Gfx *gfx; | |
} type3_font_info_t; | |
static void _free_type3_font_info(void *closure) | |
{ | |
type3_font_info_t *info = (type3_font_info_t *)closure; | |
delete info->gfx; | |
delete info->outputDev; | |
delete info; | |
} | |
static cairo_status_t _init_type3_glyph(cairo_scaled_font_t *scaled_font, cairo_t *cr, cairo_font_extents_t *extents) | |
{ | |
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); | |
const double *mat = info->font->getFontBBox(); | |
extents->ascent = mat[3]; /* y2 */ | |
extents->descent = -mat[3]; /* -y1 */ | |
extents->height = extents->ascent + extents->descent; | |
extents->max_x_advance = mat[2] - mat[1]; /* x2 - x1 */ | |
extents->max_y_advance = 0; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics, bool color) | |
{ | |
Dict *charProcs; | |
Object charProc; | |
cairo_matrix_t matrix, invert_y_axis; | |
const double *mat; | |
double wx, wy; | |
type3_font_info_t *info; | |
Gfx *gfx; | |
cairo_status_t status; | |
info = (type3_font_info_t *)cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key); | |
charProcs = std::static_pointer_cast<Gfx8BitFont>(info->font)->getCharProcs(); | |
if (!charProcs) { | |
return CAIRO_STATUS_USER_FONT_ERROR; | |
} | |
if ((int)glyph >= charProcs->getLength()) { | |
return CAIRO_STATUS_USER_FONT_ERROR; | |
} | |
mat = info->font->getFontMatrix(); | |
matrix.xx = mat[0]; | |
matrix.yx = mat[1]; | |
matrix.xy = mat[2]; | |
matrix.yy = mat[3]; | |
matrix.x0 = mat[4]; | |
matrix.y0 = mat[5]; | |
cairo_matrix_init_scale(&invert_y_axis, 1, -1); | |
cairo_matrix_multiply(&matrix, &matrix, &invert_y_axis); | |
cairo_transform(cr, &matrix); | |
cairo_set_source(cr, cairo_user_scaled_font_get_foreground_marker(scaled_font)); | |
CairoOutputDev *output_dev = info->outputDev; | |
output_dev->setCairo(cr); | |
gfx = info->gfx; | |
gfx->saveState(); | |
output_dev->startDoc(info->doc, info->fontEngine); | |
output_dev->startType3Render(gfx->getState(), gfx->getXRef()); | |
output_dev->setType3RenderType(color ? CairoOutputDev::Type3RenderColor : CairoOutputDev::Type3RenderMask); | |
charProc = charProcs->getVal(glyph); | |
if (!charProc.isStream()) { | |
return CAIRO_STATUS_USER_FONT_ERROR; | |
} | |
Object charProcResObject = charProc.streamGetDict()->lookup("Resources"); | |
if (charProcResObject.isDict()) { | |
gfx->pushResources(charProcResObject.getDict()); | |
} | |
gfx->display(&charProc); | |
if (charProcResObject.isDict()) { | |
gfx->popResources(); | |
} | |
output_dev->getType3GlyphWidth(&wx, &wy); | |
cairo_matrix_transform_distance(&matrix, &wx, &wy); | |
metrics->x_advance = wx; | |
metrics->y_advance = wy; | |
if (output_dev->hasType3GlyphBBox()) { | |
double *bbox = output_dev->getType3GlyphBBox(); | |
cairo_matrix_transform_point(&matrix, &bbox[0], &bbox[1]); | |
cairo_matrix_transform_point(&matrix, &bbox[2], &bbox[3]); | |
metrics->x_bearing = bbox[0]; | |
metrics->y_bearing = bbox[1]; | |
metrics->width = bbox[2] - bbox[0]; | |
metrics->height = bbox[3] - bbox[1]; | |
} | |
status = CAIRO_STATUS_SUCCESS; | |
// If this is a render color glyph callback but the Type 3 glyph | |
// specified non-color, return NOT_IMPLEMENTED. Cairo will then | |
// call the render non-color glyph callback. | |
if (color && !output_dev->type3GlyphHasColor()) { | |
status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; | |
} | |
return status; | |
} | |
static cairo_status_t _render_type3_color_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics) | |
{ | |
return _render_type3_glyph(scaled_font, glyph, cr, metrics, true); | |
} | |
static cairo_status_t _render_type3_noncolor_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics) | |
{ | |
return _render_type3_glyph(scaled_font, glyph, cr, metrics, false); | |
} | |
CairoType3Font *CairoType3Font::create(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref) | |
{ | |
std::vector<int> codeToGID; | |
char *name; | |
const double *mat; | |
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); | |
// When both callbacks are set, Cairo will call the color glyph | |
// callback first. If that returns NOT_IMPLEMENTED, Cairo will | |
// then call the non-color glyph callback. | |
cairo_user_font_face_set_render_color_glyph_func(font_face, _render_type3_color_glyph); | |
cairo_user_font_face_set_render_glyph_func(font_face, _render_type3_noncolor_glyph); | |
CairoOutputDev *output_dev = new CairoOutputDev(); | |
output_dev->setPrinting(printing); | |
Dict *resDict = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getResources(); | |
mat = gfxFont->getFontBBox(); | |
PDFRectangle box; | |
box.x1 = mat[0]; | |
box.y1 = mat[1]; | |
box.x2 = mat[2]; | |
box.y2 = mat[3]; | |
Gfx *gfx = new Gfx(doc, output_dev, resDict, &box, nullptr); | |
type3_font_info_t *info = new type3_font_info_t(gfxFont, doc, fontEngine, output_dev, gfx); | |
cairo_font_face_set_user_data(font_face, &type3_font_key, (void *)info, _free_type3_font_info); | |
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 (int j = 0; j < charProcs->getLength(); j++) { | |
if (strcmp(name, charProcs->getKey(j)) == 0) { | |
codeToGID[i] = j; | |
} | |
} | |
} | |
} | |
return new CairoType3Font(ref, font_face, std::move(codeToGID), printing, xref); | |
} | |
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() { } | |
bool CairoType3Font::matches(Ref &other, bool printingA) | |
{ | |
return (other == ref && printing == printingA); | |
} | |
//------------------------------------------------------------------------ | |
// CairoFontEngine | |
//------------------------------------------------------------------------ | |
std::unordered_map<std::string, FreeTypeFontFace> CairoFontEngine::fontFileCache; | |
std::recursive_mutex CairoFontEngine::fontFileCacheMutex; | |
CairoFontEngine::CairoFontEngine(FT_Library libA) | |
{ | |
lib = libA; | |
fontCache.reserve(cairoFontCacheSize); | |
FT_Int major, minor, patch; | |
// as of FT 2.1.8, CID fonts are indexed by CID instead of GID | |
FT_Library_Version(lib, &major, &minor, &patch); | |
useCIDs = major > 2 || (major == 2 && (minor > 1 || (minor == 1 && patch > 7))); | |
} | |
CairoFontEngine::~CairoFontEngine() { } | |
std::shared_ptr<CairoFont> CairoFontEngine::getFont(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, bool printing, XRef *xref) | |
{ | |
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); | |
} | |
return font; | |
} | |
} | |
GfxFontType fontType = gfxFont->getType(); | |
if (fontType == fontType3) { | |
font = std::shared_ptr<CairoFont>(CairoType3Font::create(gfxFont, doc, this, printing, xref)); | |
} else { | |
font = std::shared_ptr<CairoFont>(CairoFreeTypeFont::create(gfxFont, xref, lib, this, useCIDs)); | |
} | |
if (font) { | |
if (fontCache.size() == cairoFontCacheSize) { | |
fontCache.erase(fontCache.begin()); | |
} | |
fontCache.push_back(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()) { | |
FreeTypeFontFace font = it->second; | |
cairo_font_face_reference(font.cairo_font_face); | |
return font; | |
} | |
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; | |
} | |