find-font.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. #include <util/file-serializer.h>
  2. #include <ctype.h>
  3. #include <time.h>
  4. #include <obs-module.h>
  5. #include "find-font.h"
  6. DARRAY(struct font_path_info) font_list;
  7. static inline bool read_data(struct serializer *s, void *data, size_t size)
  8. {
  9. return s_read(s, data, size) == size;
  10. }
  11. static inline bool write_data(struct serializer *s, const void *data,
  12. size_t size)
  13. {
  14. return s_write(s, data, size) == size;
  15. }
  16. #define read_var(s, data) read_data(s, &data, sizeof(data))
  17. #define write_var(s, data) write_data(s, &data, sizeof(data))
  18. static bool read_str(struct serializer *s, char **p_str)
  19. {
  20. uint32_t size;
  21. char *str;
  22. if (!read_var(s, size))
  23. return false;
  24. str = bmalloc(size + 1);
  25. if (size && !read_data(s, str, size)) {
  26. bfree(str);
  27. return false;
  28. }
  29. str[size] = 0;
  30. *p_str = str;
  31. return true;
  32. }
  33. static bool write_str(struct serializer *s, const char *str)
  34. {
  35. uint32_t size = (uint32_t)(str ? strlen(str) : 0);
  36. if (!write_var(s, size))
  37. return false;
  38. if (size && !write_data(s, str, size))
  39. return false;
  40. return true;
  41. }
  42. static bool load_cached_font_list(struct serializer *s)
  43. {
  44. bool success = true;
  45. int count;
  46. success = read_var(s, count);
  47. if (!success)
  48. return false;
  49. da_init(font_list);
  50. da_resize(font_list, count);
  51. #define do_read(var) \
  52. success = read_var(s, var); \
  53. if (!success) \
  54. break
  55. for (int i = 0; i < count; i++) {
  56. struct font_path_info *info = &font_list.array[i];
  57. success = read_str(s, &info->face_and_style);
  58. if (!success)
  59. break;
  60. do_read(info->full_len);
  61. do_read(info->face_len);
  62. do_read(info->is_bitmap);
  63. do_read(info->num_sizes);
  64. if (info->num_sizes) {
  65. info->sizes = bmalloc(sizeof(int) * info->num_sizes);
  66. success = read_data(s, info->sizes,
  67. sizeof(int) * info->num_sizes);
  68. if (!success)
  69. break;
  70. }
  71. do_read(info->bold);
  72. success = read_str(s, &info->path);
  73. if (!success)
  74. break;
  75. do_read(info->italic);
  76. do_read(info->index);
  77. }
  78. #undef do_read
  79. if (!success) {
  80. free_os_font_list();
  81. return false;
  82. }
  83. return true;
  84. }
  85. extern uint32_t get_font_checksum();
  86. static const uint32_t font_cache_ver = 1;
  87. bool load_cached_os_font_list(void)
  88. {
  89. char *file_name = obs_module_config_path("font_data.bin");
  90. uint32_t old_checksum;
  91. uint32_t new_checksum;
  92. struct serializer s;
  93. uint32_t ver;
  94. bool success;
  95. success = file_input_serializer_init(&s, file_name);
  96. bfree(file_name);
  97. if (!success)
  98. return false;
  99. success = read_data(&s, &ver, sizeof(ver));
  100. if (!success || ver != font_cache_ver) {
  101. success = false;
  102. goto finish;
  103. }
  104. success = s_read(&s, &old_checksum, sizeof(old_checksum));
  105. new_checksum = get_font_checksum();
  106. if (!success || old_checksum != new_checksum) {
  107. success = false;
  108. goto finish;
  109. }
  110. success = load_cached_font_list(&s);
  111. finish:
  112. file_input_serializer_free(&s);
  113. return success;
  114. }
  115. void save_font_list(void)
  116. {
  117. char *file_name = obs_module_config_path("font_data.bin");
  118. uint32_t font_checksum = get_font_checksum();
  119. int font_count = (int)font_list.num;
  120. struct serializer s;
  121. bool success = false;
  122. if (font_checksum)
  123. success =
  124. file_output_serializer_init_safe(&s, file_name, "tmp");
  125. bfree(file_name);
  126. if (!success)
  127. return;
  128. success = write_var(&s, font_cache_ver);
  129. if (!success)
  130. return;
  131. success = write_var(&s, font_checksum);
  132. if (!success)
  133. return;
  134. success = write_var(&s, font_count);
  135. if (!success)
  136. return;
  137. #define do_write(var) \
  138. success = write_var(&s, var); \
  139. if (!success) \
  140. break
  141. for (size_t i = 0; i < font_list.num; i++) {
  142. struct font_path_info *info = &font_list.array[i];
  143. success = write_str(&s, info->face_and_style);
  144. if (!success)
  145. break;
  146. do_write(info->full_len);
  147. do_write(info->face_len);
  148. do_write(info->is_bitmap);
  149. do_write(info->num_sizes);
  150. if (info->num_sizes) {
  151. success = write_data(&s, info->sizes,
  152. sizeof(int) * info->num_sizes);
  153. if (!success)
  154. break;
  155. }
  156. do_write(info->bold);
  157. success = write_str(&s, info->path);
  158. if (!success)
  159. break;
  160. do_write(info->italic);
  161. do_write(info->index);
  162. }
  163. #undef do_write
  164. file_output_serializer_free(&s);
  165. }
  166. static void create_bitmap_sizes(struct font_path_info *info, FT_Face face)
  167. {
  168. DARRAY(int) sizes;
  169. if (!info->is_bitmap) {
  170. info->num_sizes = 0;
  171. info->sizes = NULL;
  172. return;
  173. }
  174. da_init(sizes);
  175. da_reserve(sizes, face->num_fixed_sizes);
  176. for (int i = 0; i < face->num_fixed_sizes; i++) {
  177. int val = face->available_sizes[i].size >> 6;
  178. da_push_back(sizes, &val);
  179. }
  180. info->sizes = sizes.array;
  181. info->num_sizes = (uint32_t)face->num_fixed_sizes;
  182. }
  183. static void add_font_path(FT_Face face, FT_Long idx, const char *family_in,
  184. const char *style_in, const char *path)
  185. {
  186. struct dstr face_and_style = {0};
  187. struct font_path_info info;
  188. if (!family_in || !path)
  189. return;
  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 = (uint32_t)face_and_style.len;
  206. info.face_len = (uint32_t)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. 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 free_os_font_list(void)
  254. {
  255. for (size_t i = 0; i < font_list.num; i++)
  256. font_path_info_free(font_list.array + i);
  257. da_free(font_list);
  258. }
  259. static inline size_t get_rating(struct font_path_info *info, struct dstr *cmp)
  260. {
  261. const char *src = info->face_and_style;
  262. const char *dst = cmp->array;
  263. size_t num = 0;
  264. do {
  265. char ch1 = (char)toupper(*src);
  266. char ch2 = (char)toupper(*dst);
  267. if (ch1 != ch2)
  268. break;
  269. num++;
  270. } while (*src++ && *dst++);
  271. return num;
  272. }
  273. const char *get_font_path(const char *family, uint16_t size, const char *style,
  274. uint32_t flags, FT_Long *idx)
  275. {
  276. const char *best_path = NULL;
  277. double best_rating = 0.0;
  278. struct dstr face_and_style = {0};
  279. struct dstr style_str = {0};
  280. bool bold = !!(flags & OBS_FONT_BOLD);
  281. bool italic = !!(flags & OBS_FONT_ITALIC);
  282. if (!family || !*family)
  283. return NULL;
  284. if (style) {
  285. dstr_copy(&style_str, style);
  286. dstr_replace(&style_str, "Bold", "");
  287. dstr_replace(&style_str, "Italic", "");
  288. dstr_replace(&style_str, " ", " ");
  289. dstr_depad(&style_str);
  290. }
  291. dstr_copy(&face_and_style, family);
  292. if (!dstr_is_empty(&style_str)) {
  293. dstr_cat(&face_and_style, " ");
  294. dstr_cat_dstr(&face_and_style, &style_str);
  295. }
  296. for (size_t i = 0; i < font_list.num; i++) {
  297. struct font_path_info *info = font_list.array + i;
  298. double rating = (double)get_rating(info, &face_and_style);
  299. if (rating < info->face_len)
  300. continue;
  301. if (info->is_bitmap) {
  302. int best_diff = 1000;
  303. for (uint32_t j = 0; j < info->num_sizes; j++) {
  304. int diff = abs(info->sizes[j] - size);
  305. if (diff < best_diff)
  306. best_diff = diff;
  307. }
  308. rating /= (double)(best_diff + 1.0);
  309. }
  310. if (info->bold == bold)
  311. rating += 1.0;
  312. if (info->italic == italic)
  313. rating += 1.0;
  314. if (rating > best_rating) {
  315. best_path = info->path;
  316. *idx = info->index;
  317. best_rating = rating;
  318. }
  319. }
  320. dstr_free(&style_str);
  321. dstr_free(&face_and_style);
  322. return best_path;
  323. }