find-font.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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) return false;
  48. da_init(font_list);
  49. da_resize(font_list, count);
  50. #define do_read(var) \
  51. success = read_var(s, var); \
  52. if (!success) break
  53. for (int i = 0; i < count; i++) {
  54. struct font_path_info *info = &font_list.array[i];
  55. success = read_str(s, &info->face_and_style);
  56. if (!success) break;
  57. do_read(info->full_len);
  58. do_read(info->face_len);
  59. do_read(info->is_bitmap);
  60. do_read(info->num_sizes);
  61. info->sizes = bmalloc(sizeof(int) * info->num_sizes);
  62. success = read_data(s, info->sizes,
  63. sizeof(int) * info->num_sizes);
  64. if (!success) break;
  65. do_read(info->bold);
  66. success = read_str(s, &info->path);
  67. if (!success) break;
  68. do_read(info->italic);
  69. do_read(info->index);
  70. }
  71. #undef do_read
  72. if (!success) {
  73. free_os_font_list();
  74. return false;
  75. }
  76. return true;
  77. }
  78. extern uint32_t get_font_checksum();
  79. static const uint32_t font_cache_ver = 1;
  80. bool load_cached_os_font_list(void)
  81. {
  82. char *file_name = obs_module_config_path("font_data.bin");
  83. uint32_t old_checksum;
  84. uint32_t new_checksum;
  85. struct serializer s;
  86. uint32_t ver;
  87. bool success;
  88. success = file_input_serializer_init(&s, file_name);
  89. bfree(file_name);
  90. if (!success)
  91. return false;
  92. success = read_data(&s, &ver, sizeof(ver));
  93. if (!success || ver != font_cache_ver) {
  94. success = false;
  95. goto finish;
  96. }
  97. success = s_read(&s, &old_checksum, sizeof(old_checksum));
  98. new_checksum = get_font_checksum();
  99. if (!success || old_checksum != new_checksum) {
  100. success = false;
  101. goto finish;
  102. }
  103. success = load_cached_font_list(&s);
  104. finish:
  105. file_input_serializer_free(&s);
  106. return success;
  107. }
  108. void save_font_list(void)
  109. {
  110. char *file_name = obs_module_config_path("font_data.bin");
  111. uint32_t font_checksum = get_font_checksum();
  112. int font_count = (int)font_list.num;
  113. struct serializer s;
  114. bool success = false;
  115. if (font_checksum)
  116. success = file_output_serializer_init_safe(&s, file_name,
  117. "tmp");
  118. bfree(file_name);
  119. if (!success)
  120. return;
  121. success = write_var(&s, font_cache_ver);
  122. if (!success) return;
  123. success = write_var(&s, font_checksum);
  124. if (!success) return;
  125. success = write_var(&s, font_count);
  126. if (!success) return;
  127. #define do_write(var) \
  128. success = write_var(&s, var); \
  129. if (!success) break
  130. for (size_t i = 0; i < font_list.num; i++) {
  131. struct font_path_info *info = &font_list.array[i];
  132. success = write_str(&s, info->face_and_style);
  133. if (!success) break;
  134. do_write(info->full_len);
  135. do_write(info->face_len);
  136. do_write(info->is_bitmap);
  137. do_write(info->num_sizes);
  138. success = write_data(&s, info->sizes,
  139. sizeof(int) * info->num_sizes);
  140. if (!success) break;
  141. do_write(info->bold);
  142. success = write_str(&s, info->path);
  143. if (!success) break;
  144. do_write(info->italic);
  145. do_write(info->index);
  146. }
  147. #undef do_write
  148. file_output_serializer_free(&s);
  149. }
  150. static void create_bitmap_sizes(struct font_path_info *info, FT_Face face)
  151. {
  152. DARRAY(int) sizes;
  153. if (!info->is_bitmap) {
  154. info->num_sizes = 0;
  155. info->sizes = NULL;
  156. return;
  157. }
  158. da_init(sizes);
  159. da_reserve(sizes, face->num_fixed_sizes);
  160. for (int i = 0; i < face->num_fixed_sizes; i++) {
  161. int val = face->available_sizes[i].size >> 6;
  162. da_push_back(sizes, &val);
  163. }
  164. info->sizes = sizes.array;
  165. info->num_sizes = (uint32_t)face->num_fixed_sizes;
  166. }
  167. static void add_font_path(FT_Face face,
  168. FT_Long idx,
  169. const char *family_in,
  170. const char *style_in,
  171. const char *path)
  172. {
  173. struct dstr face_and_style = {0};
  174. struct font_path_info info;
  175. if (!family_in || !path)
  176. return;
  177. dstr_copy(&face_and_style, family_in);
  178. if (face->style_name) {
  179. struct dstr style = {0};
  180. dstr_copy(&style, style_in);
  181. dstr_replace(&style, "Bold", "");
  182. dstr_replace(&style, "Italic", "");
  183. dstr_replace(&style, " ", " ");
  184. dstr_depad(&style);
  185. if (!dstr_is_empty(&style)) {
  186. dstr_cat(&face_and_style, " ");
  187. dstr_cat_dstr(&face_and_style, &style);
  188. }
  189. dstr_free(&style);
  190. }
  191. info.face_and_style = face_and_style.array;
  192. info.full_len = (uint32_t)face_and_style.len;
  193. info.face_len = (uint32_t)strlen(family_in);
  194. info.is_bitmap = !!(face->face_flags & FT_FACE_FLAG_FIXED_SIZES);
  195. info.bold = !!(face->style_flags & FT_STYLE_FLAG_BOLD);
  196. info.italic = !!(face->style_flags & FT_STYLE_FLAG_ITALIC);
  197. info.index = idx;
  198. info.path = bstrdup(path);
  199. create_bitmap_sizes(&info, face);
  200. da_push_back(font_list, &info);
  201. /*blog(LOG_DEBUG, "name: %s\n\tstyle: %s\n\tpath: %s\n",
  202. family_in,
  203. style_in,
  204. path);*/
  205. }
  206. void build_font_path_info(FT_Face face, FT_Long idx, const char *path)
  207. {
  208. FT_UInt num_names = FT_Get_Sfnt_Name_Count(face);
  209. DARRAY(char*) family_names;
  210. da_init(family_names);
  211. da_push_back(family_names, &face->family_name);
  212. for (FT_UInt i = 0; i < num_names; i++) {
  213. FT_SfntName name;
  214. char *family;
  215. FT_Error ret = FT_Get_Sfnt_Name(face, i, &name);
  216. if (ret != 0 || name.name_id != TT_NAME_ID_FONT_FAMILY)
  217. continue;
  218. family = sfnt_name_to_utf8(&name);
  219. if (!family)
  220. continue;
  221. for (size_t i = 0; i < family_names.num; i++) {
  222. if (astrcmpi(family_names.array[i], family) == 0) {
  223. bfree(family);
  224. family = NULL;
  225. break;
  226. }
  227. }
  228. if (family)
  229. da_push_back(family_names, &family);
  230. }
  231. for (size_t i = 0; i < family_names.num; i++) {
  232. add_font_path(face, idx, family_names.array[i],
  233. face->style_name, path);
  234. /* first item isn't our allocation */
  235. if (i > 0)
  236. bfree(family_names.array[i]);
  237. }
  238. da_free(family_names);
  239. }
  240. void free_os_font_list(void)
  241. {
  242. for (size_t i = 0; i < font_list.num; i++)
  243. font_path_info_free(font_list.array + i);
  244. da_free(font_list);
  245. }
  246. static inline size_t get_rating(struct font_path_info *info, struct dstr *cmp)
  247. {
  248. const char *src = info->face_and_style;
  249. const char *dst = cmp->array;
  250. size_t num = 0;
  251. do {
  252. char ch1 = (char)toupper(*src);
  253. char ch2 = (char)toupper(*dst);
  254. if (ch1 != ch2)
  255. break;
  256. num++;
  257. } while (*src++ && *dst++);
  258. return num;
  259. }
  260. const char *get_font_path(const char *family, uint16_t size, const char *style,
  261. uint32_t flags, FT_Long *idx)
  262. {
  263. const char *best_path = NULL;
  264. double best_rating = 0.0;
  265. struct dstr face_and_style = {0};
  266. struct dstr style_str = {0};
  267. bool bold = !!(flags & OBS_FONT_BOLD);
  268. bool italic = !!(flags & OBS_FONT_ITALIC);
  269. if (!family || !*family)
  270. return NULL;
  271. if (style) {
  272. dstr_copy(&style_str, style);
  273. dstr_replace(&style_str, "Bold", "");
  274. dstr_replace(&style_str, "Italic", "");
  275. dstr_replace(&style_str, " ", " ");
  276. dstr_depad(&style_str);
  277. }
  278. dstr_copy(&face_and_style, family);
  279. if (!dstr_is_empty(&style_str)) {
  280. dstr_cat(&face_and_style, " ");
  281. dstr_cat_dstr(&face_and_style, &style_str);
  282. }
  283. for (size_t i = 0; i < font_list.num; i++) {
  284. struct font_path_info *info = font_list.array + i;
  285. double rating = (double)get_rating(info, &face_and_style);
  286. if (rating < info->face_len)
  287. continue;
  288. if (info->is_bitmap) {
  289. int best_diff = 1000;
  290. for (uint32_t j = 0; j < info->num_sizes; j++) {
  291. int diff = abs(info->sizes[j] - size);
  292. if (diff < best_diff)
  293. best_diff = diff;
  294. }
  295. rating /= (double)(best_diff + 1.0);
  296. }
  297. if (info->bold == bold) rating += 1.0;
  298. if (info->italic == italic) rating += 1.0;
  299. if (rating > best_rating) {
  300. best_path = info->path;
  301. *idx = info->index;
  302. best_rating = rating;
  303. }
  304. }
  305. dstr_free(&style_str);
  306. dstr_free(&face_and_style);
  307. return best_path;
  308. }