find-font-windows.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. #include <ctype.h>
  2. #include <obs.h>
  3. #include <util/dstr.h>
  4. #include <util/darray.h>
  5. #include "find-font.h"
  6. #include "text-freetype2.h"
  7. #define WIN32_LEAN_AND_MEAN
  8. #include <windows.h>
  9. #include <shellapi.h>
  10. #include <shlobj.h>
  11. DARRAY(struct font_path_info) font_list;
  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. };
  47. unsigned int iso_codes[] = {
  48. 20127,
  49. 0,
  50. 28591
  51. };
  52. unsigned int ms_codes[] = {
  53. 1201,
  54. 1201,
  55. 932,
  56. 0,
  57. 950,
  58. 0,
  59. 0,
  60. 0,
  61. 0,
  62. 0,
  63. 1201
  64. };
  65. static const size_t mac_code_count = sizeof(mac_codes) / sizeof(mac_codes[0]);
  66. static const size_t iso_code_count = sizeof(iso_codes) / sizeof(iso_codes[0]);
  67. static const size_t ms_code_count = sizeof(ms_codes) / sizeof(ms_codes[0]);
  68. static unsigned int get_mac_code(uint16_t encoding_id, uint16_t language_id)
  69. {
  70. for (size_t i = 0; i < mac_code_count; i++) {
  71. const struct mac_font_mapping *mac_code = &mac_codes[i];
  72. if (mac_code->encoding_id == encoding_id &&
  73. mac_code->language_id == language_id)
  74. return mac_code->code_page;
  75. }
  76. return 0;
  77. }
  78. static unsigned int get_code_page_for_font(uint16_t platform_id,
  79. uint16_t encoding_id, uint16_t language_id)
  80. {
  81. unsigned int ret;
  82. switch (platform_id) {
  83. case TT_PLATFORM_APPLE_UNICODE:
  84. return 1201;
  85. case TT_PLATFORM_MACINTOSH:
  86. ret = get_mac_code(encoding_id, language_id);
  87. if (!ret)
  88. ret = get_mac_code(encoding_id, TT_MAC_LANGID_ANY);
  89. return ret;
  90. case TT_PLATFORM_ISO:
  91. if (encoding_id < iso_code_count)
  92. return iso_codes[encoding_id];
  93. break;
  94. case TT_PLATFORM_MICROSOFT:
  95. if (encoding_id < ms_code_count)
  96. return ms_codes[encoding_id];
  97. break;
  98. }
  99. return 0;
  100. }
  101. static char *wide_to_utf8(const wchar_t *str, size_t len)
  102. {
  103. size_t utf8_len;
  104. char *utf8_str = NULL;
  105. utf8_len = (size_t)WideCharToMultiByte(CP_UTF8, 0, str, (int)len,
  106. NULL, 0, NULL, false);
  107. if (utf8_len) {
  108. utf8_str = bzalloc(utf8_len + 1);
  109. utf8_len = (size_t)WideCharToMultiByte(CP_UTF8, 0,
  110. str, (int)len,
  111. utf8_str, (int)utf8_len + 1, NULL, false);
  112. if (!utf8_len) {
  113. bfree(utf8_str);
  114. utf8_str = NULL;
  115. }
  116. }
  117. return utf8_str;
  118. }
  119. static char *convert_utf16_be_to_utf8(FT_SfntName *sfnt_name)
  120. {
  121. size_t utf16_len = sfnt_name->string_len / 2;
  122. wchar_t *utf16_str = malloc((utf16_len + 1) * sizeof(wchar_t));
  123. char *utf8_str = NULL;
  124. utf16_str[utf16_len] = 0;
  125. /* convert to little endian */
  126. for (size_t i = 0; i < utf16_len; i++) {
  127. size_t pos = i * 2;
  128. wchar_t ch = *(wchar_t *)&sfnt_name->string[pos];
  129. utf16_str[i] = ((ch >> 8) & 0xFF) | ((ch << 8) & 0xFF00);
  130. }
  131. utf8_str = wide_to_utf8(utf16_str, utf16_len);
  132. free(utf16_str);
  133. return utf8_str;
  134. }
  135. static char *sfnt_name_to_utf8(FT_SfntName *sfnt_name)
  136. {
  137. unsigned int code_page = get_code_page_for_font(
  138. sfnt_name->platform_id,
  139. sfnt_name->encoding_id,
  140. sfnt_name->language_id);
  141. char *utf8_str = NULL;
  142. wchar_t *utf16_str;
  143. size_t utf16_len;
  144. if (code_page == 1201)
  145. return convert_utf16_be_to_utf8(sfnt_name);
  146. else if (code_page == 0)
  147. return NULL;
  148. utf16_len = MultiByteToWideChar(code_page, 0,
  149. sfnt_name->string, sfnt_name->string_len, NULL, 0);
  150. if (utf16_len) {
  151. utf16_str = malloc((utf16_len + 1) * sizeof(wchar_t));
  152. utf16_len = MultiByteToWideChar(code_page, 0,
  153. sfnt_name->string, sfnt_name->string_len,
  154. utf16_str, (int)utf16_len);
  155. if (utf16_len) {
  156. utf16_str[utf16_len] = 0;
  157. utf8_str = wide_to_utf8(utf16_str, utf16_len);
  158. }
  159. free(utf16_str);
  160. }
  161. return utf8_str;
  162. }
  163. static void create_bitmap_sizes(struct font_path_info *info, FT_Face face)
  164. {
  165. DARRAY(int) sizes;
  166. if (!info->is_bitmap) {
  167. info->num_sizes = 0;
  168. info->sizes = NULL;
  169. return;
  170. }
  171. da_init(sizes);
  172. da_reserve(sizes, face->num_fixed_sizes);
  173. for (int i = 0; i < face->num_fixed_sizes; i++) {
  174. int val = face->available_sizes[i].size >> 6;
  175. da_push_back(sizes, &val);
  176. }
  177. info->sizes = sizes.array;
  178. info->num_sizes = face->num_fixed_sizes;
  179. }
  180. static void add_font_path(FT_Face face,
  181. FT_Long idx,
  182. const char *family_in,
  183. const char *style_in,
  184. const char *path)
  185. {
  186. if (!family_in || !style_in || !path)
  187. return;
  188. struct dstr face_and_style = {0};
  189. struct font_path_info info;
  190. dstr_copy(&face_and_style, family_in);
  191. if (face->style_name) {
  192. struct dstr style = {0};
  193. dstr_copy(&style, style_in);
  194. dstr_replace(&style, "Bold", "");
  195. dstr_replace(&style, "Italic", "");
  196. dstr_replace(&style, " ", " ");
  197. dstr_depad(&style);
  198. if (!dstr_is_empty(&style)) {
  199. dstr_cat(&face_and_style, " ");
  200. dstr_cat_dstr(&face_and_style, &style);
  201. }
  202. dstr_free(&style);
  203. }
  204. info.face_and_style = face_and_style.array;
  205. info.full_len = face_and_style.len;
  206. info.face_len = strlen(family_in);
  207. info.is_bitmap = !!(face->face_flags & FT_FACE_FLAG_FIXED_SIZES);
  208. info.bold = !!(face->style_flags & FT_STYLE_FLAG_BOLD);
  209. info.italic = !!(face->style_flags & FT_STYLE_FLAG_ITALIC);
  210. info.index = idx;
  211. info.path = bstrdup(path);
  212. create_bitmap_sizes(&info, face);
  213. da_push_back(font_list, &info);
  214. /*blog(LOG_DEBUG, "name: %s\n\tstyle: %s\n\tpath: %s\n",
  215. family_in,
  216. style_in,
  217. path);*/
  218. }
  219. static void build_font_path_info(FT_Face face, FT_Long idx, const char *path)
  220. {
  221. FT_UInt num_names = FT_Get_Sfnt_Name_Count(face);
  222. DARRAY(char*) family_names;
  223. da_init(family_names);
  224. da_push_back(family_names, &face->family_name);
  225. for (FT_UInt i = 0; i < num_names; i++) {
  226. FT_SfntName name;
  227. char *family;
  228. FT_Error ret = FT_Get_Sfnt_Name(face, i, &name);
  229. if (ret != 0 || name.name_id != TT_NAME_ID_FONT_FAMILY)
  230. continue;
  231. family = sfnt_name_to_utf8(&name);
  232. if (!family)
  233. continue;
  234. for (size_t i = 0; i < family_names.num; i++) {
  235. if (astrcmpi(family_names.array[i], family) == 0) {
  236. bfree(family);
  237. family = NULL;
  238. break;
  239. }
  240. }
  241. if (family)
  242. da_push_back(family_names, &family);
  243. }
  244. for (size_t i = 0; i < family_names.num; i++) {
  245. add_font_path(face, idx, family_names.array[i],
  246. face->style_name, path);
  247. /* first item isn't our allocation */
  248. if (i > 0)
  249. bfree(family_names.array[i]);
  250. }
  251. da_free(family_names);
  252. }
  253. void load_os_font_list(void)
  254. {
  255. struct dstr path = {0};
  256. HANDLE handle;
  257. WIN32_FIND_DATAA wfd;
  258. dstr_reserve(&path, MAX_PATH);
  259. HRESULT res = SHGetFolderPathA(NULL, CSIDL_FONTS, NULL,
  260. SHGFP_TYPE_CURRENT, path.array);
  261. if (res != S_OK) {
  262. blog(LOG_WARNING, "Error finding windows font folder");
  263. return;
  264. }
  265. path.len = strlen(path.array);
  266. dstr_cat(&path, "\\*.*");
  267. handle = FindFirstFileA(path.array, &wfd);
  268. if (handle == INVALID_HANDLE_VALUE)
  269. goto free_string;
  270. dstr_resize(&path, path.len - 4);
  271. do {
  272. struct dstr full_path = {0};
  273. FT_Face face;
  274. FT_Long idx = 0;
  275. FT_Long max_faces = 1;
  276. if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  277. continue;
  278. dstr_copy_dstr(&full_path, &path);
  279. dstr_cat(&full_path, "\\");
  280. dstr_cat(&full_path, wfd.cFileName);
  281. while (idx < max_faces) {
  282. FT_Error ret = FT_New_Face(ft2_lib, full_path.array,
  283. idx, &face);
  284. if (ret != 0)
  285. break;
  286. build_font_path_info(face, idx++, full_path.array);
  287. max_faces = face->num_faces;
  288. FT_Done_Face(face);
  289. }
  290. dstr_free(&full_path);
  291. } while (FindNextFileA(handle, &wfd));
  292. FindClose(handle);
  293. free_string:
  294. dstr_free(&path);
  295. }
  296. void free_os_font_list(void)
  297. {
  298. for (size_t i = 0; i < font_list.num; i++)
  299. font_path_info_free(font_list.array + i);
  300. da_free(font_list);
  301. }
  302. static inline size_t get_rating(struct font_path_info *info, struct dstr *cmp)
  303. {
  304. const char *src = info->face_and_style;
  305. const char *dst = cmp->array;
  306. size_t num = 0;
  307. do {
  308. char ch1 = (char)toupper(*src);
  309. char ch2 = (char)toupper(*dst);
  310. if (ch1 != ch2)
  311. break;
  312. num++;
  313. } while (*src++ && *dst++);
  314. return num;
  315. }
  316. const char *get_font_path(const char *family, uint16_t size, const char *style,
  317. uint32_t flags, FT_Long *idx)
  318. {
  319. const char *best_path = NULL;
  320. double best_rating = 0.0;
  321. struct dstr face_and_style = {0};
  322. struct dstr style_str = {0};
  323. bool bold = !!(flags & OBS_FONT_BOLD);
  324. bool italic = !!(flags & OBS_FONT_ITALIC);
  325. if (!family)
  326. return NULL;
  327. dstr_copy(&style_str, style);
  328. dstr_replace(&style_str, "Bold", "");
  329. dstr_replace(&style_str, "Italic", "");
  330. dstr_replace(&style_str, " ", " ");
  331. dstr_depad(&style_str);
  332. dstr_copy(&face_and_style, family);
  333. if (!dstr_is_empty(&style_str)) {
  334. dstr_cat(&face_and_style, " ");
  335. dstr_cat_dstr(&face_and_style, &style_str);
  336. }
  337. for (size_t i = 0; i < font_list.num; i++) {
  338. struct font_path_info *info = font_list.array + i;
  339. double rating = (double)get_rating(info, &face_and_style);
  340. if (rating < info->face_len)
  341. continue;
  342. if (info->is_bitmap) {
  343. int best_diff = 1000;
  344. for (size_t j = 0; j < info->num_sizes; j++) {
  345. int diff = abs(info->sizes[j] - size);
  346. if (diff < best_diff)
  347. best_diff = diff;
  348. }
  349. rating /= (double)(best_diff + 1.0);
  350. }
  351. if (info->bold == bold) rating += 1.0;
  352. if (info->italic == italic) rating += 1.0;
  353. if (rating > best_rating) {
  354. best_path = info->path;
  355. *idx = info->index;
  356. best_rating = rating;
  357. }
  358. }
  359. dstr_free(&style_str);
  360. dstr_free(&face_and_style);
  361. return best_path;
  362. }