tTFParser.ml 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. (*
  2. * Copyright (C)2005-2014 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. *)
  22. open TTFData
  23. open IO
  24. type ctx = {
  25. file : Stdlib.in_channel;
  26. ch : input;
  27. mutable entry : entry;
  28. }
  29. let rd16 = BigEndian.read_i16
  30. let rdu16 = BigEndian.read_ui16
  31. let rd32 = BigEndian.read_i32
  32. let rd32r = BigEndian.read_real_i32
  33. let parse_header ctx =
  34. let ch = ctx.ch in
  35. let major_version = rdu16 ch in
  36. let minor_version = rdu16 ch in
  37. let num_tables = rdu16 ch in
  38. let search_range = rdu16 ch in
  39. let entry_selector = rdu16 ch in
  40. let range_shift = rdu16 ch in
  41. {
  42. hd_major_version = major_version;
  43. hd_minor_version = minor_version;
  44. hd_num_tables = num_tables;
  45. hd_search_range = search_range;
  46. hd_entry_selector = entry_selector;
  47. hd_range_shift = range_shift;
  48. }
  49. let parse_directory ctx header =
  50. let ch = ctx.ch in
  51. let directory = Hashtbl.create 0 in
  52. for i = 0 to header.hd_num_tables - 1 do
  53. let name = nread_string ch 4 in
  54. let cs = rd32r ch in
  55. let off = rd32r ch in
  56. let length = rd32r ch in
  57. Hashtbl.add directory name {
  58. entry_table_name = name;
  59. entry_checksum = cs;
  60. entry_offset = off;
  61. entry_length = length;
  62. }
  63. done;
  64. directory
  65. let parse_head_table ctx =
  66. let ch = ctx.ch in
  67. let version = rd32r ch in
  68. let font_revision = rd32r ch in
  69. let checksum_adjustment = rd32r ch in
  70. let magic_number = rd32r ch in
  71. let flags = rdu16 ch in
  72. let units_per_em = rdu16 ch in
  73. let created = BigEndian.read_double ch in
  74. let modified = BigEndian.read_double ch in
  75. let xmin = rd16 ch in
  76. let ymin = rd16 ch in
  77. let xmax = rd16 ch in
  78. let ymax = rd16 ch in
  79. let mac_style = rdu16 ch in
  80. let lowest_rec_ppem = rdu16 ch in
  81. let font_direction_hint = rd16 ch in
  82. let index_to_loc_format = rd16 ch in
  83. let glyph_data_format = rd16 ch in
  84. {
  85. hd_version = version;
  86. hd_font_revision = font_revision;
  87. hd_checksum_adjustment = checksum_adjustment;
  88. hd_magic_number = magic_number;
  89. hd_flags = flags;
  90. hd_units_per_em = units_per_em;
  91. hd_created = created;
  92. hd_modified = modified;
  93. hd_xmin = xmin;
  94. hd_ymin = ymin;
  95. hd_xmax = xmax;
  96. hd_ymax = ymax;
  97. hd_mac_style = mac_style;
  98. hd_lowest_rec_ppem = lowest_rec_ppem;
  99. hd_font_direction_hint = font_direction_hint;
  100. hd_index_to_loc_format = index_to_loc_format;
  101. hd_glyph_data_format = glyph_data_format;
  102. }
  103. let parse_hhea_table ctx =
  104. let ch = ctx.ch in
  105. let version = rd32r ch in
  106. let ascender = rd16 ch in
  107. let descender = rd16 ch in
  108. let line_gap = rd16 ch in
  109. let advance_width_max = rdu16 ch in
  110. let min_left_side_bearing = rd16 ch in
  111. let min_right_side_bearing = rd16 ch in
  112. let x_max_extent = rd16 ch in
  113. let caret_slope_rise = rd16 ch in
  114. let caret_slope_run = rd16 ch in
  115. let caret_offset = rd16 ch in
  116. let reserved = nread_string ch 8 in
  117. let metric_data_format = rd16 ch in
  118. let number_of_hmetrics = rdu16 ch in
  119. {
  120. hhea_version = version;
  121. hhea_ascent = ascender;
  122. hhea_descent = descender;
  123. hhea_line_gap = line_gap;
  124. hhea_advance_width_max = advance_width_max;
  125. hhea_min_left_side_bearing = min_left_side_bearing;
  126. hhea_min_right_side_bearing = min_right_side_bearing;
  127. hhea_x_max_extent = x_max_extent;
  128. hhea_caret_slope_rise = caret_slope_rise;
  129. hhea_caret_slope_run = caret_slope_run;
  130. hhea_caret_offset = caret_offset;
  131. hhea_reserved = reserved;
  132. hhea_metric_data_format = metric_data_format;
  133. hhea_number_of_hmetrics = number_of_hmetrics;
  134. }
  135. let parse_maxp_table ctx =
  136. let ch = ctx.ch in
  137. let version_number = rd32r ch in
  138. let num_glyphs = rdu16 ch in
  139. let max_points = rdu16 ch in
  140. let max_contours = rdu16 ch in
  141. let max_component_points = rdu16 ch in
  142. let max_component_contours = rdu16 ch in
  143. let max_zones = rdu16 ch in
  144. let max_twilight_points = rdu16 ch in
  145. let max_storage = rdu16 ch in
  146. let max_function_defs = rdu16 ch in
  147. let max_instruction_defs = rdu16 ch in
  148. let max_stack_elements = rdu16 ch in
  149. let max_size_of_instructions = rdu16 ch in
  150. let max_component_elements = rdu16 ch in
  151. let max_component_depth = rdu16 ch in
  152. {
  153. maxp_version_number = version_number;
  154. maxp_num_glyphs = num_glyphs;
  155. maxp_max_points = max_points;
  156. maxp_max_contours = max_contours;
  157. maxp_max_component_points = max_component_points;
  158. maxp_max_component_contours = max_component_contours;
  159. maxp_max_zones = max_zones;
  160. maxp_max_twilight_points = max_twilight_points;
  161. maxp_max_storage = max_storage;
  162. maxp_max_function_defs = max_function_defs;
  163. maxp_max_instruction_defs = max_instruction_defs;
  164. maxp_max_stack_elements = max_stack_elements;
  165. maxp_max_size_of_instructions = max_size_of_instructions;
  166. maxp_max_component_elements = max_component_elements;
  167. maxp_max_component_depth = max_component_depth;
  168. }
  169. let parse_loca_table head maxp ctx =
  170. let ch = ctx.ch in
  171. if head.hd_index_to_loc_format = 0 then
  172. Array.init (maxp.maxp_num_glyphs + 1) (fun _ -> Int32.of_int ((rdu16 ch) * 2))
  173. else
  174. Array.init (maxp.maxp_num_glyphs + 1) (fun _ -> rd32r ch)
  175. let parse_hmtx_table maxp hhea ctx =
  176. let ch = ctx.ch in
  177. let last_advance_width = ref 0 in (* check me 1/2*)
  178. Array.init maxp.maxp_num_glyphs (fun i ->
  179. let advance_width = if i > hhea.hhea_number_of_hmetrics-1 then (* check me 2/2*)
  180. !last_advance_width
  181. else
  182. rdu16 ch
  183. in
  184. last_advance_width := advance_width;
  185. let left_side_bearing = rd16 ch in
  186. {
  187. advance_width = advance_width;
  188. left_side_bearing = left_side_bearing;
  189. }
  190. )
  191. let parse_cmap_table ctx =
  192. let ch = ctx.ch in
  193. let version = rdu16 ch in
  194. let num_subtables = rdu16 ch in
  195. let dir = ExtList.List.init num_subtables (fun _ ->
  196. let platform_id = rdu16 ch in
  197. let platform_specific_id = rdu16 ch in
  198. let offset = rd32r ch in
  199. {
  200. csh_platform_id = platform_id;
  201. csh_platform_specific_id = platform_specific_id;
  202. csh_offset = offset;
  203. }
  204. ) in
  205. let dir = List.stable_sort (fun csh1 csh2 ->
  206. if csh1.csh_platform_id < csh2.csh_platform_id then -1
  207. else if csh1.csh_platform_id > csh2.csh_platform_id then 1
  208. else compare csh1.csh_platform_specific_id csh2.csh_platform_specific_id
  209. ) dir in
  210. let parse_sub entry =
  211. seek_in ctx.file ((Int32.to_int ctx.entry.entry_offset) + (Int32.to_int entry.csh_offset));
  212. let format = rdu16 ch in
  213. let def = match format with
  214. | 0 ->
  215. let length = rdu16 ch in
  216. let language = rdu16 ch in
  217. let glyph_index = Array.init 256 (fun _ -> read ch) in
  218. Cmap0 {
  219. c0_format = 0;
  220. c0_length = length;
  221. c0_language = language;
  222. c0_glyph_index_array = glyph_index;
  223. }
  224. | 4 ->
  225. let length = rdu16 ch in
  226. let language = rdu16 ch in
  227. let seg_count_x2 = rdu16 ch in
  228. let seg_count = seg_count_x2 / 2 in
  229. let search_range = rdu16 ch in
  230. let entry_selector = rdu16 ch in
  231. let range_shift = rdu16 ch in
  232. let end_code = Array.init seg_count (fun _ -> rdu16 ch) in
  233. let reserved = rdu16 ch in
  234. assert (reserved = 0);
  235. let start_code = Array.init seg_count (fun _ -> rdu16 ch) in
  236. let id_delta = Array.init seg_count (fun _ -> rdu16 ch) in
  237. let id_range_offset = Array.init seg_count (fun _ -> rdu16 ch) in
  238. let count = (length - (8 * seg_count + 16)) / 2 in
  239. let glyph_index = Array.init count (fun _ -> rdu16 ch) in
  240. Cmap4 {
  241. c4_format = format;
  242. c4_length = length;
  243. c4_language = language;
  244. c4_seg_count_x2 = seg_count_x2;
  245. c4_search_range = search_range;
  246. c4_entry_selector = entry_selector;
  247. c4_range_shift = range_shift;
  248. c4_end_code = end_code;
  249. c4_reserved_pad = reserved;
  250. c4_start_code = start_code;
  251. c4_id_delta = id_delta;
  252. c4_id_range_offset = id_range_offset;
  253. c4_glyph_index_array = glyph_index;
  254. }
  255. | 6 ->
  256. let length = rdu16 ch in
  257. let language = rdu16 ch in
  258. let first_code = rdu16 ch in
  259. let entry_count = rdu16 ch in
  260. let glyph_index = Array.init entry_count (fun _ -> rdu16 ch) in
  261. Cmap6 {
  262. c6_format = format;
  263. c6_length = length;
  264. c6_language = language;
  265. c6_first_code = first_code;
  266. c6_entry_count = entry_count;
  267. c6_glyph_index_array = glyph_index;
  268. }
  269. | 12 ->
  270. ignore (rd16 ch);
  271. let length = rd32r ch in
  272. let language = rd32r ch in
  273. let num_groups = rd32r ch in
  274. let groups = ExtList.List.init (Int32.to_int num_groups) (fun _ ->
  275. let start = rd32r ch in
  276. let stop = rd32r ch in
  277. let start_glyph = rd32r ch in
  278. {
  279. c12g_start_char_code = start;
  280. c12g_end_char_code = stop;
  281. c12g_start_glyph_code = start_glyph;
  282. }
  283. ) in
  284. Cmap12 {
  285. c12_format = Int32.of_int 12;
  286. c12_length = length;
  287. c12_language = language;
  288. c12_num_groups = num_groups;
  289. c12_groups = groups;
  290. }
  291. | x ->
  292. failwith ("Not implemented format: " ^ (string_of_int x));
  293. in
  294. {
  295. cs_def = def;
  296. cs_header = entry;
  297. }
  298. in
  299. {
  300. cmap_version = version;
  301. cmap_num_subtables = num_subtables;
  302. cmap_subtables = List.map parse_sub dir;
  303. }
  304. let parse_glyf_table maxp loca cmap hmtx ctx =
  305. let ch = ctx.ch in
  306. let parse_glyf i =
  307. seek_in ctx.file ((Int32.to_int ctx.entry.entry_offset) + (Int32.to_int loca.(i)));
  308. let num_contours = rd16 ch in
  309. let xmin = rd16 ch in
  310. let ymin = rd16 ch in
  311. let xmax = rd16 ch in
  312. let ymax = rd16 ch in
  313. let header = {
  314. gh_num_contours = num_contours;
  315. gh_xmin = xmin;
  316. gh_ymin = ymin;
  317. gh_xmax = xmax;
  318. gh_ymax = ymax;
  319. } in
  320. if num_contours >= 0 then begin
  321. let num_points = ref 0 in
  322. let end_pts_of_contours = Array.init num_contours (fun i ->
  323. let v = rdu16 ch in
  324. if i = num_contours - 1 then num_points := v + 1;
  325. v
  326. ) in
  327. let instruction_length = rdu16 ch in
  328. let instructions = Array.init instruction_length (fun _ ->
  329. read ch
  330. ) in
  331. let flags = DynArray.create () in
  332. let rec loop index =
  333. if index >= !num_points then () else begin
  334. let v = read_byte ch in
  335. let incr = if (v land 8) == 0 then begin
  336. DynArray.add flags v;
  337. 1
  338. end else begin
  339. let r = (int_of_char (read ch)) in
  340. for i = 0 to r do DynArray.add flags v done;
  341. r + 1
  342. end in
  343. loop (index + incr)
  344. end
  345. in
  346. loop 0;
  347. assert (DynArray.length flags = !num_points);
  348. let x_coordinates = Array.init !num_points (fun i ->
  349. let flag = DynArray.get flags i in
  350. if flag land 0x10 <> 0 then begin
  351. if flag land 0x02 <> 0 then read_byte ch
  352. else 0
  353. end else begin
  354. if flag land 0x02 <> 0 then -read_byte ch
  355. else rd16 ch
  356. end
  357. ) in
  358. let y_coordinates = Array.init !num_points (fun i ->
  359. let flag = DynArray.get flags i in
  360. if flag land 0x20 <> 0 then begin
  361. if flag land 0x04 <> 0 then read_byte ch
  362. else 0
  363. end else begin
  364. if flag land 0x04 <> 0 then -read_byte ch
  365. else rd16 ch
  366. end;
  367. ) in
  368. TGlyfSimple (header, {
  369. gs_end_pts_of_contours = end_pts_of_contours;
  370. gs_instruction_length = instruction_length;
  371. gs_instructions = instructions;
  372. gs_flags = DynArray.to_array flags;
  373. gs_x_coordinates = x_coordinates;
  374. gs_y_coordinates = y_coordinates;
  375. })
  376. end else if num_contours = -1 then begin
  377. let acc = DynArray.create () in
  378. let rec loop () =
  379. let flags = rdu16 ch in
  380. let glyph_index = rdu16 ch in
  381. let arg1,arg2 = if flags land 1 <> 0 then begin
  382. let arg1 = rd16 ch in
  383. let arg2 = rd16 ch in
  384. arg1,arg2
  385. end else begin
  386. let arg1 = read_byte ch in
  387. let arg2 = read_byte ch in
  388. arg1,arg2
  389. end in
  390. let fmt214 i = (float_of_int i) /. (float_of_int 0x4000) in
  391. let fmode = if flags land 8 <> 0 then
  392. Scale (fmt214 (rd16 ch))
  393. else if flags land 64 <> 0 then begin
  394. let s1 = fmt214 (rd16 ch) in
  395. let s2 = fmt214 (rd16 ch) in
  396. ScaleXY (s1,s2)
  397. end else if flags land 128 <> 0 then begin
  398. let a = fmt214 (rd16 ch) in
  399. let b = fmt214 (rd16 ch) in
  400. let c = fmt214 (rd16 ch) in
  401. let d = fmt214 (rd16 ch) in
  402. ScaleMatrix (a,b,c,d)
  403. end else
  404. NoScale
  405. in
  406. DynArray.add acc {
  407. gc_flags = flags;
  408. gc_glyf_index = glyph_index;
  409. gc_arg1 = if flags land 2 <> 0 then arg1 else 0;
  410. gc_arg2 = if flags land 2 <> 0 then arg2 else 0;
  411. gc_transformation = fmode;
  412. };
  413. if flags land 0x20 <> 0 then loop ();
  414. in
  415. loop ();
  416. TGlyfComposite (header,(DynArray.to_list acc))
  417. end else
  418. failwith "Unknown Glyf"
  419. in
  420. Array.init maxp.maxp_num_glyphs (fun i ->
  421. let len = (Int32.to_int loca.(i + 1)) - (Int32.to_int loca.(i)) in
  422. if len > 0 then parse_glyf i else TGlyfNull
  423. )
  424. let parse_kern_table ctx =
  425. let ch = ctx.ch in
  426. let version = Int32.of_int (rd16 ch) in
  427. let num_tables = Int32.of_int (rd16 ch) in
  428. let tables = ExtList.List.init (Int32.to_int num_tables) (fun _ ->
  429. let length = Int32.of_int (rdu16 ch) in
  430. let tuple_index = rdu16 ch in
  431. let coverage = rdu16 ch in
  432. let def = match coverage lsr 8 with
  433. | 0 ->
  434. let num_pairs = rdu16 ch in
  435. let search_range = rdu16 ch in
  436. let entry_selector = rdu16 ch in
  437. let range_shift = rdu16 ch in
  438. let kerning_pairs = ExtList.List.init num_pairs (fun _ ->
  439. let left = rdu16 ch in
  440. let right = rdu16 ch in
  441. let value = rd16 ch in
  442. {
  443. kern_left = left;
  444. kern_right = right;
  445. kern_value = value;
  446. }
  447. ) in
  448. Kern0 {
  449. k0_num_pairs = num_pairs;
  450. k0_search_range = search_range;
  451. k0_entry_selector = entry_selector;
  452. k0_range_shift = range_shift;
  453. k0_pairs = kerning_pairs;
  454. }
  455. | 2 ->
  456. let row_width = rdu16 ch in
  457. let left_offset_table = rdu16 ch in
  458. let right_offset_table = rdu16 ch in
  459. let array_offset = rdu16 ch in
  460. let first_glyph = rdu16 ch in
  461. let num_glyphs = rdu16 ch in
  462. let offsets = ExtList.List.init num_glyphs (fun _ ->
  463. rdu16 ch
  464. ) in
  465. Kern2 {
  466. k2_row_width = row_width;
  467. k2_left_offset_table = left_offset_table;
  468. k2_right_offset_table = right_offset_table;
  469. k2_array = array_offset;
  470. k2_first_glyph = first_glyph;
  471. k2_num_glyphs = num_glyphs;
  472. k2_offsets = offsets;
  473. }
  474. | i ->
  475. failwith ("Unknown kerning: " ^ (string_of_int i));
  476. in
  477. {
  478. ks_def = def;
  479. ks_header = {
  480. ksh_length = length;
  481. ksh_coverage = coverage;
  482. ksh_tuple_index = tuple_index;
  483. }
  484. }
  485. ) in
  486. {
  487. kern_version = version;
  488. kern_num_tables = num_tables;
  489. kern_subtables = tables;
  490. }
  491. let parse_name_table ctx =
  492. let ch = ctx.ch in
  493. let format = rdu16 ch in
  494. let num_records = rdu16 ch in
  495. let offset = rdu16 ch in
  496. let records = Array.init num_records (fun _ ->
  497. let platform_id = rdu16 ch in
  498. let platform_specific_id = rdu16 ch in
  499. let language_id = rdu16 ch in
  500. let name_id = rdu16 ch in
  501. let length = rdu16 ch in
  502. let offset = rdu16 ch in
  503. {
  504. nr_platform_id = platform_id;
  505. nr_platform_specific_id = platform_specific_id;
  506. nr_language_id = language_id;
  507. nr_name_id = name_id;
  508. nr_length = length;
  509. nr_offset = offset;
  510. nr_value = "";
  511. }
  512. ) in
  513. let ttf_name = ref "" in
  514. (* TODO: use real utf16 conversion *)
  515. let set_name n =
  516. let l = ExtList.List.init (String.length n / 2) (fun i -> String.make 1 n.[i * 2 + 1]) in
  517. ttf_name := String.concat "" l
  518. in
  519. let records = Array.map (fun r ->
  520. seek_in ctx.file ((Int32.to_int ctx.entry.entry_offset) + offset + r.nr_offset);
  521. r.nr_value <- nread_string ch r.nr_length;
  522. if r.nr_name_id = 4 && r.nr_platform_id = 3 || r.nr_platform_id = 0 then set_name r.nr_value;
  523. r
  524. ) records in
  525. {
  526. name_format = format;
  527. name_num_records = num_records;
  528. name_offset = offset;
  529. name_records = records;
  530. },!ttf_name
  531. let parse_os2_table ctx =
  532. let ch = ctx.ch in
  533. let version = rdu16 ch in
  534. let x_avg_char_width = rd16 ch in
  535. let us_weight_class = rdu16 ch in
  536. let us_width_class = rdu16 ch in
  537. let fs_type = rd16 ch in
  538. let y_subscript_x_size = rd16 ch in
  539. let y_subscript_y_size = rd16 ch in
  540. let y_subscript_x_offset = rd16 ch in
  541. let y_subscript_y_offset = rd16 ch in
  542. let y_superscript_x_size = rd16 ch in
  543. let y_superscript_y_size = rd16 ch in
  544. let y_superscript_x_offset = rd16 ch in
  545. let y_superscript_y_offset = rd16 ch in
  546. let y_strikeout_size = rd16 ch in
  547. let y_strikeout_position = rd16 ch in
  548. let s_family_class = rd16 ch in
  549. let b_family_type = read_byte ch in
  550. let b_serif_style = read_byte ch in
  551. let b_weight = read_byte ch in
  552. let b_proportion = read_byte ch in
  553. let b_contrast = read_byte ch in
  554. let b_stroke_variation = read_byte ch in
  555. let b_arm_style = read_byte ch in
  556. let b_letterform = read_byte ch in
  557. let b_midline = read_byte ch in
  558. let b_x_height = read_byte ch in
  559. let ul_unicode_range_1 = rd32r ch in
  560. let ul_unicode_range_2 = rd32r ch in
  561. let ul_unicode_range_3 = rd32r ch in
  562. let ul_unicode_range_4 = rd32r ch in
  563. let ach_vendor_id = rd32r ch in
  564. let fs_selection = rd16 ch in
  565. let us_first_char_index = rdu16 ch in
  566. let us_last_char_index = rdu16 ch in
  567. let s_typo_ascender = rd16 ch in
  568. let s_typo_descender = rd16 ch in
  569. let s_typo_line_gap = rd16 ch in
  570. let us_win_ascent = rdu16 ch in
  571. let us_win_descent = rdu16 ch in
  572. {
  573. os2_version = version;
  574. os2_x_avg_char_width = x_avg_char_width;
  575. os2_us_weight_class = us_weight_class;
  576. os2_us_width_class = us_width_class;
  577. os2_fs_type = fs_type;
  578. os2_y_subscript_x_size = y_subscript_x_size;
  579. os2_y_subscript_y_size = y_subscript_y_size;
  580. os2_y_subscript_x_offset = y_subscript_x_offset;
  581. os2_y_subscript_y_offset = y_subscript_y_offset;
  582. os2_y_superscript_x_size = y_superscript_x_size;
  583. os2_y_superscript_y_size = y_superscript_y_size;
  584. os2_y_superscript_x_offset = y_superscript_x_offset;
  585. os2_y_superscript_y_offset = y_superscript_y_offset;
  586. os2_y_strikeout_size = y_strikeout_size;
  587. os2_y_strikeout_position = y_strikeout_position;
  588. os2_s_family_class = s_family_class;
  589. os2_b_family_type = b_family_type;
  590. os2_b_serif_style = b_serif_style;
  591. os2_b_weight = b_weight;
  592. os2_b_proportion = b_proportion;
  593. os2_b_contrast = b_contrast;
  594. os2_b_stroke_variation = b_stroke_variation;
  595. os2_b_arm_style = b_arm_style;
  596. os2_b_letterform = b_letterform;
  597. os2_b_midline = b_midline;
  598. os2_b_x_height = b_x_height;
  599. os2_ul_unicode_range_1 = ul_unicode_range_1;
  600. os2_ul_unicode_range_2 = ul_unicode_range_2;
  601. os2_ul_unicode_range_3 = ul_unicode_range_3;
  602. os2_ul_unicode_range_4 = ul_unicode_range_4;
  603. os2_ach_vendor_id = ach_vendor_id;
  604. os2_fs_selection = fs_selection;
  605. os2_us_first_char_index = us_first_char_index;
  606. os2_us_last_char_index = us_last_char_index;
  607. os2_s_typo_ascender = s_typo_ascender;
  608. os2_s_typo_descender = s_typo_descender;
  609. os2_s_typo_line_gap = s_typo_line_gap;
  610. os2_us_win_ascent = us_win_ascent;
  611. os2_us_win_descent = us_win_descent;
  612. }
  613. let parse file : ttf =
  614. let ctx = {
  615. file = file;
  616. ch = input_channel file;
  617. entry = {
  618. entry_table_name = "";
  619. entry_offset = Int32.of_int 0;
  620. entry_length = Int32.of_int 0;
  621. entry_checksum = Int32.of_int 0;
  622. }
  623. } in
  624. let header = parse_header ctx in
  625. let directory = parse_directory ctx header in
  626. let parse_table entry f =
  627. seek_in file (Int32.to_int entry.entry_offset);
  628. ctx.entry <- entry;
  629. f ctx
  630. in
  631. let parse_req_table name f =
  632. try
  633. let entry = Hashtbl.find directory name in
  634. parse_table entry f
  635. with Not_found ->
  636. failwith (Printf.sprintf "Required table %s could not be found" name)
  637. in
  638. let parse_opt_table name f =
  639. try
  640. let entry = Hashtbl.find directory name in
  641. Some (parse_table entry f)
  642. with Not_found ->
  643. None
  644. in
  645. let head = parse_req_table "head" parse_head_table in
  646. let hhea = parse_req_table "hhea" parse_hhea_table in
  647. let maxp = parse_req_table "maxp" parse_maxp_table in
  648. let loca = parse_req_table "loca" (parse_loca_table head maxp) in
  649. let hmtx = parse_req_table "hmtx" (parse_hmtx_table maxp hhea) in
  650. let cmap = parse_req_table "cmap" (parse_cmap_table) in
  651. let glyfs = parse_req_table "glyf" (parse_glyf_table maxp loca cmap hmtx) in
  652. let kern = parse_opt_table "kern" (parse_kern_table) in
  653. let name,ttf_name = parse_req_table "name" (parse_name_table) in
  654. let os2 = parse_req_table "OS/2" (parse_os2_table) in
  655. {
  656. ttf_header = header;
  657. ttf_font_name = ttf_name;
  658. ttf_directory = directory;
  659. ttf_head = head;
  660. ttf_hhea = hhea;
  661. ttf_maxp = maxp;
  662. ttf_loca = loca;
  663. ttf_hmtx = hmtx;
  664. ttf_cmap = cmap;
  665. ttf_glyfs = glyfs;
  666. ttf_name = name;
  667. ttf_os2 = os2;
  668. ttf_kern = kern;
  669. }