Turn audio into a shareable video. forked from nypublicradio/audiogram

register_font.cc 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #include <pango/pangocairo.h>
  2. #include <pango/pango-fontmap.h>
  3. #include <pango/pango.h>
  4. #ifdef __APPLE__
  5. #include <CoreText/CoreText.h>
  6. #elif defined(_WIN32)
  7. #include <windows.h>
  8. #else
  9. #include <fontconfig/fontconfig.h>
  10. #endif
  11. #include <ft2build.h>
  12. #include FT_FREETYPE_H
  13. #include FT_TRUETYPE_TABLES_H
  14. #include FT_SFNT_NAMES_H
  15. #include FT_TRUETYPE_IDS_H
  16. #ifndef FT_SFNT_OS2
  17. #define FT_SFNT_OS2 ft_sfnt_os2
  18. #endif
  19. // OSX seems to read the strings in MacRoman encoding and ignore Unicode entries.
  20. // You can verify this by opening a TTF with both Unicode and Macroman on OSX.
  21. // It uses the MacRoman name, while Fontconfig and Windows use Unicode
  22. #ifdef __APPLE__
  23. #define PREFERRED_PLATFORM_ID TT_PLATFORM_MACINTOSH
  24. #define PREFERRED_ENCODING_ID TT_MAC_ID_ROMAN
  25. #else
  26. #define PREFERRED_PLATFORM_ID TT_PLATFORM_MICROSOFT
  27. #define PREFERRED_ENCODING_ID TT_MS_ID_UNICODE_CS
  28. #endif
  29. #define IS_PREFERRED_ENC(X) \
  30. X.platform_id == PREFERRED_PLATFORM_ID && X.encoding_id == PREFERRED_ENCODING_ID
  31. #define GET_NAME_RANK(X) \
  32. (IS_PREFERRED_ENC(X) ? 1 : 0) + (X.name_id == TT_NAME_ID_PREFERRED_FAMILY ? 1 : 0)
  33. /*
  34. * Return a UTF-8 encoded string given a TrueType name buf+len
  35. * and its platform and encoding
  36. */
  37. char *
  38. to_utf8(FT_Byte* buf, FT_UInt len, FT_UShort pid, FT_UShort eid) {
  39. size_t ret_len = len * 4; // max chars in a utf8 string
  40. char *ret = (char*)malloc(ret_len + 1); // utf8 string + null
  41. if (!ret) return NULL;
  42. // In my testing of hundreds of fonts from the Google Font repo, the two types
  43. // of fonts are TT_PLATFORM_MICROSOFT with TT_MS_ID_UNICODE_CS encoding, or
  44. // TT_PLATFORM_MACINTOSH with TT_MAC_ID_ROMAN encoding. Usually both, never neither
  45. char const *fromcode;
  46. if (pid == TT_PLATFORM_MACINTOSH && eid == TT_MAC_ID_ROMAN) {
  47. fromcode = "MAC";
  48. } else if (pid == TT_PLATFORM_MICROSOFT && eid == TT_MS_ID_UNICODE_CS) {
  49. fromcode = "UTF-16BE";
  50. } else {
  51. free(ret);
  52. return NULL;
  53. }
  54. GIConv cd = g_iconv_open("UTF-8", fromcode);
  55. if (cd == (GIConv)-1) {
  56. free(ret);
  57. return NULL;
  58. }
  59. size_t inbytesleft = len;
  60. size_t outbytesleft = ret_len;
  61. size_t n_converted = g_iconv(cd, (char**)&buf, &inbytesleft, &ret, &outbytesleft);
  62. ret -= ret_len - outbytesleft; // rewind the pointers to their
  63. buf -= len - inbytesleft; // original starting positions
  64. if (n_converted == (size_t)-1) {
  65. free(ret);
  66. return NULL;
  67. } else {
  68. ret[ret_len - outbytesleft] = '\0';
  69. return ret;
  70. }
  71. }
  72. /*
  73. * Find a family name in the face's name table, preferring the one the
  74. * system, fall back to the other
  75. */
  76. typedef struct _NameDef {
  77. const char *buf;
  78. int rank; // the higher the more desirable
  79. } NameDef;
  80. gint
  81. _name_def_compare(gconstpointer a, gconstpointer b) {
  82. return ((NameDef*)a)->rank > ((NameDef*)b)->rank ? -1 : 1;
  83. }
  84. // Some versions of GTK+ do not have this, particualrly the one we
  85. // currently link to in node-canvas's wiki
  86. void
  87. _free_g_list_item(gpointer data, gpointer user_data) {
  88. NameDef *d = (NameDef *)data;
  89. free((void *)(d->buf));
  90. }
  91. void
  92. _g_list_free_full(GList *list) {
  93. g_list_foreach(list, _free_g_list_item, NULL);
  94. g_list_free(list);
  95. }
  96. char *
  97. get_family_name(FT_Face face) {
  98. FT_SfntName name;
  99. GList *list = NULL;
  100. char *utf8name = NULL;
  101. for (unsigned i = 0; i < FT_Get_Sfnt_Name_Count(face); ++i) {
  102. FT_Get_Sfnt_Name(face, i, &name);
  103. if (name.name_id == TT_NAME_ID_FONT_FAMILY || name.name_id == TT_NAME_ID_PREFERRED_FAMILY) {
  104. char *buf = to_utf8(name.string, name.string_len, name.platform_id, name.encoding_id);
  105. if (buf) {
  106. NameDef *d = (NameDef*)malloc(sizeof(NameDef));
  107. d->buf = (const char*)buf;
  108. d->rank = GET_NAME_RANK(name);
  109. list = g_list_insert_sorted(list, (gpointer)d, _name_def_compare);
  110. }
  111. }
  112. }
  113. GList *best_def = g_list_first(list);
  114. if (best_def) utf8name = (char*) strdup(((NameDef*)best_def->data)->buf);
  115. if (list) _g_list_free_full(list);
  116. return utf8name;
  117. }
  118. PangoWeight
  119. get_pango_weight(FT_UShort weight) {
  120. switch (weight) {
  121. case 100: return PANGO_WEIGHT_THIN;
  122. case 200: return PANGO_WEIGHT_ULTRALIGHT;
  123. case 300: return PANGO_WEIGHT_LIGHT;
  124. #if PANGO_VERSION >= PANGO_VERSION_ENCODE(1, 36, 7)
  125. case 350: return PANGO_WEIGHT_SEMILIGHT;
  126. #endif
  127. case 380: return PANGO_WEIGHT_BOOK;
  128. case 400: return PANGO_WEIGHT_NORMAL;
  129. case 500: return PANGO_WEIGHT_MEDIUM;
  130. case 600: return PANGO_WEIGHT_SEMIBOLD;
  131. case 700: return PANGO_WEIGHT_BOLD;
  132. case 800: return PANGO_WEIGHT_ULTRABOLD;
  133. case 900: return PANGO_WEIGHT_HEAVY;
  134. case 1000: return PANGO_WEIGHT_ULTRAHEAVY;
  135. default: return PANGO_WEIGHT_NORMAL;
  136. }
  137. }
  138. PangoStretch
  139. get_pango_stretch(FT_UShort width) {
  140. switch (width) {
  141. case 1: return PANGO_STRETCH_ULTRA_CONDENSED;
  142. case 2: return PANGO_STRETCH_EXTRA_CONDENSED;
  143. case 3: return PANGO_STRETCH_CONDENSED;
  144. case 4: return PANGO_STRETCH_SEMI_CONDENSED;
  145. case 5: return PANGO_STRETCH_NORMAL;
  146. case 6: return PANGO_STRETCH_SEMI_EXPANDED;
  147. case 7: return PANGO_STRETCH_EXPANDED;
  148. case 8: return PANGO_STRETCH_EXTRA_EXPANDED;
  149. case 9: return PANGO_STRETCH_ULTRA_EXPANDED;
  150. default: return PANGO_STRETCH_NORMAL;
  151. }
  152. }
  153. PangoStyle
  154. get_pango_style(FT_Long flags) {
  155. if (flags & FT_STYLE_FLAG_ITALIC) {
  156. return PANGO_STYLE_ITALIC;
  157. } else {
  158. return PANGO_STYLE_NORMAL;
  159. }
  160. }
  161. PangoFontDescription *
  162. get_pango_font_description(unsigned char* filepath) {
  163. FT_Library library;
  164. FT_Face face;
  165. PangoFontDescription *desc = pango_font_description_new();
  166. if (!FT_Init_FreeType(&library) && !FT_New_Face(library, (const char*)filepath, 0, &face)) {
  167. TT_OS2 *table = (TT_OS2*)FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
  168. char *family = get_family_name(face);
  169. if (family) pango_font_description_set_family_static(desc, family);
  170. pango_font_description_set_weight(desc, get_pango_weight(table->usWeightClass));
  171. pango_font_description_set_stretch(desc, get_pango_stretch(table->usWidthClass));
  172. pango_font_description_set_style(desc, get_pango_style(face->style_flags));
  173. FT_Done_Face(face);
  174. return desc;
  175. }
  176. pango_font_description_free(desc);
  177. return NULL;
  178. }
  179. /*
  180. * Register font with the OS
  181. */
  182. bool
  183. register_font(unsigned char *filepath, PangoFontDescription **desc) {
  184. bool success;
  185. #ifdef __APPLE__
  186. CFURLRef filepathUrl = CFURLCreateFromFileSystemRepresentation(NULL, filepath, strlen((char*)filepath), false);
  187. success = CTFontManagerRegisterFontsForURL(filepathUrl, kCTFontManagerScopeProcess, NULL);
  188. #elif defined(_WIN32)
  189. success = AddFontResourceEx((LPCSTR)filepath, FR_PRIVATE, 0) != 0;
  190. #else
  191. success = FcConfigAppFontAddFile(FcConfigGetCurrent(), (FcChar8 *)(filepath));
  192. #endif
  193. if (!success) return false;
  194. *desc = get_pango_font_description(filepath);
  195. // Tell Pango to throw away the current FontMap and create a new one. This
  196. // has the effect of registering the new font in Pango by re-looking up all
  197. // font families.
  198. pango_cairo_font_map_set_default(NULL);
  199. return true;
  200. }