//======================================================================== // // 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 // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2005 Martin Kretzschmar // Copyright (C) 2005, 2009, 2012, 2013, 2015, 2017-2019 Albert Astals Cid // Copyright (C) 2006, 2007, 2010, 2011 Carlos Garcia Campos // Copyright (C) 2007 Koji Otani // Copyright (C) 2008, 2009 Chris Wilson // Copyright (C) 2008, 2012, 2014, 2016, 2017 Adrian Johnson // Copyright (C) 2009 Darren Kenny // Copyright (C) 2010 Suzuki Toshiya // Copyright (C) 2010 Jan Kümmel // Copyright (C) 2012 Hib Eris // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2015, 2016 Jason Crain // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Christian Persch // Copyright (C) 2020 Michal // // 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 // //======================================================================== #include #include "config.h" #include #include #include "CairoFontEngine.h" #include "CairoOutputDev.h" #include "GlobalParams.h" #include #include #include "goo/gfile.h" #include "Error.h" #include "XRef.h" #include "Gfx.h" #include "Page.h" #if defined(HAVE_FCNTL_H) && defined(HAVE_SYS_MMAN_H) && defined(HAVE_SYS_STAT_H) # include # include # include # define CAN_CHECK_OPEN_FACES 1 #endif //------------------------------------------------------------------------ // 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() { cairo_font_face_destroy(cairo_font_face); gfree(codeToGID); } 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 (codeToGID && code < codeToGIDLen) { gid = (FT_UInt)codeToGID[code]; } else { gid = (FT_UInt)code; } return gid; } double CairoFont::getSubstitutionCorrection(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 = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') { break; } } if (code < 256) { w1 = ((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 = ((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; 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, char *font_data, int font_data_len, FT_Face *face_out, cairo_font_face_t **font_face_out) { FT_Face face; cairo_font_face_t *font_face; if (font_data == nullptr) { if (FT_New_Face(lib, filename, 0, &face)) return false; } else { if (FT_New_Memory_Face(lib, (unsigned char *)font_data, font_data_len, 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; } #ifdef CAN_CHECK_OPEN_FACES struct _ft_face_data { int fd; unsigned long hash; size_t size; unsigned char *bytes; FT_Library lib; FT_Face face; cairo_font_face_t *font_face; }; class _FtFaceDataProxy { _ft_face_data *_data; public: _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); } operator _ft_face_data *() { return _data; } }; static thread_local std::forward_list<_FtFaceDataProxy> _local_open_faces; static unsigned long _djb_hash(const unsigned char *bytes, size_t len) { unsigned long hash = 5381; while (len--) { unsigned char c = *bytes++; hash *= 33; hash ^= c; } return hash; } static bool _ft_face_data_equal(struct _ft_face_data *a, struct _ft_face_data *b) { if (a->lib != b->lib) return false; if (a->size != b->size) return false; if (a->hash != b->hash) return false; return memcmp(a->bytes, b->bytes, a->size) == 0; } static void _ft_done_face(void *closure) { struct _ft_face_data *data = (struct _ft_face_data *)closure; if (data->fd != -1) { # if defined(__SUNPRO_CC) && defined(__sun) && defined(__SVR4) munmap((char *)data->bytes, data->size); # else munmap(data->bytes, data->size); # endif close(data->fd); } else { gfree(data->bytes); } FT_Done_Face(data->face); gfree(data); } static bool _ft_new_face(FT_Library lib, const char *filename, char *font_data, int font_data_len, FT_Face *face_out, cairo_font_face_t **font_face_out) { struct stat st; struct _ft_face_data tmpl; tmpl.fd = -1; if (font_data == nullptr) { /* if we fail to mmap the file, just pass it to FreeType instead */ tmpl.fd = openFileDescriptor(filename, O_RDONLY); if (tmpl.fd == -1) return _ft_new_face_uncached(lib, filename, font_data, font_data_len, face_out, font_face_out); if (fstat(tmpl.fd, &st) == -1) { close(tmpl.fd); return _ft_new_face_uncached(lib, filename, font_data, font_data_len, face_out, font_face_out); } tmpl.bytes = (unsigned char *)mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, tmpl.fd, 0); if (tmpl.bytes == MAP_FAILED) { close(tmpl.fd); return _ft_new_face_uncached(lib, filename, font_data, font_data_len, face_out, font_face_out); } tmpl.size = st.st_size; } else { tmpl.bytes = (unsigned char *)font_data; tmpl.size = font_data_len; } /* check to see if this is a duplicate of any of the currently open fonts */ tmpl.lib = lib; tmpl.hash = _djb_hash(tmpl.bytes, tmpl.size); for (_ft_face_data *l : _local_open_faces) { if (_ft_face_data_equal(l, &tmpl)) { if (tmpl.fd != -1) { # if defined(__SUNPRO_CC) && defined(__sun) && defined(__SVR4) munmap((char *)tmpl.bytes, tmpl.size); # else munmap(tmpl.bytes, tmpl.size); # endif close(tmpl.fd); } else { gfree(tmpl.bytes); } *face_out = l->face; *font_face_out = cairo_font_face_reference(l->font_face); return true; } } /* not a dup, open and insert into list */ if (FT_New_Memory_Face(lib, (FT_Byte *)tmpl.bytes, tmpl.size, 0, &tmpl.face)) { if (tmpl.fd != -1) { # if defined(__SUNPRO_CC) && defined(__sun) && defined(__SVR4) munmap((char *)tmpl.bytes, tmpl.size); # else munmap(tmpl.bytes, tmpl.size); # endif close(tmpl.fd); } return false; } struct _ft_face_data *l = (struct _ft_face_data *)gmallocn(1, sizeof(struct _ft_face_data)); *l = tmpl; l->font_face = cairo_ft_font_face_create_for_ft_face(tmpl.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; } _local_open_faces.remove_if([](_ft_face_data *data) { 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; } #else # define _ft_new_face _ft_new_face_uncached #endif 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) { } CairoFreeTypeFont::~CairoFreeTypeFont() { } CairoFreeTypeFont *CairoFreeTypeFont::create(GfxFont *gfxFont, XRef *xref, FT_Library lib, bool useCIDs) { GooString *fileName; const char *fileNameC; char *font_data; int font_data_len; int i, n; GfxFontType fontType; GfxFontLoc *fontLoc; char **enc; const char *name; FoFiTrueType *ff; FoFiType1C *ff1c; Ref ref; FT_Face face; cairo_font_face_t *font_face; int *codeToGID; unsigned int codeToGIDLen; codeToGID = nullptr; codeToGIDLen = 0; font_data = nullptr; font_data_len = 0; fileName = nullptr; fileNameC = nullptr; bool substitute = false; ref = *gfxFont->getID(); 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) { font_data = gfxFont->readEmbFontFile(xref, &font_data_len); if (nullptr == font_data) goto err2; // external font } else { // gfxFontLocExternal 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, font_data, font_data_len, &face, &font_face)) { error(errSyntaxError, -1, "could not create type1 face"); goto err2; } enc = ((Gfx8BitFont *)gfxFont)->getEncoding(); codeToGID = (int *)gmallocn(256, sizeof(int)); codeToGIDLen = 256; for (i = 0; i < 256; ++i) { codeToGID[i] = 0; if ((name = enc[i])) { codeToGID[i] = FT_Get_Name_Index(face, (char *)name); if (codeToGID[i] == 0) { Unicode u; u = globalParams->mapNameToUnicodeText(name); codeToGID[i] = FT_Get_Char_Index(face, u); } if (codeToGID[i] == 0) { name = GfxFont::getAlternateName(name); if (name) { codeToGID[i] = FT_Get_Name_Index(face, (char *)name); } } } } break; case fontCIDType2: case fontCIDType2OT: codeToGID = nullptr; n = 0; if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); if (n) { codeToGID = (int *)gmallocn(n, sizeof(int)); memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int)); } } else { if (font_data != nullptr) { ff = FoFiTrueType::make(font_data, font_data_len); } else { ff = FoFiTrueType::load(fileNameC); } if (!ff) goto err2; codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n); delete ff; } codeToGIDLen = n; /* Fall through */ case fontTrueType: case fontTrueTypeOT: if (font_data != nullptr) { ff = FoFiTrueType::make(font_data, font_data_len); } else { ff = FoFiTrueType::load(fileNameC); } 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) { codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); codeToGIDLen = 256; } delete ff; if (!_ft_new_face(lib, fileNameC, font_data, font_data_len, &face, &font_face)) { error(errSyntaxError, -1, "could not create truetype face\n"); goto err2; } break; case fontCIDType0: case fontCIDType0C: codeToGID = nullptr; codeToGIDLen = 0; if (!useCIDs) { if (font_data != nullptr) { ff1c = FoFiType1C::make(font_data, font_data_len); } else { ff1c = FoFiType1C::load(fileNameC); } if (ff1c) { codeToGID = ff1c->getCIDToGIDMap((int *)&codeToGIDLen); delete ff1c; } } if (!_ft_new_face(lib, fileNameC, font_data, font_data_len, &face, &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 (n) { codeToGID = (int *)gmallocn(n, sizeof(int)); memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int)); } } codeToGIDLen = n; if (!codeToGID) { if (!useCIDs) { if (font_data != nullptr) { ff = FoFiTrueType::make(font_data, font_data_len); } else { ff = FoFiTrueType::load(fileNameC); } if (ff) { if (ff->isOpenTypeCFF()) { codeToGID = ff->getCIDToGIDMap((int *)&codeToGIDLen); } delete ff; } } } if (!_ft_new_face(lib, fileNameC, font_data, font_data_len, &face, &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; } delete fontLoc; return new CairoFreeTypeFont(ref, font_face, codeToGID, codeToGIDLen, substitute); err2: /* hmm? */ delete fontLoc; gfree(codeToGID); gfree(font_data); 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 { GfxFont *font; PDFDoc *doc; CairoFontEngine *fontEngine; bool printing; XRef *xref; } type3_font_info_t; static void _free_type3_font_info(void *closure) { type3_font_info_t *info = (type3_font_info_t *)closure; info->font->decRefCnt(); free(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; GfxFont *font; info = (type3_font_info_t *)cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key); font = info->font; const double *mat = 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) { Dict *charProcs; Object charProc; CairoOutputDev *output_dev; cairo_matrix_t matrix, invert_y_axis; const double *mat; double wx, wy; PDFRectangle box; type3_font_info_t *info; GfxFont *font; Dict *resDict; Gfx *gfx; info = (type3_font_info_t *)cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key); font = info->font; resDict = ((Gfx8BitFont *)font)->getResources(); charProcs = ((Gfx8BitFont *)(info->font))->getCharProcs(); if (!charProcs) return CAIRO_STATUS_USER_FONT_ERROR; if ((int)glyph >= charProcs->getLength()) return CAIRO_STATUS_USER_FONT_ERROR; mat = 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); output_dev = new CairoOutputDev(); output_dev->setCairo(cr); output_dev->setPrinting(info->printing); mat = font->getFontBBox(); box.x1 = mat[0]; box.y1 = mat[1]; box.x2 = mat[2]; box.y2 = mat[3]; gfx = new Gfx(info->doc, output_dev, resDict, &box, nullptr); output_dev->startDoc(info->doc, info->fontEngine); output_dev->startPage(1, gfx->getState(), gfx->getXRef()); output_dev->setInType3Char(true); charProc = charProcs->getVal(glyph); gfx->display(&charProc); 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]; } delete gfx; delete output_dev; return CAIRO_STATUS_SUCCESS; } CairoType3Font *CairoType3Font::create(GfxFont *gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref) { type3_font_info_t *info; cairo_font_face_t *font_face; Ref ref; int *codeToGID; unsigned int codeToGIDLen; int i, j; char **enc; Dict *charProcs; char *name; charProcs = ((Gfx8BitFont *)gfxFont)->getCharProcs(); info = (type3_font_info_t *)malloc(sizeof(*info)); ref = *gfxFont->getID(); font_face = cairo_user_font_face_create(); cairo_user_font_face_set_init_func(font_face, _init_type3_glyph); cairo_user_font_face_set_render_glyph_func(font_face, _render_type3_glyph); gfxFont->incRefCnt(); info->font = gfxFont; info->doc = doc; info->fontEngine = fontEngine; info->printing = printing; info->xref = xref; cairo_font_face_set_user_data(font_face, &type3_font_key, (void *)info, _free_type3_font_info); enc = ((Gfx8BitFont *)gfxFont)->getEncoding(); codeToGID = (int *)gmallocn(256, sizeof(int)); codeToGIDLen = 256; for (i = 0; i < 256; ++i) { codeToGID[i] = 0; if (charProcs && (name = enc[i])) { for (j = 0; j < charProcs->getLength(); j++) { if (strcmp(name, charProcs->getKey(j)) == 0) { codeToGID[i] = j; } } } } return new CairoType3Font(ref, font_face, codeToGID, codeToGIDLen, 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() { } bool CairoType3Font::matches(Ref &other, bool printingA) { return (other == ref && printing == printingA); } //------------------------------------------------------------------------ // CairoFontEngine //------------------------------------------------------------------------ #define fontEngineLocker() std::unique_lock locker(mutex) CairoFontEngine::CairoFontEngine(FT_Library libA) { int i; lib = libA; for (i = 0; i < cairoFontCacheSize; ++i) { fontCache[i] = nullptr; } 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() { int i; for (i = 0; i < cairoFontCacheSize; ++i) { if (fontCache[i]) delete fontCache[i]; } } CairoFont *CairoFontEngine::getFont(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]; } fontCache[0] = font; return font; } } fontType = gfxFont->getType(); if (fontType == fontType3) font = CairoType3Font::create(gfxFont, doc, this, printing, xref); else font = CairoFreeTypeFont::create(gfxFont, xref, lib, 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]; } fontCache[0] = font; return font; }