123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- #include <pango/pangocairo.h>
- #include <pango/pango-fontmap.h>
- #include <pango/pango.h>
-
- #ifdef __APPLE__
- #include <CoreText/CoreText.h>
- #elif defined(_WIN32)
- #include <windows.h>
- #else
- #include <fontconfig/fontconfig.h>
- #endif
-
- #include <ft2build.h>
- #include FT_FREETYPE_H
- #include FT_TRUETYPE_TABLES_H
- #include FT_SFNT_NAMES_H
- #include FT_TRUETYPE_IDS_H
- #ifndef FT_SFNT_OS2
- #define FT_SFNT_OS2 ft_sfnt_os2
- #endif
-
- // OSX seems to read the strings in MacRoman encoding and ignore Unicode entries.
- // You can verify this by opening a TTF with both Unicode and Macroman on OSX.
- // It uses the MacRoman name, while Fontconfig and Windows use Unicode
- #ifdef __APPLE__
- #define PREFERRED_PLATFORM_ID TT_PLATFORM_MACINTOSH
- #define PREFERRED_ENCODING_ID TT_MAC_ID_ROMAN
- #else
- #define PREFERRED_PLATFORM_ID TT_PLATFORM_MICROSOFT
- #define PREFERRED_ENCODING_ID TT_MS_ID_UNICODE_CS
- #endif
-
- #define IS_PREFERRED_ENC(X) \
- X.platform_id == PREFERRED_PLATFORM_ID && X.encoding_id == PREFERRED_ENCODING_ID
-
- #define GET_NAME_RANK(X) \
- (IS_PREFERRED_ENC(X) ? 1 : 0) + (X.name_id == TT_NAME_ID_PREFERRED_FAMILY ? 1 : 0)
-
- /*
- * Return a UTF-8 encoded string given a TrueType name buf+len
- * and its platform and encoding
- */
-
- char *
- to_utf8(FT_Byte* buf, FT_UInt len, FT_UShort pid, FT_UShort eid) {
- size_t ret_len = len * 4; // max chars in a utf8 string
- char *ret = (char*)malloc(ret_len + 1); // utf8 string + null
-
- if (!ret) return NULL;
-
- // In my testing of hundreds of fonts from the Google Font repo, the two types
- // of fonts are TT_PLATFORM_MICROSOFT with TT_MS_ID_UNICODE_CS encoding, or
- // TT_PLATFORM_MACINTOSH with TT_MAC_ID_ROMAN encoding. Usually both, never neither
-
- char const *fromcode;
-
- if (pid == TT_PLATFORM_MACINTOSH && eid == TT_MAC_ID_ROMAN) {
- fromcode = "MAC";
- } else if (pid == TT_PLATFORM_MICROSOFT && eid == TT_MS_ID_UNICODE_CS) {
- fromcode = "UTF-16BE";
- } else {
- free(ret);
- return NULL;
- }
-
- GIConv cd = g_iconv_open("UTF-8", fromcode);
-
- if (cd == (GIConv)-1) {
- free(ret);
- return NULL;
- }
-
- size_t inbytesleft = len;
- size_t outbytesleft = ret_len;
-
- size_t n_converted = g_iconv(cd, (char**)&buf, &inbytesleft, &ret, &outbytesleft);
-
- ret -= ret_len - outbytesleft; // rewind the pointers to their
- buf -= len - inbytesleft; // original starting positions
-
- if (n_converted == (size_t)-1) {
- free(ret);
- return NULL;
- } else {
- ret[ret_len - outbytesleft] = '\0';
- return ret;
- }
- }
-
- /*
- * Find a family name in the face's name table, preferring the one the
- * system, fall back to the other
- */
-
- typedef struct _NameDef {
- const char *buf;
- int rank; // the higher the more desirable
- } NameDef;
-
- gint
- _name_def_compare(gconstpointer a, gconstpointer b) {
- return ((NameDef*)a)->rank > ((NameDef*)b)->rank ? -1 : 1;
- }
-
- // Some versions of GTK+ do not have this, particualrly the one we
- // currently link to in node-canvas's wiki
- void
- _free_g_list_item(gpointer data, gpointer user_data) {
- NameDef *d = (NameDef *)data;
- free((void *)(d->buf));
- }
-
- void
- _g_list_free_full(GList *list) {
- g_list_foreach(list, _free_g_list_item, NULL);
- g_list_free(list);
- }
-
- char *
- get_family_name(FT_Face face) {
- FT_SfntName name;
- GList *list = NULL;
- char *utf8name = NULL;
-
- for (unsigned i = 0; i < FT_Get_Sfnt_Name_Count(face); ++i) {
- FT_Get_Sfnt_Name(face, i, &name);
-
- if (name.name_id == TT_NAME_ID_FONT_FAMILY || name.name_id == TT_NAME_ID_PREFERRED_FAMILY) {
- char *buf = to_utf8(name.string, name.string_len, name.platform_id, name.encoding_id);
-
- if (buf) {
- NameDef *d = (NameDef*)malloc(sizeof(NameDef));
- d->buf = (const char*)buf;
- d->rank = GET_NAME_RANK(name);
-
- list = g_list_insert_sorted(list, (gpointer)d, _name_def_compare);
- }
- }
- }
-
- GList *best_def = g_list_first(list);
- if (best_def) utf8name = (char*) strdup(((NameDef*)best_def->data)->buf);
- if (list) _g_list_free_full(list);
-
- return utf8name;
- }
-
- PangoWeight
- get_pango_weight(FT_UShort weight) {
- switch (weight) {
- case 100: return PANGO_WEIGHT_THIN;
- case 200: return PANGO_WEIGHT_ULTRALIGHT;
- case 300: return PANGO_WEIGHT_LIGHT;
- #if PANGO_VERSION >= PANGO_VERSION_ENCODE(1, 36, 7)
- case 350: return PANGO_WEIGHT_SEMILIGHT;
- #endif
- case 380: return PANGO_WEIGHT_BOOK;
- case 400: return PANGO_WEIGHT_NORMAL;
- case 500: return PANGO_WEIGHT_MEDIUM;
- case 600: return PANGO_WEIGHT_SEMIBOLD;
- case 700: return PANGO_WEIGHT_BOLD;
- case 800: return PANGO_WEIGHT_ULTRABOLD;
- case 900: return PANGO_WEIGHT_HEAVY;
- case 1000: return PANGO_WEIGHT_ULTRAHEAVY;
- default: return PANGO_WEIGHT_NORMAL;
- }
- }
-
- PangoStretch
- get_pango_stretch(FT_UShort width) {
- switch (width) {
- case 1: return PANGO_STRETCH_ULTRA_CONDENSED;
- case 2: return PANGO_STRETCH_EXTRA_CONDENSED;
- case 3: return PANGO_STRETCH_CONDENSED;
- case 4: return PANGO_STRETCH_SEMI_CONDENSED;
- case 5: return PANGO_STRETCH_NORMAL;
- case 6: return PANGO_STRETCH_SEMI_EXPANDED;
- case 7: return PANGO_STRETCH_EXPANDED;
- case 8: return PANGO_STRETCH_EXTRA_EXPANDED;
- case 9: return PANGO_STRETCH_ULTRA_EXPANDED;
- default: return PANGO_STRETCH_NORMAL;
- }
- }
-
- PangoStyle
- get_pango_style(FT_Long flags) {
- if (flags & FT_STYLE_FLAG_ITALIC) {
- return PANGO_STYLE_ITALIC;
- } else {
- return PANGO_STYLE_NORMAL;
- }
- }
-
- PangoFontDescription *
- get_pango_font_description(unsigned char* filepath) {
- FT_Library library;
- FT_Face face;
- PangoFontDescription *desc = pango_font_description_new();
-
- if (!FT_Init_FreeType(&library) && !FT_New_Face(library, (const char*)filepath, 0, &face)) {
- TT_OS2 *table = (TT_OS2*)FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
- char *family = get_family_name(face);
-
- if (family) pango_font_description_set_family_static(desc, family);
- pango_font_description_set_weight(desc, get_pango_weight(table->usWeightClass));
- pango_font_description_set_stretch(desc, get_pango_stretch(table->usWidthClass));
- pango_font_description_set_style(desc, get_pango_style(face->style_flags));
-
- FT_Done_Face(face);
-
- return desc;
- }
-
- pango_font_description_free(desc);
-
- return NULL;
- }
-
- /*
- * Register font with the OS
- */
-
- bool
- register_font(unsigned char *filepath, PangoFontDescription **desc) {
- bool success;
-
- #ifdef __APPLE__
- CFURLRef filepathUrl = CFURLCreateFromFileSystemRepresentation(NULL, filepath, strlen((char*)filepath), false);
- success = CTFontManagerRegisterFontsForURL(filepathUrl, kCTFontManagerScopeProcess, NULL);
- #elif defined(_WIN32)
- success = AddFontResourceEx((LPCSTR)filepath, FR_PRIVATE, 0) != 0;
- #else
- success = FcConfigAppFontAddFile(FcConfigGetCurrent(), (FcChar8 *)(filepath));
- #endif
-
- if (!success) return false;
-
- *desc = get_pango_font_description(filepath);
-
- // Tell Pango to throw away the current FontMap and create a new one. This
- // has the effect of registering the new font in Pango by re-looking up all
- // font families.
- pango_cairo_font_map_set_default(NULL);
-
- return true;
- }
-
|