| //======================================================================== |
| // |
| // 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 <jeff@infidigm.net> |
| // Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com> |
| // Copyright (C) 2005 Martin Kretzschmar <martink@gnome.org> |
| // Copyright (C) 2005, 2009, 2012, 2013, 2015, 2017-2019 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2006, 2007, 2010, 2011 Carlos Garcia Campos <carlosgc@gnome.org> |
| // Copyright (C) 2007 Koji Otani <sho@bbr.jp> |
| // Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk> |
| // Copyright (C) 2008, 2012, 2014, 2016, 2017 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2009 Darren Kenny <darren.kenny@sun.com> |
| // Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp> |
| // Copyright (C) 2010 Jan Kümmel <jan+freedesktop@snorc.org> |
| // Copyright (C) 2012 Hib Eris <hib@hiberis.nl> |
| // Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de> |
| // Copyright (C) 2015, 2016 Jason Crain <jason@aquaticape.us> |
| // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
| // Copyright (C) 2019 Christian Persch <chpe@src.gnome.org> |
| // |
| // 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 <config.h> |
| |
| #include "config.h" |
| #include <string.h> |
| #include "CairoFontEngine.h" |
| #include "CairoOutputDev.h" |
| #include "GlobalParams.h" |
| #include <fofi/FoFiTrueType.h> |
| #include <fofi/FoFiType1C.h> |
| #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 <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/mman.h> |
| #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(void) { |
| return cairo_font_face; |
| } |
| |
| unsigned long |
| CairoFont::getGlyph(CharCode code, |
| 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 |
| static struct _ft_face_data { |
| struct _ft_face_data *prev, *next, **head; |
| |
| int fd; |
| unsigned long hash; |
| size_t size; |
| unsigned char *bytes; |
| |
| FT_Library lib; |
| FT_Face face; |
| cairo_font_face_t *font_face; |
| } *_ft_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->next) |
| data->next->prev = data->prev; |
| if (data->prev) |
| data->prev->next = data->next; |
| else |
| _ft_open_faces = data->next; |
| |
| 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 _ft_face_data *l; |
| 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 (l = _ft_open_faces; l; l = l->next) { |
| 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; |
| } |
| |
| l = (struct _ft_face_data *) gmallocn (1, sizeof (struct _ft_face_data)); |
| *l = tmpl; |
| l->prev = nullptr; |
| l->next = _ft_open_faces; |
| if (_ft_open_faces) |
| _ft_open_faces->prev = l; |
| _ft_open_faces = l; |
| |
| 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; |
| } |
| |
| *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<std::recursive_mutex> 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; |
| } |