Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
font.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
(** Hello *) (* https://wiki.osdev.org/PC_Screen_Font *) type psfheader = { magic : int32; version : int32; headersize : int32; flags : int32; number_of_glyphs : int32; bytes_per_glyph : int32; height : int32; width : int32; } module Glyph = struct type t = { bitmap : bytes; width : int; height : int } let dimensions g = (g.width, g.height, 0, 0) let bitmap g = g.bitmap end type t = { header : psfheader; glyphs : bytes array; map : (Uchar.t * int) list; } type reader_t = int64 -> int -> Bytes.t -> unit (* ----- internal ----- *) let read_header (reader : reader_t) : (psfheader, string) result = let header_length = 4 * 8 in let header_buffer = Bytes.create header_length in try reader 0L header_length header_buffer; Result.ok { magic = Bytes.get_int32_le header_buffer 0; version = Bytes.get_int32_le header_buffer 4; headersize = Bytes.get_int32_le header_buffer 8; flags = Bytes.get_int32_le header_buffer 12; number_of_glyphs = Bytes.get_int32_le header_buffer 16; bytes_per_glyph = Bytes.get_int32_le header_buffer 20; height = Bytes.get_int32_le header_buffer 24; width = Bytes.get_int32_le header_buffer 28; } with Sys_error reason -> Result.error reason let load_glyphs (reader : reader_t) header : (bytes array, string) result = let bpg = Int32.to_int header.bytes_per_glyph in try Result.ok (Array.init (Int32.to_int header.number_of_glyphs) (fun i -> let buffer = Bytes.create bpg in reader (Int64.of_int (Int32.to_int header.headersize + (i * bpg))) bpg buffer; buffer)) with Sys_error reason -> Result.error reason let inner_load_map_table reader header : ((Uchar.t * int) list, string) result = try let offset : int64 ref = ref (Int64.of_int (Int32.to_int header.headersize + Int32.to_int header.number_of_glyphs * Int32.to_int header.bytes_per_glyph)) in let rec outerloop (counter : int) (tail : (Uchar.t * int) list list) : (Uchar.t * int) list list = let rec find_next_terminator (sofar : char list) : char list option = try let buffer = Bytes.create 1 in reader !offset 1 buffer; offset := Int64.add !offset 1L; let c = Bytes.get buffer 0 in match c with | '\255' -> Some sofar | _ -> find_next_terminator (c :: sofar) with | Invalid_argument _ -> None | Sys_error _ -> None in match find_next_terminator [] with | None -> tail | Some bytes_list -> let next_batch_list = List.rev bytes_list in let next_batch_buffer = Bytes.create (List.length next_batch_list) in List.iteri (fun i c -> Bytes.set next_batch_buffer i c) next_batch_list; let rec bytes_to_unicodes (offset : int) (tail : Uchar.t list) : Uchar.t list = let remaining = Bytes.length next_batch_buffer - offset in match remaining with | 0 -> tail | _ -> ( let c = Bytes.get_utf_8_uchar next_batch_buffer offset in let size = Uchar.utf_decode_length c in match Uchar.utf_decode_is_valid c with | false -> bytes_to_unicodes (offset + size) tail | true -> bytes_to_unicodes (offset + size) (Uchar.utf_decode_uchar c :: tail)) in let char_list = bytes_to_unicodes 0 [] in let this_uchars = List.map (fun c -> (c, counter)) char_list in outerloop (counter + 1) (this_uchars :: tail) in let rest = outerloop 0 [] in Result.ok (List.concat rest) with Sys_error reason -> Result.error reason let load_map_table reader header : ((Uchar.t * int) list, string) result = let flags = Int32.to_int header.flags in match flags with | 0 -> Result.ok (List.init (Int32.to_int header.number_of_glyphs) (fun i -> (Uchar.of_int i, i))) | 1 -> inner_load_map_table reader header | _ -> Result.error (Printf.sprintf "Unrecognised header flag 0x%x" flags) let ( >>= ) = Result.bind let channel_reader inc : reader_t = fun offset length buffer -> In_channel.seek inc offset; match In_channel.really_input inc buffer 0 length with | Some () -> () | None -> invalid_arg "failed to get bytes" let bytes_reader inbuf : reader_t = fun offset length outbuf -> Bytes.blit inbuf (Int64.to_int offset) outbuf 0 length let load_font reader = read_header reader >>= fun header -> load_glyphs reader header >>= fun glyphs -> load_map_table reader header >>= fun map -> Result.ok { header; glyphs; map } (* ----- public ----- *) let of_bytes data = let reader = bytes_reader data in load_font reader let of_file filename = In_channel.with_open_bin filename (fun ic -> let reader = channel_reader ic in load_font reader) let print_header (font : t) = let header = font.header in Printf.printf "Magic: 0x%08x\n" (Int32.to_int header.magic); Printf.printf "Version: %d\n" (Int32.to_int header.version); Printf.printf "Header Size: %d\n" (Int32.to_int header.headersize); Printf.printf "Flags: 0x%08x\n" (Int32.to_int header.flags); Printf.printf "Number of Glyphs: %d\n" (Int32.to_int header.number_of_glyphs); Printf.printf "Bytes per Glyph: %d\n" (Int32.to_int header.bytes_per_glyph); Printf.printf "Width: %d\n" (Int32.to_int header.width); Printf.printf "Height: %d\n" (Int32.to_int header.height) let glyph_count (font : t) : int = Int32.to_int font.header.number_of_glyphs let glyph_of_char (font : t) (u : Uchar.t) : Glyph.t option = match List.assoc_opt u font.map with | None -> None | Some index -> ( match index >= 0 && index < Array.length font.glyphs with | false -> None | true -> Some { bitmap = font.glyphs.(index); width = Int32.to_int font.header.width; height = Int32.to_int font.header.height; })