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