find-font-windows.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #include <util/dstr.h>
  2. #include <util/darray.h>
  3. #include <util/crc32.h>
  4. #include "find-font.h"
  5. #include "text-freetype2.h"
  6. #define WIN32_LEAN_AND_MEAN
  7. #include <windows.h>
  8. #include <shellapi.h>
  9. #include <shlobj.h>
  10. extern DARRAY(struct font_path_info) font_list;
  11. extern void save_font_list(void);
  12. struct mac_font_mapping {
  13. unsigned short encoding_id;
  14. unsigned short language_id;
  15. unsigned int code_page;
  16. };
  17. #define TT_MAC_LANGID_ANY 0xFFFF
  18. static const struct mac_font_mapping mac_codes[] = {
  19. {TT_MAC_ID_ROMAN, TT_MAC_LANGID_ENGLISH, 10000},
  20. {TT_MAC_ID_ROMAN, TT_MAC_LANGID_ICELANDIC, 10079},
  21. {TT_MAC_ID_ROMAN, TT_MAC_LANGID_TURKISH, 10081},
  22. {TT_MAC_ID_ROMAN, TT_MAC_LANGID_POLISH, 10029},
  23. {TT_MAC_ID_ROMAN, TT_MAC_LANGID_ROMANIAN, 10010},
  24. {TT_MAC_ID_ROMAN, TT_MAC_LANGID_CZECH, 10029},
  25. {TT_MAC_ID_ROMAN, TT_MAC_LANGID_SLOVAK, 10029},
  26. {TT_MAC_ID_ROMAN, TT_MAC_LANGID_ANY, 10000},
  27. {TT_MAC_ID_JAPANESE, TT_MAC_LANGID_JAPANESE, 932},
  28. {TT_MAC_ID_JAPANESE, TT_MAC_LANGID_ANY, 932},
  29. {TT_MAC_ID_TRADITIONAL_CHINESE, TT_MAC_LANGID_CHINESE_SIMPLIFIED, 950},
  30. {TT_MAC_ID_TRADITIONAL_CHINESE, TT_MAC_LANGID_ANY, 950},
  31. {TT_MAC_ID_KOREAN, TT_MAC_LANGID_KOREAN, 51949},
  32. {TT_MAC_ID_KOREAN, TT_MAC_LANGID_ANY, 51949},
  33. {TT_MAC_ID_ARABIC, TT_MAC_LANGID_ARABIC, 10004},
  34. {TT_MAC_ID_ARABIC, TT_MAC_LANGID_URDU, 0},
  35. {TT_MAC_ID_ARABIC, TT_MAC_LANGID_FARSI, 0},
  36. {TT_MAC_ID_ARABIC, TT_MAC_LANGID_ANY, 10004},
  37. {TT_MAC_ID_HEBREW, TT_MAC_LANGID_HEBREW, 10005},
  38. {TT_MAC_ID_HEBREW, TT_MAC_LANGID_ANY, 10005},
  39. {TT_MAC_ID_GREEK, TT_MAC_LANGID_ANY, 10006},
  40. {TT_MAC_ID_RUSSIAN, TT_MAC_LANGID_ANY, 10007},
  41. {TT_MAC_ID_DEVANAGARI, TT_MAC_LANGID_ANY, 0},
  42. {TT_MAC_ID_GURMUKHI, TT_MAC_LANGID_ANY, 0},
  43. {TT_MAC_ID_GUJARATI, TT_MAC_LANGID_ANY, 0},
  44. {TT_MAC_ID_SIMPLIFIED_CHINESE, TT_MAC_LANGID_CHINESE_SIMPLIFIED, 936},
  45. {TT_MAC_ID_SIMPLIFIED_CHINESE, TT_MAC_LANGID_ANY, 936}};
  46. unsigned int iso_codes[] = {20127, 0, 28591};
  47. unsigned int ms_codes[] = {1201, 1201, 932, 0, 950, 0, 0, 0, 0, 0, 1201};
  48. static const size_t mac_code_count = sizeof(mac_codes) / sizeof(mac_codes[0]);
  49. static const size_t iso_code_count = sizeof(iso_codes) / sizeof(iso_codes[0]);
  50. static const size_t ms_code_count = sizeof(ms_codes) / sizeof(ms_codes[0]);
  51. static unsigned int get_mac_code(uint16_t encoding_id, uint16_t language_id)
  52. {
  53. for (size_t i = 0; i < mac_code_count; i++) {
  54. const struct mac_font_mapping *mac_code = &mac_codes[i];
  55. if (mac_code->encoding_id == encoding_id &&
  56. mac_code->language_id == language_id)
  57. return mac_code->code_page;
  58. }
  59. return 0;
  60. }
  61. static unsigned int get_code_page_for_font(uint16_t platform_id,
  62. uint16_t encoding_id,
  63. uint16_t language_id)
  64. {
  65. unsigned int ret;
  66. switch (platform_id) {
  67. case TT_PLATFORM_APPLE_UNICODE:
  68. return 1201;
  69. case TT_PLATFORM_MACINTOSH:
  70. ret = get_mac_code(encoding_id, language_id);
  71. if (!ret)
  72. ret = get_mac_code(encoding_id, TT_MAC_LANGID_ANY);
  73. return ret;
  74. case TT_PLATFORM_ISO:
  75. if (encoding_id < iso_code_count)
  76. return iso_codes[encoding_id];
  77. break;
  78. case TT_PLATFORM_MICROSOFT:
  79. if (encoding_id < ms_code_count)
  80. return ms_codes[encoding_id];
  81. break;
  82. }
  83. return 0;
  84. }
  85. static char *wide_to_utf8(const wchar_t *str, size_t len)
  86. {
  87. size_t utf8_len;
  88. char *utf8_str = NULL;
  89. utf8_len = (size_t)WideCharToMultiByte(CP_UTF8, 0, str, (int)len, NULL,
  90. 0, NULL, false);
  91. if (utf8_len) {
  92. utf8_str = bzalloc(utf8_len + 1);
  93. utf8_len = (size_t)WideCharToMultiByte(CP_UTF8, 0, str,
  94. (int)len, utf8_str,
  95. (int)utf8_len + 1, NULL,
  96. false);
  97. if (!utf8_len) {
  98. bfree(utf8_str);
  99. utf8_str = NULL;
  100. }
  101. }
  102. return utf8_str;
  103. }
  104. static char *convert_utf16_be_to_utf8(FT_SfntName *sfnt_name)
  105. {
  106. size_t utf16_len = sfnt_name->string_len / 2;
  107. wchar_t *utf16_str = malloc((utf16_len + 1) * sizeof(wchar_t));
  108. char *utf8_str = NULL;
  109. utf16_str[utf16_len] = 0;
  110. /* convert to little endian */
  111. for (size_t i = 0; i < utf16_len; i++) {
  112. size_t pos = i * 2;
  113. wchar_t ch = *(wchar_t *)&sfnt_name->string[pos];
  114. utf16_str[i] = ((ch >> 8) & 0xFF) | ((ch << 8) & 0xFF00);
  115. }
  116. utf8_str = wide_to_utf8(utf16_str, utf16_len);
  117. free(utf16_str);
  118. return utf8_str;
  119. }
  120. char *sfnt_name_to_utf8(FT_SfntName *sfnt_name)
  121. {
  122. unsigned int code_page = get_code_page_for_font(sfnt_name->platform_id,
  123. sfnt_name->encoding_id,
  124. sfnt_name->language_id);
  125. char *utf8_str = NULL;
  126. wchar_t *utf16_str;
  127. size_t utf16_len;
  128. if (code_page == 1201)
  129. return convert_utf16_be_to_utf8(sfnt_name);
  130. else if (code_page == 0)
  131. return NULL;
  132. utf16_len = MultiByteToWideChar(code_page, 0, (char *)sfnt_name->string,
  133. sfnt_name->string_len, NULL, 0);
  134. if (utf16_len) {
  135. utf16_str = malloc((utf16_len + 1) * sizeof(wchar_t));
  136. utf16_len = MultiByteToWideChar(code_page, 0,
  137. (char *)sfnt_name->string,
  138. sfnt_name->string_len,
  139. utf16_str, (int)utf16_len);
  140. if (utf16_len) {
  141. utf16_str[utf16_len] = 0;
  142. utf8_str = wide_to_utf8(utf16_str, utf16_len);
  143. }
  144. free(utf16_str);
  145. }
  146. return utf8_str;
  147. }
  148. uint32_t get_font_checksum(void)
  149. {
  150. uint32_t checksum = 0;
  151. struct dstr path = {0};
  152. HANDLE handle;
  153. WIN32_FIND_DATAA wfd;
  154. dstr_reserve(&path, MAX_PATH);
  155. HRESULT res = SHGetFolderPathA(NULL, CSIDL_FONTS, NULL,
  156. SHGFP_TYPE_CURRENT, path.array);
  157. if (res != S_OK) {
  158. blog(LOG_WARNING, "Error finding windows font folder");
  159. return 0;
  160. }
  161. path.len = strlen(path.array);
  162. dstr_cat(&path, "\\*.*");
  163. handle = FindFirstFileA(path.array, &wfd);
  164. if (handle == INVALID_HANDLE_VALUE)
  165. goto free_string;
  166. dstr_resize(&path, path.len - 4);
  167. do {
  168. checksum = calc_crc32(checksum, &wfd.ftLastWriteTime,
  169. sizeof(FILETIME));
  170. checksum = calc_crc32(checksum, wfd.cFileName,
  171. strlen(wfd.cFileName));
  172. } while (FindNextFileA(handle, &wfd));
  173. FindClose(handle);
  174. free_string:
  175. dstr_free(&path);
  176. return checksum;
  177. }
  178. void load_os_font_list(void)
  179. {
  180. struct dstr path = {0};
  181. HANDLE handle;
  182. WIN32_FIND_DATAA wfd;
  183. dstr_reserve(&path, MAX_PATH);
  184. HRESULT res = SHGetFolderPathA(NULL, CSIDL_FONTS, NULL,
  185. SHGFP_TYPE_CURRENT, path.array);
  186. if (res != S_OK) {
  187. blog(LOG_WARNING, "Error finding windows font folder");
  188. return;
  189. }
  190. path.len = strlen(path.array);
  191. dstr_cat(&path, "\\*.*");
  192. handle = FindFirstFileA(path.array, &wfd);
  193. if (handle == INVALID_HANDLE_VALUE)
  194. goto free_string;
  195. dstr_resize(&path, path.len - 4);
  196. do {
  197. struct dstr full_path = {0};
  198. FT_Face face;
  199. FT_Long idx = 0;
  200. FT_Long max_faces = 1;
  201. if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  202. continue;
  203. dstr_copy_dstr(&full_path, &path);
  204. dstr_cat(&full_path, "\\");
  205. dstr_cat(&full_path, wfd.cFileName);
  206. while (idx < max_faces) {
  207. FT_Error ret = FT_New_Face(ft2_lib, full_path.array,
  208. idx, &face);
  209. if (ret != 0)
  210. break;
  211. build_font_path_info(face, idx++, full_path.array);
  212. max_faces = face->num_faces;
  213. FT_Done_Face(face);
  214. }
  215. dstr_free(&full_path);
  216. } while (FindNextFileA(handle, &wfd));
  217. FindClose(handle);
  218. save_font_list();
  219. free_string:
  220. dstr_free(&path);
  221. }