mupdf_load_system_font.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. // this file is compiled as part of mupdf library and ends up
  2. // in libmupdf.dll, to avoid issues related to crossing .dll boundaries
  3. // It implements loading of Fonts included in windows
  4. #include "mupdf/fitz.h"
  5. #include "mupdf/ucdn.h"
  6. #include "mupdf/pdf.h"
  7. #ifdef _WIN32
  8. #ifndef UNICODE
  9. #define UNICODE
  10. #endif
  11. #ifndef _UNICODE
  12. #define _UNICODE
  13. #endif
  14. #include <windows.h>
  15. #include <assert.h>
  16. #pragma unmanaged
  17. typedef unsigned int u32;
  18. typedef unsigned short u16;
  19. // TODO: Use more of FreeType for TTF parsing (for performance reasons,
  20. // the fonts can't be parsed completely, though)
  21. #include <ft2build.h>
  22. #include "ftheader.h"
  23. #include FT_TRUETYPE_IDS_H
  24. #include FT_TRUETYPE_TAGS_H
  25. #define TTC_VERSION1 0x00010000
  26. #define TTC_VERSION2 0x00020000
  27. #define MAX_FACENAME 256
  28. #define MAX_FONTPATH 1024
  29. #define MAX_FONT_FILES 1024
  30. #define MAX_FONTS 4096
  31. typedef struct {
  32. const char* file_path;
  33. void* data;
  34. size_t size;
  35. } font_file;
  36. typedef struct {
  37. const char* fontface;
  38. u32 index;
  39. u32 file_idx;
  40. } win_font_info;
  41. typedef struct {
  42. win_font_info fontmap[MAX_FONTS];
  43. int len;
  44. int cap;
  45. } win_fonts;
  46. typedef struct {
  47. font_file files[MAX_FONT_FILES];
  48. int len;
  49. int cap;
  50. } font_files;
  51. font_files g_font_files;
  52. typedef struct {
  53. ULONG uVersion;
  54. USHORT uNumOfTables;
  55. USHORT uSearchRange;
  56. USHORT uEntrySelector;
  57. USHORT uRangeShift;
  58. } TT_OFFSET_TABLE;
  59. typedef struct {
  60. ULONG uTag; // table name
  61. ULONG uCheckSum; // Check sum
  62. ULONG uOffset; // Offset from beginning of file
  63. ULONG uLength; // length of the table in bytes
  64. } TT_TABLE_DIRECTORY;
  65. typedef struct {
  66. USHORT uFSelector; // format selector. Always 0
  67. USHORT uNRCount; // Name Records count
  68. USHORT uStorageOffset; // Offset for strings storage, from start of the table
  69. } TT_NAME_TABLE_HEADER;
  70. typedef struct {
  71. USHORT uPlatformID;
  72. USHORT uEncodingID;
  73. USHORT uLanguageID;
  74. USHORT uNameID;
  75. USHORT uStringLength;
  76. USHORT uStringOffset; // from start of storage area
  77. } TT_NAME_RECORD;
  78. typedef struct {
  79. ULONG Tag;
  80. ULONG Version;
  81. ULONG NumFonts;
  82. } FONT_COLLECTION;
  83. static struct {
  84. const char* name;
  85. const char* pattern;
  86. } baseSubstitutes[] = {
  87. {"Courier", "CourierNewPSMT"},
  88. {"Courier-Bold", "CourierNewPS-BoldMT"},
  89. {"Courier-Oblique", "CourierNewPS-ItalicMT"},
  90. {"Courier-BoldOblique", "CourierNewPS-BoldItalicMT"},
  91. {"Helvetica", "ArialMT"},
  92. {"Helvetica-Bold", "Arial-BoldMT"},
  93. {"Helvetica-Oblique", "Arial-ItalicMT"},
  94. {"Helvetica-BoldOblique", "Arial-BoldItalicMT"},
  95. {"Times-Roman", "TimesNewRomanPSMT"},
  96. {"Times-Bold", "TimesNewRomanPS-BoldMT"},
  97. {"Times-Italic", "TimesNewRomanPS-ItalicMT"},
  98. {"Times-BoldItalic", "TimesNewRomanPS-BoldItalicMT"},
  99. {"Symbol", "SymbolMT"},
  100. };
  101. static win_fonts g_win_fonts;
  102. static int did_init = 0;
  103. static CRITICAL_SECTION cs_fonts;
  104. static int streq(const char* s1, const char* s2) {
  105. if (strcmp(s1, s2) == 0) {
  106. return 1;
  107. }
  108. return 0;
  109. }
  110. static int streqi(const char* s1, const char* s2) {
  111. if (_stricmp(s1, s2) == 0) {
  112. return 1;
  113. }
  114. return 0;
  115. }
  116. static inline USHORT BEtoHs(USHORT x) {
  117. BYTE* data = (BYTE*)&x;
  118. return (data[0] << 8) | data[1];
  119. }
  120. static inline ULONG BEtoHl(ULONG x) {
  121. BYTE* data = (BYTE*)&x;
  122. return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
  123. }
  124. /* A little bit more sophisticated name matching so that e.g. "EurostileExtended"
  125. matches "EurostileExtended-Roman" or "Tahoma-Bold,Bold" matches "Tahoma-Bold" */
  126. static int cmp_font_name(const char* name1, const char* name2) {
  127. int len1 = strlen(name1);
  128. int len2 = strlen(name2);
  129. if (len1 != len2) {
  130. const char* rest = len1 > len2 ? name1 + len2 : name2 + len1;
  131. if (',' == *rest || streqi(rest, "-roman"))
  132. return _strnicmp(name1, name2, fz_mini(len1, len2));
  133. }
  134. return _stricmp(name1, name2);
  135. }
  136. static int font_name_eq(const char* name1, const char* name2) {
  137. return cmp_font_name(name1, name2) == 0;
  138. }
  139. static int cmp_win_font_info(const void* el1, const void* el2) {
  140. win_font_info* i1 = (win_font_info*)el1;
  141. win_font_info* i2 = (win_font_info*)el2;
  142. return cmp_font_name(i1->fontface, i2->fontface);
  143. }
  144. static win_font_info* pdf_find_windows_font_path(const char* fontname) {
  145. win_font_info* map = &(g_win_fonts.fontmap[0]);
  146. size_t n = (size_t)g_win_fonts.len;
  147. size_t elSize = sizeof(win_font_info);
  148. win_font_info el;
  149. el.fontface = fontname;
  150. el.index = 0;
  151. win_font_info* res = (win_font_info*)bsearch(&el, map, n, elSize, cmp_win_font_info);
  152. return res;
  153. }
  154. /* source and dest can be same */
  155. static void decode_unicode_BE(fz_context* ctx, char* source, int sourcelen, char* dest, int destlen) {
  156. WCHAR* tmp;
  157. int converted, i;
  158. if (sourcelen % 2 != 0)
  159. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : invalid unicode string");
  160. tmp = fz_malloc_array(ctx, sourcelen / 2 + 1, WCHAR);
  161. for (i = 0; i < sourcelen / 2; i++)
  162. tmp[i] = BEtoHs(((WCHAR*)source)[i]);
  163. tmp[sourcelen / 2] = '\0';
  164. converted = WideCharToMultiByte(CP_UTF8, 0, tmp, -1, dest, destlen, NULL, NULL);
  165. fz_free(ctx, tmp);
  166. if (!converted)
  167. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : invalid unicode string");
  168. }
  169. static void decode_platform_string(fz_context* ctx, int platform, int enctype, char* source, int sourcelen, char* dest,
  170. int destlen) {
  171. switch (platform) {
  172. case TT_PLATFORM_APPLE_UNICODE:
  173. switch (enctype) {
  174. case TT_APPLE_ID_DEFAULT:
  175. case TT_APPLE_ID_UNICODE_2_0:
  176. decode_unicode_BE(ctx, source, sourcelen, dest, destlen);
  177. return;
  178. }
  179. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : unsupported encoding (%d/%d)", platform, enctype);
  180. case TT_PLATFORM_MACINTOSH:
  181. switch (enctype) {
  182. case TT_MAC_ID_ROMAN:
  183. if (sourcelen + 1 > destlen)
  184. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : overlong fontname: %s", source);
  185. // TODO: Convert to UTF-8 from what encoding?
  186. memcpy(dest, source, sourcelen);
  187. dest[sourcelen] = 0;
  188. return;
  189. }
  190. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : unsupported encoding (%d/%d)", platform, enctype);
  191. case TT_PLATFORM_MICROSOFT:
  192. switch (enctype) {
  193. case TT_MS_ID_SYMBOL_CS:
  194. case TT_MS_ID_UNICODE_CS:
  195. case TT_MS_ID_UCS_4:
  196. decode_unicode_BE(ctx, source, sourcelen, dest, destlen);
  197. return;
  198. }
  199. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : unsupported encoding (%d/%d)", platform, enctype);
  200. default:
  201. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : unsupported encoding (%d/%d)", platform, enctype);
  202. }
  203. }
  204. // on my machine it's ~21k for facename and path
  205. static int g_font_allocated = 0;
  206. static int get_font_file(const char* file_path) {
  207. int i;
  208. font_file* ff;
  209. for (i = 0; i < g_font_files.len; i++) {
  210. ff = &(g_font_files.files[i]);
  211. if (streq(file_path, ff->file_path)) {
  212. return i;
  213. }
  214. }
  215. return -1;
  216. }
  217. static int get_or_append_font_file(const char* file_path) {
  218. font_file* ff;
  219. int i = get_font_file(file_path);
  220. if (i >= 0) {
  221. return i;
  222. }
  223. if (g_font_files.len >= g_font_files.cap) {
  224. return -1;
  225. }
  226. i = g_font_files.len;
  227. ff = &g_font_files.files[i];
  228. g_font_allocated += strlen(file_path) + 1;
  229. ff->file_path = strdup(file_path);
  230. ff->data = NULL;
  231. ff->size = 0;
  232. g_font_files.len++;
  233. return i;
  234. }
  235. static void append_mapping(fz_context* ctx, const char* facename, const char* path, int index) {
  236. win_fonts* fl = &g_win_fonts;
  237. int file_idx = get_or_append_font_file(path);
  238. if (file_idx < 0) {
  239. return;
  240. }
  241. if (fl->len >= fl->cap) {
  242. // fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : fontlist overflow");
  243. return;
  244. }
  245. win_font_info* i = &fl->fontmap[fl->len];
  246. g_font_allocated += strlen(facename) + 1;
  247. // TODO: allocate facename and path from a pool allocator
  248. i->fontface = strdup(facename);
  249. i->file_idx = (u32)file_idx;
  250. i->index = (u32)index;
  251. fl->len++;
  252. }
  253. static void safe_read(fz_context* ctx, fz_stream* file, int offset, char* buf, int size) {
  254. int n;
  255. fz_seek(ctx, file, offset, SEEK_SET);
  256. n = fz_read(ctx, file, (unsigned char*)buf, size);
  257. if (n != size)
  258. fz_throw(ctx, FZ_ERROR_GENERIC, "safe_read: read %d, expected %d", n, size);
  259. }
  260. static void read_ttf_string(fz_context* ctx, fz_stream* file, int offset, TT_NAME_RECORD* ttRecordBE, char* buf,
  261. int size) {
  262. char szTemp[MAX_FACENAME * 2];
  263. // ignore empty and overlong strings
  264. int stringLength = BEtoHs(ttRecordBE->uStringLength);
  265. if (stringLength == 0 || stringLength >= sizeof(szTemp))
  266. return;
  267. safe_read(ctx, file, offset + BEtoHs(ttRecordBE->uStringOffset), szTemp, stringLength);
  268. decode_platform_string(ctx, BEtoHs(ttRecordBE->uPlatformID), BEtoHs(ttRecordBE->uEncodingID), szTemp, stringLength,
  269. buf, size);
  270. }
  271. static void remove_spaces(char* srcDest) {
  272. char* dest;
  273. for (dest = srcDest; *srcDest; srcDest++) {
  274. if (*srcDest != ' ') {
  275. *dest++ = *srcDest;
  276. }
  277. }
  278. *dest = '\0';
  279. }
  280. static void makeFakePSName(char szName[MAX_FACENAME], const char* szStyle) {
  281. // append the font's subfamily, unless it's a Regular font
  282. if (*szStyle && !streqi(szStyle, "Regular")) {
  283. fz_strlcat(szName, "-", MAX_FACENAME);
  284. fz_strlcat(szName, szStyle, MAX_FACENAME);
  285. }
  286. remove_spaces(szName);
  287. }
  288. static void parseTTF(fz_context* ctx, fz_stream* file, int offset, int index, const char* path) {
  289. TT_OFFSET_TABLE ttOffsetTableBE;
  290. TT_TABLE_DIRECTORY tblDirBE;
  291. TT_NAME_TABLE_HEADER ttNTHeaderBE;
  292. TT_NAME_RECORD ttRecordBE;
  293. char szPSName[MAX_FACENAME] = {0};
  294. char szTTName[MAX_FACENAME] = {0};
  295. char szStyle[MAX_FACENAME] = {0};
  296. char szCJKName[MAX_FACENAME] = {0};
  297. int i, count, tblOffset;
  298. safe_read(ctx, file, offset, (char*)&ttOffsetTableBE, sizeof(TT_OFFSET_TABLE));
  299. // check if this is a TrueType font of version 1.0 or an OpenType font
  300. if (BEtoHl(ttOffsetTableBE.uVersion) != TTC_VERSION1 && BEtoHl(ttOffsetTableBE.uVersion) != TTAG_OTTO) {
  301. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : invalid font '%s', invalid version %x", path,
  302. BEtoHl(ttOffsetTableBE.uVersion));
  303. }
  304. // determine the name table's offset by iterating through the offset table
  305. count = BEtoHs(ttOffsetTableBE.uNumOfTables);
  306. for (i = 0; i < count; i++) {
  307. int entryOffset = offset + sizeof(TT_OFFSET_TABLE) + i * sizeof(TT_TABLE_DIRECTORY);
  308. safe_read(ctx, file, entryOffset, (char*)&tblDirBE, sizeof(TT_TABLE_DIRECTORY));
  309. if (!BEtoHl(tblDirBE.uTag) || BEtoHl(tblDirBE.uTag) == TTAG_name) {
  310. break;
  311. }
  312. }
  313. if (count == i || !BEtoHl(tblDirBE.uTag)) {
  314. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : nameless font");
  315. }
  316. tblOffset = BEtoHl(tblDirBE.uOffset);
  317. // read the 'name' table for record count and offsets
  318. safe_read(ctx, file, tblOffset, (char*)&ttNTHeaderBE, sizeof(TT_NAME_TABLE_HEADER));
  319. offset = tblOffset + sizeof(TT_NAME_TABLE_HEADER);
  320. tblOffset += BEtoHs(ttNTHeaderBE.uStorageOffset);
  321. // read through the strings for PostScript name and font family
  322. count = BEtoHs(ttNTHeaderBE.uNRCount);
  323. for (i = 0; i < count; i++) {
  324. short langId, nameId;
  325. BOOL isCJKName;
  326. safe_read(ctx, file, offset + i * sizeof(TT_NAME_RECORD), (char*)&ttRecordBE, sizeof(TT_NAME_RECORD));
  327. langId = BEtoHs(ttRecordBE.uLanguageID);
  328. nameId = BEtoHs(ttRecordBE.uNameID);
  329. isCJKName = TT_NAME_ID_FONT_FAMILY == nameId && LANG_CHINESE == PRIMARYLANGID(langId);
  330. // ignore non-English strings (except for Chinese font names)
  331. if (langId && langId != TT_MS_LANGID_ENGLISH_UNITED_STATES && !isCJKName) {
  332. continue;
  333. }
  334. // ignore names other than font (sub)family and PostScript name
  335. fz_try(ctx) {
  336. if (isCJKName) {
  337. read_ttf_string(ctx, file, tblOffset, &ttRecordBE, szCJKName, sizeof(szCJKName));
  338. } else if (TT_NAME_ID_FONT_FAMILY == nameId) {
  339. read_ttf_string(ctx, file, tblOffset, &ttRecordBE, szTTName, sizeof(szTTName));
  340. } else if (TT_NAME_ID_FONT_SUBFAMILY == nameId) {
  341. read_ttf_string(ctx, file, tblOffset, &ttRecordBE, szStyle, sizeof(szStyle));
  342. } else if (TT_NAME_ID_PS_NAME == nameId) {
  343. read_ttf_string(ctx, file, tblOffset, &ttRecordBE, szPSName, sizeof(szPSName));
  344. }
  345. }
  346. fz_catch(ctx) {
  347. fz_report_error(ctx);
  348. fz_warn(ctx, "ignoring face name decoding fonterror");
  349. }
  350. }
  351. // try to prevent non-Arial fonts from accidentally substituting Arial
  352. if (streq(szPSName, "ArialMT")) {
  353. // cf. https://code.google.com/p/sumatrapdf/issues/detail?id=2471
  354. if (!streq(szTTName, "Arial")) {
  355. szPSName[0] = '\0';
  356. } else if (strstr(path, "caps") || strstr(path, "Caps")) {
  357. // TODO: is there a better way to distinguish Arial Caps from Arial proper?
  358. // cf. https://code.google.com/p/sumatrapdf/issues/detail?id=1290
  359. fz_throw(ctx, FZ_ERROR_GENERIC, "ignore %s, as it can't be distinguished from Arial,Regular", path);
  360. }
  361. }
  362. if (szPSName[0]) {
  363. append_mapping(ctx, szPSName, path, index);
  364. }
  365. if (szTTName[0]) {
  366. // derive a PostScript-like name and add it, if it's different from the font's
  367. // included PostScript name; cf. https://code.google.com/p/sumatrapdf/issues/detail?id=376
  368. // compare the two names before adding this one
  369. if (!font_name_eq(szTTName, szPSName)) {
  370. append_mapping(ctx, szTTName, path, index);
  371. }
  372. }
  373. if (szCJKName[0]) {
  374. makeFakePSName(szCJKName, szStyle);
  375. if (!font_name_eq(szCJKName, szPSName) && !font_name_eq(szCJKName, szTTName)) {
  376. append_mapping(ctx, szCJKName, path, index);
  377. }
  378. }
  379. }
  380. static void parseTTFs(fz_context* ctx, const char* path) {
  381. fz_stream* file = 0;
  382. fz_try(ctx) {
  383. file = fz_open_file(ctx, path);
  384. parseTTF(ctx, file, 0, 0, path);
  385. }
  386. fz_always(ctx) {
  387. fz_drop_stream(ctx, file);
  388. }
  389. fz_catch(ctx) {
  390. fz_rethrow(ctx);
  391. }
  392. }
  393. static void parseTTCs(fz_context* ctx, const char* path) {
  394. FONT_COLLECTION fontcollectionBE;
  395. ULONG i, numFonts, *offsettableBE = NULL;
  396. fz_stream* file = fz_open_file(ctx, path);
  397. fz_var(offsettableBE);
  398. fz_try(ctx) {
  399. safe_read(ctx, file, 0, (char*)&fontcollectionBE, sizeof(FONT_COLLECTION));
  400. if (BEtoHl(fontcollectionBE.Tag) != TTAG_ttcf) {
  401. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : wrong format %x", BEtoHl(fontcollectionBE.Tag));
  402. }
  403. if (BEtoHl(fontcollectionBE.Version) != TTC_VERSION1 && BEtoHl(fontcollectionBE.Version) != TTC_VERSION2) {
  404. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror : invalid version %x", BEtoHl(fontcollectionBE.Version));
  405. }
  406. numFonts = BEtoHl(fontcollectionBE.NumFonts);
  407. offsettableBE = fz_malloc_array(ctx, numFonts, ULONG);
  408. int offset = (int)sizeof(FONT_COLLECTION);
  409. safe_read(ctx, file, offset, (char*)offsettableBE, numFonts * sizeof(ULONG));
  410. for (i = 0; i < numFonts; i++) {
  411. parseTTF(ctx, file, BEtoHl(offsettableBE[i]), i, path);
  412. }
  413. }
  414. fz_always(ctx) {
  415. fz_free(ctx, offsettableBE);
  416. fz_drop_stream(ctx, file);
  417. }
  418. fz_catch(ctx) {
  419. fz_rethrow(ctx);
  420. }
  421. }
  422. static void extend_system_font_list(fz_context* ctx, const WCHAR* path) {
  423. WCHAR szPath[MAX_PATH], *lpFileName;
  424. WIN32_FIND_DATA FileData;
  425. HANDLE hList;
  426. GetFullPathNameW(path, nelem(szPath), szPath, &lpFileName);
  427. hList = FindFirstFile(szPath, &FileData);
  428. if (hList == INVALID_HANDLE_VALUE) {
  429. // Don't complain about missing directories
  430. if (GetLastError() == ERROR_FILE_NOT_FOUND) {
  431. return;
  432. }
  433. fz_throw(ctx, FZ_ERROR_GENERIC, "extend_system_font_list: unknown error %d", GetLastError());
  434. }
  435. do {
  436. if (!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  437. char szPathUtf8[MAX_PATH], *fileExt;
  438. int res;
  439. lstrcpyn(lpFileName, FileData.cFileName, szPath + MAX_PATH - lpFileName);
  440. res = WideCharToMultiByte(CP_UTF8, 0, szPath, -1, szPathUtf8, sizeof(szPathUtf8), NULL, NULL);
  441. if (!res) {
  442. fz_warn(ctx, "WideCharToMultiByte failed");
  443. continue;
  444. }
  445. fileExt = szPathUtf8 + strlen(szPathUtf8) - 4;
  446. fz_try(ctx) {
  447. if (streqi(fileExt, ".ttc")) {
  448. parseTTCs(ctx, szPathUtf8);
  449. } else if (streqi(fileExt, ".ttf") || streqi(fileExt, ".otf")) {
  450. parseTTFs(ctx, szPathUtf8);
  451. }
  452. }
  453. fz_catch(ctx) {
  454. fz_report_error(ctx);
  455. // ignore errors occurring while parsing a given font file
  456. }
  457. }
  458. } while (FindNextFile(hList, &FileData));
  459. FindClose(hList);
  460. }
  461. // cf. https://blogs.msdn.com/b/oldnewthing/archive/2004/10/25/247180.aspx
  462. EXTERN_C IMAGE_DOS_HEADER __ImageBase;
  463. #define CURRENT_HMODULE ((HMODULE) & __ImageBase)
  464. // clang-cl notices the mismatch in function parameters with qsort
  465. // as _stricmp is int _stricmp(const char *string1, const char *string2);
  466. // and qsort expects int (*compar)(const void*,const void*)).
  467. static int stricmp_wrapper(const void* ptr1, const void* ptr2) {
  468. const char* string1 = (const char*)ptr1;
  469. const char* string2 = (const char*)ptr2;
  470. return _stricmp(string1, string2);
  471. }
  472. static void create_system_font_list(fz_context* ctx) {
  473. WCHAR szFontDir[MAX_PATH];
  474. UINT cch;
  475. cch = GetWindowsDirectory(szFontDir, nelem(szFontDir) - 12);
  476. if (0 < cch && cch < nelem(szFontDir) - 12) {
  477. wcscat_s(szFontDir, MAX_PATH, L"\\Fonts\\*.?t?");
  478. extend_system_font_list(ctx, szFontDir);
  479. }
  480. if (g_win_fonts.len == 0) {
  481. fz_warn(ctx, "couldn't find any usable system fonts");
  482. }
  483. #ifdef NOCJKFONT
  484. {
  485. // If no CJK fallback font is builtin but one has been shipped separately (in the same
  486. // directory as the main executable), add it to the list of loadable system fonts
  487. WCHAR szFile[MAX_PATH], *lpFileName;
  488. szFile[0] = '\0';
  489. GetModuleFileName(CURRENT_HMODULE, szFontDir, MAX_PATH);
  490. szFontDir[nelem(szFontDir) - 1] = '\0';
  491. GetFullPathNameW(szFontDir, MAX_PATH, szFile, &lpFileName);
  492. lstrcpyn(lpFileName, L"DroidSansFallback.ttf", szFile + MAX_PATH - lpFileName);
  493. extend_system_font_list(ctx, szFile);
  494. }
  495. #endif
  496. // sort the font list, so that it can be searched binarily
  497. void* map = (void*)&(g_win_fonts.fontmap[0]);
  498. size_t n = (size_t)g_win_fonts.len;
  499. size_t elSize = sizeof(win_font_info);
  500. qsort(map, n, elSize, cmp_win_font_info);
  501. #ifdef DEBUG
  502. // allow to overwrite system fonts for debugging purposes
  503. // (either pass a full path or a search pattern such as "fonts\*.ttf")
  504. cch = GetEnvironmentVariable(L"MUPDF_FONTS_PATTERN", szFontDir, nelem(szFontDir));
  505. if (0 < cch && cch < nelem(szFontDir)) {
  506. int i, prev_len = g_win_fonts.len;
  507. extend_system_font_list(ctx, szFontDir);
  508. for (i = prev_len; i < g_win_fonts.len; i++) {
  509. win_font_info* entry = bsearch(g_win_fonts.fontmap[i].fontface, g_win_fonts.fontmap, prev_len,
  510. sizeof(win_font_info), cmp_win_font_info);
  511. if (entry) {
  512. *entry = g_win_fonts.fontmap[i];
  513. }
  514. }
  515. void* map = (void*)&(g_win_fonts.fontmap[0]);
  516. size_t n = (size_t)g_win_fonts.len;
  517. size_t elSize = sizeof(win_font_info);
  518. qsort(map, n, elSize, cmp_win_font_info);
  519. }
  520. #endif
  521. }
  522. // TODO(port): replace the caller
  523. static void* fz_resize_array(fz_context* ctx, void* p, unsigned int count, unsigned int size) {
  524. void* np = fz_realloc(ctx, p, count * size);
  525. if (!np)
  526. fz_throw(ctx, FZ_ERROR_GENERIC, "resize array (%d x %d bytes) failed", count, size);
  527. return np;
  528. }
  529. static fz_buffer* load_and_cache_font(fz_context* ctx, win_font_info* fi, const char* font_name) {
  530. fz_buffer* buffer = NULL;
  531. int file_idx = (int)fi->file_idx;
  532. font_file* ff;
  533. EnterCriticalSection(&cs_fonts);
  534. ff = &g_font_files.files[file_idx];
  535. if (ff->data) {
  536. buffer = fz_new_buffer_from_shared_data(ctx, ff->data, ff->size);
  537. fz_warn(ctx, "found cached font '%s' from '%s'", font_name, ff->file_path);
  538. }
  539. LeaveCriticalSection(&cs_fonts);
  540. if (buffer) {
  541. return buffer;
  542. }
  543. // can fz_throw so load outside of cs
  544. buffer = fz_read_file(ctx, ff->file_path);
  545. if (!buffer) {
  546. return NULL;
  547. }
  548. EnterCriticalSection(&cs_fonts);
  549. // TODO: free this data. will have to make a copy using allocator
  550. // not bound to ctx
  551. ff->size = fz_buffer_extract(ctx, buffer, (unsigned char**)&ff->data);
  552. buffer = fz_new_buffer_from_shared_data(ctx, ff->data, ff->size);
  553. LeaveCriticalSection(&cs_fonts);
  554. fz_warn(ctx, "loaded font '%s' from '%s'", font_name, ff->file_path);
  555. return buffer;
  556. }
  557. static int str_ends_with(const char* str, const char* end) {
  558. size_t len1 = strlen(str);
  559. size_t len2 = strlen(end);
  560. return len1 >= len2 && streq(str + len1 - len2, end);
  561. }
  562. static fz_font* load_windows_font_by_name(fz_context* ctx, const char* orig_name) {
  563. win_font_info* found = NULL;
  564. char *comma, *fontname;
  565. fz_font* font;
  566. fz_buffer* buffer;
  567. EnterCriticalSection(&cs_fonts);
  568. if (g_win_fonts.len == 0) {
  569. fz_try(ctx) {
  570. create_system_font_list(ctx);
  571. }
  572. fz_catch(ctx) {
  573. fz_report_error(ctx);
  574. }
  575. }
  576. LeaveCriticalSection(&cs_fonts);
  577. if (g_win_fonts.len == 0) {
  578. fz_throw(ctx, FZ_ERROR_GENERIC, "fonterror: couldn't find any fonts");
  579. }
  580. // work on a normalized copy of the font name
  581. fontname = fz_strdup(ctx, orig_name);
  582. remove_spaces(fontname);
  583. // first, try to find the exact font name (including appended style information)
  584. comma = strchr(fontname, ',');
  585. if (comma) {
  586. *comma = '-';
  587. found = pdf_find_windows_font_path(fontname);
  588. if (found) {
  589. goto Exit;
  590. }
  591. *comma = ',';
  592. } else {
  593. // second, substitute the font name with a known PostScript name
  594. int i;
  595. for (i = 0; i < nelem(baseSubstitutes) && !found; i++)
  596. if (streq(fontname, baseSubstitutes[i].name)) {
  597. found = pdf_find_windows_font_path(baseSubstitutes[i].pattern);
  598. if (found) {
  599. goto Exit;
  600. }
  601. }
  602. }
  603. // third, search for the font name without additional style information
  604. found = pdf_find_windows_font_path(fontname);
  605. if (found) {
  606. goto Exit;
  607. }
  608. // fourth, try to separate style from basename for prestyled fonts (e.g. "ArialBold")
  609. if (!comma && (str_ends_with(fontname, "Bold") || str_ends_with(fontname, "Italic"))) {
  610. int styleLen = str_ends_with(fontname, "Bold") ? 4 : str_ends_with(fontname, "BoldItalic") ? 10 : 6;
  611. fontname = (char*)fz_resize_array(ctx, fontname, strlen(fontname) + 2, sizeof(char));
  612. comma = fontname + strlen(fontname) - styleLen;
  613. memmove(comma + 1, comma, styleLen + 1);
  614. *comma = '-';
  615. found = pdf_find_windows_font_path(fontname);
  616. if (found) {
  617. goto Exit;
  618. }
  619. *comma = ',';
  620. found = pdf_find_windows_font_path(fontname);
  621. if (found) {
  622. goto Exit;
  623. }
  624. }
  625. // fifth, try to convert the font name from the common Chinese codepage 936
  626. if (fontname[0] < 0) {
  627. WCHAR cjkNameW[MAX_FACENAME];
  628. char cjkName[MAX_FACENAME];
  629. if (MultiByteToWideChar(936, MB_ERR_INVALID_CHARS, fontname, -1, cjkNameW, nelem(cjkNameW)) &&
  630. WideCharToMultiByte(CP_UTF8, 0, cjkNameW, -1, cjkName, nelem(cjkName), NULL, NULL)) {
  631. comma = strchr(cjkName, ',');
  632. if (comma) {
  633. *comma = '-';
  634. found = pdf_find_windows_font_path(cjkName);
  635. if (found) {
  636. goto Exit;
  637. }
  638. *comma = ',';
  639. }
  640. found = pdf_find_windows_font_path(cjkName);
  641. if (found) {
  642. goto Exit;
  643. }
  644. }
  645. }
  646. Exit:
  647. fz_free(ctx, fontname);
  648. if (!found) {
  649. fz_throw(ctx, FZ_ERROR_GENERIC, "couldn't find system font '%s'", orig_name);
  650. }
  651. buffer = load_and_cache_font(ctx, found, orig_name);
  652. int use_glyph_bbox = !streq(found->fontface, "DroidSansFallback");
  653. font = fz_new_font_from_buffer(ctx, orig_name, buffer, found->index, use_glyph_bbox);
  654. font->flags.ft_substitute = 1;
  655. return font;
  656. }
  657. static fz_font* load_windows_font(fz_context* ctx, const char* fontname, int bold, int italic,
  658. int needs_exact_metrics) {
  659. fz_font* font;
  660. const char* clean_name = pdf_clean_font_name(fontname);
  661. int is_base_14 = clean_name != fontname;
  662. /* metrics for Times-Roman don't match those of Windows' Times-Roman */
  663. /* https://code.google.com/p/sumatrapdf/issues/detail?id=2173 */
  664. /* https://github.com/sumatrapdfreader/sumatrapdf/issues/2108 */
  665. /* https://github.com/sumatrapdfreader/sumatrapdf/issues/2028 */
  666. /* TODO: should this always return NULL if is_base_14 is true? */
  667. if (is_base_14) {
  668. if (!strncmp(clean_name, "Times", 5)) {
  669. return NULL;
  670. }
  671. if (!strncmp(clean_name, "Helvetica", 9)) {
  672. return NULL;
  673. }
  674. if (!strncmp(clean_name, "Courier", 7)) {
  675. return NULL;
  676. }
  677. }
  678. if (needs_exact_metrics) {
  679. int len;
  680. if (fz_lookup_base14_font(ctx, fontname, &len))
  681. return NULL;
  682. if (clean_name != fontname && !strncmp(clean_name, "Times-", 6))
  683. return NULL;
  684. }
  685. font = load_windows_font_by_name(ctx, fontname);
  686. /* use the font's own metrics for base 14 fonts */
  687. if (is_base_14)
  688. font->flags.ft_substitute = 0;
  689. return font;
  690. }
  691. static fz_font* load_windows_cjk_font(fz_context* ctx, const char* fontname, int ros, int serif) {
  692. fz_font* font = NULL;
  693. /* try to find a matching system font before falling back to an approximate one */
  694. fz_try(ctx) {
  695. font = load_windows_font_by_name(ctx, fontname);
  696. }
  697. fz_catch(ctx) {
  698. fz_report_error(ctx);
  699. }
  700. if (font)
  701. return font;
  702. /* try to fall back to a reasonable system font */
  703. fz_try(ctx) {
  704. if (serif) {
  705. switch (ros) {
  706. case FZ_ADOBE_CNS:
  707. font = load_windows_font_by_name(ctx, "MingLiU");
  708. break;
  709. case FZ_ADOBE_GB:
  710. font = load_windows_font_by_name(ctx, "SimSun");
  711. break;
  712. case FZ_ADOBE_JAPAN:
  713. font = load_windows_font_by_name(ctx, "MS-Mincho");
  714. break;
  715. case FZ_ADOBE_KOREA:
  716. font = load_windows_font_by_name(ctx, "Batang");
  717. break;
  718. default:
  719. fz_throw(ctx, FZ_ERROR_GENERIC, "invalid serif ros");
  720. }
  721. } else {
  722. switch (ros) {
  723. case FZ_ADOBE_CNS:
  724. font = load_windows_font_by_name(ctx, "DFKaiShu-SB-Estd-BF");
  725. break;
  726. case FZ_ADOBE_GB:
  727. fz_try(ctx) {
  728. font = load_windows_font_by_name(ctx, "KaiTi");
  729. }
  730. fz_catch(ctx) {
  731. font = load_windows_font_by_name(ctx, "KaiTi_GB2312");
  732. fz_report_error(ctx);
  733. }
  734. break;
  735. case FZ_ADOBE_JAPAN:
  736. font = load_windows_font_by_name(ctx, "MS-Gothic");
  737. break;
  738. case FZ_ADOBE_KOREA:
  739. font = load_windows_font_by_name(ctx, "Gulim");
  740. break;
  741. default:
  742. fz_throw(ctx, FZ_ERROR_GENERIC, "invalid sans-serif ros");
  743. }
  744. }
  745. }
  746. fz_catch(ctx) {
  747. #ifdef NOCJKFONT
  748. /* If no CJK fallback font is builtin, maybe one has been shipped separately */
  749. font = load_windows_font_by_name(ctx, "DroidSansFallback");
  750. #else
  751. fz_rethrow(ctx);
  752. #endif
  753. }
  754. return font;
  755. }
  756. #endif
  757. /*
  758. Segoe UI Emoji Regular
  759. Cambria Math Regular - math symbols
  760. Segoe UI Symbol Regular - math and other symbols
  761. Charis SIL => Times New Roman or Georgia
  762. https://learn.microsoft.com/en-us/windows/apps/design/globalizing/loc-international-fonts
  763. */
  764. static fz_font* load_windows_fallback_font(fz_context* ctx, int script, int language, int serif, int bold, int italic) {
  765. fz_font* font = NULL;
  766. const char* font_name = NULL;
  767. // TODO: more scripts
  768. switch (script) {
  769. case UCDN_SCRIPT_BENGALI: // bangla
  770. case UCDN_SCRIPT_GURMUKHI:
  771. case UCDN_SCRIPT_GUJARATI:
  772. case UCDN_SCRIPT_KANNADA:
  773. case UCDN_SCRIPT_MALAYALAM:
  774. case UCDN_SCRIPT_SINHALA:
  775. case UCDN_SCRIPT_SORA_SOMPENG:
  776. case UCDN_SCRIPT_OL_CHIKI:
  777. case UCDN_SCRIPT_ORIYA: // odia
  778. case UCDN_SCRIPT_TAMIL:
  779. case UCDN_SCRIPT_TELUGU:
  780. case UCDN_SCRIPT_DEVANAGARI: {
  781. font_name = "NirmalaUI";
  782. if (bold) {
  783. font_name = "NirmalaUI-Bold";
  784. }
  785. } break;
  786. case UCDN_SCRIPT_CYRILLIC:
  787. case UCDN_SCRIPT_GREEK:
  788. case UCDN_SCRIPT_ARMENIAN:
  789. case UCDN_SCRIPT_GEORGIAN: {
  790. font_name = "Sylfaen";
  791. } break;
  792. // per chatgpt Times New Roman is closest to Noto Serif
  793. case UCDN_SCRIPT_LATIN:
  794. // case UCDN_SCRIPT_GREEK:
  795. // case UCDN_SCRIPT_CYRILLIC:
  796. case UCDN_SCRIPT_COMMON:
  797. case UCDN_SCRIPT_INHERITED:
  798. case UCDN_SCRIPT_UNKNOWN: {
  799. font_name = "TimesNewRomanPSMT";
  800. if (bold) {
  801. font_name = "TimesNewRomanPS-BoldMT";
  802. if (italic) {
  803. font_name = "TimesNewRomanPS-BoldItalicMT";
  804. }
  805. } else if (italic) {
  806. font_name = "TimesNewRomanPS-ItalicMT";
  807. }
  808. } break;
  809. }
  810. if (!font_name) {
  811. fz_warn(ctx, "couldn't find windows system font for script %d, language: %d, bold: %d, italic: %d", script,
  812. language, (int)bold, (int)italic);
  813. return NULL;
  814. }
  815. /* try to find a matching system font before falling back to an approximate one */
  816. fz_try(ctx) {
  817. font = load_windows_font_by_name(ctx, font_name);
  818. }
  819. fz_catch(ctx) {
  820. fz_report_error(ctx);
  821. }
  822. return font;
  823. }
  824. void init_system_font_list(void) {
  825. // this should always happen on main thread
  826. if (did_init) {
  827. return;
  828. }
  829. InitializeCriticalSection(&cs_fonts);
  830. g_win_fonts.len = 0;
  831. g_win_fonts.cap = MAX_FONTS;
  832. g_font_files.len = 0;
  833. g_font_files.cap = MAX_FONT_FILES;
  834. did_init = 1;
  835. }
  836. void destroy_system_font_list(void) {
  837. // TODO: free names and file data
  838. DeleteCriticalSection(&cs_fonts);
  839. }
  840. void install_load_windows_font_funcs(fz_context* ctx) {
  841. init_system_font_list();
  842. fz_install_load_system_font_funcs(ctx, load_windows_font, load_windows_cjk_font, load_windows_fallback_font);
  843. }