lib/filigree/src/font/cff.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const face_table = @import("face.zig");
  3 
  4 pub const Error = error{
  5     InvalidFont,
  6     MissingTable,
  7     UnsupportedOutline,
  8     OutOfMemory,
  9 };
 10 
 11 const max_stack = 64;
 12 const max_subr_depth = 16;
 13 const max_ops = 65536;
 14 
 15 pub fn buildGlyph(face: face_table.Face, glyph_id: u32, sink: anytype) Error!void {
 16     if (glyph_id >= face.num_glyphs) return error.InvalidFont;
 17     const font = try Font.init(face);
 18     if (font.cid_keyed) return error.UnsupportedOutline;
 19     if (glyph_id >= font.charstrings.count) return error.InvalidFont;
 20     var interpreter = Interpreter.init(font);
 21     const charstring = try font.charstrings.item(glyph_id);
 22     switch (try interpreter.run(charstring, sink, 0)) {
 23         .ended => {},
 24         .returned, .exhausted => return error.InvalidFont,
 25     }
 26     try sink.close();
 27 }
 28 
 29 const Font = struct {
 30     data: []const u8,
 31     charstrings: Index,
 32     global_subrs: Index,
 33     local_subrs: ?Index = null,
 34     cid_keyed: bool = false,
 35 
 36     fn init(face: face_table.Face) Error!Font {
 37         const data = face.tableSlice("CFF ") orelse return error.MissingTable;
 38         if (data.len < 4) return error.InvalidFont;
 39         if (data[0] != 1) return error.UnsupportedOutline;
 40         const header_size: usize = data[2];
 41         if (header_size < 4 or header_size > data.len) return error.InvalidFont;
 42 
 43         const names = try Index.init(data, header_size);
 44         if (names.count != 1) return error.InvalidFont;
 45         const top_dicts = try Index.init(data, names.end);
 46         if (top_dicts.count == 0) return error.InvalidFont;
 47         const top = try parseTopDict(try top_dicts.item(0));
 48         const strings = try Index.init(data, top_dicts.end);
 49         const global_subrs = try Index.init(data, strings.end);
 50         if (top.charstrings_offset == 0) return error.InvalidFont;
 51         const charstrings = try Index.init(data, top.charstrings_offset);
 52         if (charstrings.count != face.num_glyphs) return error.InvalidFont;
 53 
 54         var local_subrs: ?Index = null;
 55         if (top.private_size != 0) {
 56             const private_start: usize = @intCast(top.private_offset);
 57             const private_size: usize = @intCast(top.private_size);
 58             if (private_start > data.len or private_size > data.len - private_start) return error.InvalidFont;
 59             const private = try parsePrivateDict(data[private_start..][0..private_size]);
 60             if (private.local_subrs_offset) |offset| {
 61                 const subrs_offset: usize = @intCast(offset);
 62                 if (subrs_offset > data.len - private_start) return error.InvalidFont;
 63                 local_subrs = try Index.init(data, private_start + subrs_offset);
 64             }
 65         }
 66 
 67         return .{
 68             .data = data,
 69             .charstrings = charstrings,
 70             .global_subrs = global_subrs,
 71             .local_subrs = local_subrs,
 72             .cid_keyed = top.cid_keyed,
 73         };
 74     }
 75 };
 76 
 77 const Index = struct {
 78     data: []const u8,
 79     count: u32 = 0,
 80     off_size: u8 = 0,
 81     offsets_offset: usize = 0,
 82     object_offset: usize = 0,
 83     end: usize = 0,
 84 
 85     fn init(data: []const u8, offset: usize) Error!Index {
 86         if (offset > data.len or data.len - offset < 2) return error.InvalidFont;
 87         const count = try readU16(data, offset);
 88         if (count == 0) {
 89             return .{
 90                 .data = data,
 91                 .end = offset + 2,
 92             };
 93         }
 94         if (data.len - offset < 3) return error.InvalidFont;
 95         const off_size = data[offset + 2];
 96         if (off_size == 0 or off_size > 4) return error.InvalidFont;
 97         const count_usize: usize = count;
 98         const offsets_offset = offset + 3;
 99         const offset_bytes = (count_usize + 1) * @as(usize, off_size);
100         if (offsets_offset > data.len or offset_bytes > data.len - offsets_offset) return error.InvalidFont;
101         const object_offset = offsets_offset + offset_bytes;
102         const first = try readIndexOffset(data, offsets_offset, off_size, 0);
103         const last = try readIndexOffset(data, offsets_offset, off_size, count_usize);
104         if (first != 1 or last == 0) return error.InvalidFont;
105         const end = object_offset + @as(usize, @intCast(last - 1));
106         if (end > data.len) return error.InvalidFont;
107         return .{
108             .data = data,
109             .count = count,
110             .off_size = off_size,
111             .offsets_offset = offsets_offset,
112             .object_offset = object_offset,
113             .end = end,
114         };
115     }
116 
117     fn item(self: Index, index: u32) Error![]const u8 {
118         if (index >= self.count) return error.InvalidFont;
119         const i: usize = @intCast(index);
120         const start_offset = try readIndexOffset(self.data, self.offsets_offset, self.off_size, i);
121         const end_offset = try readIndexOffset(self.data, self.offsets_offset, self.off_size, i + 1);
122         if (start_offset == 0 or end_offset < start_offset) return error.InvalidFont;
123         const start = self.object_offset + @as(usize, @intCast(start_offset - 1));
124         const end = self.object_offset + @as(usize, @intCast(end_offset - 1));
125         if (start > self.data.len or end > self.data.len or end < start) return error.InvalidFont;
126         return self.data[start..end];
127     }
128 };
129 
130 const TopDict = struct {
131     charstrings_offset: u32 = 0,
132     private_size: u32 = 0,
133     private_offset: u32 = 0,
134     cid_keyed: bool = false,
135 };
136 
137 const PrivateDict = struct {
138     local_subrs_offset: ?u32 = null,
139 };
140 
141 fn parseTopDict(data: []const u8) Error!TopDict {
142     var result = TopDict{};
143     var stack = DictStack{};
144     var offset: usize = 0;
145     while (offset < data.len) {
146         const first = data[offset];
147         offset += 1;
148         if (isDictOperator(first)) {
149             if (first == 12) {
150                 if (offset >= data.len) return error.InvalidFont;
151                 const op = data[offset];
152                 offset += 1;
153                 switch (op) {
154                     30, 36, 37 => result.cid_keyed = true,
155                     else => {},
156                 }
157             } else switch (first) {
158                 17 => result.charstrings_offset = stack.lastU32() orelse return error.InvalidFont,
159                 18 => {
160                     if (stack.len < 2) return error.InvalidFont;
161                     result.private_size = try stack.u32At(stack.len - 2);
162                     result.private_offset = try stack.u32At(stack.len - 1);
163                 },
164                 else => {},
165             }
166             stack.clear();
167             continue;
168         }
169         try stack.push(try readDictNumber(data, &offset, first));
170     }
171     return result;
172 }
173 
174 fn parsePrivateDict(data: []const u8) Error!PrivateDict {
175     var result = PrivateDict{};
176     var stack = DictStack{};
177     var offset: usize = 0;
178     while (offset < data.len) {
179         const first = data[offset];
180         offset += 1;
181         if (isDictOperator(first)) {
182             if (first == 19) {
183                 result.local_subrs_offset = stack.lastU32() orelse return error.InvalidFont;
184             } else if (first == 12) {
185                 if (offset >= data.len) return error.InvalidFont;
186                 offset += 1;
187             }
188             stack.clear();
189             continue;
190         }
191         try stack.push(try readDictNumber(data, &offset, first));
192     }
193     return result;
194 }
195 
196 const DictStack = struct {
197     values: [max_stack]i32 = undefined,
198     len: usize = 0,
199 
200     fn push(self: *DictStack, value: i32) Error!void {
201         if (self.len >= self.values.len) return error.InvalidFont;
202         self.values[self.len] = value;
203         self.len += 1;
204     }
205 
206     fn clear(self: *DictStack) void {
207         self.len = 0;
208     }
209 
210     fn lastU32(self: *const DictStack) ?u32 {
211         if (self.len == 0) return null;
212         return std.math.cast(u32, self.values[self.len - 1]);
213     }
214 
215     fn u32At(self: *const DictStack, index: usize) Error!u32 {
216         if (index >= self.len) return error.InvalidFont;
217         return std.math.cast(u32, self.values[index]) orelse error.InvalidFont;
218     }
219 };
220 
221 const RunResult = enum {
222     exhausted,
223     returned,
224     ended,
225 };
226 
227 const OperatorResult = enum {
228     none,
229     ended,
230     returned,
231 };
232 
233 const Interpreter = struct {
234     font: Font,
235     stack: [max_stack]i32 = undefined,
236     stack_len: usize = 0,
237     x: i32 = 0,
238     y: i32 = 0,
239     stem_count: usize = 0,
240     width_seen: bool = false,
241     op_count: usize = 0,
242 
243     fn init(font: Font) Interpreter {
244         return .{ .font = font };
245     }
246 
247     fn run(self: *Interpreter, data: []const u8, sink: anytype, depth: usize) Error!RunResult {
248         if (depth > max_subr_depth) return error.InvalidFont;
249         var offset: usize = 0;
250         while (offset < data.len) {
251             self.op_count += 1;
252             if (self.op_count > max_ops) return error.InvalidFont;
253             const first = data[offset];
254             offset += 1;
255             if (first == 12) {
256                 if (offset >= data.len) return error.InvalidFont;
257                 const op = data[offset];
258                 offset += 1;
259                 try self.escaped(op, sink);
260                 continue;
261             }
262             if (first == 28 or first > 31) {
263                 try self.push(try readCharNumber(data, &offset, first));
264                 continue;
265             }
266             if (first <= 31) {
267                 switch (try self.operator(first, data, &offset, sink, depth)) {
268                     .ended => return .ended,
269                     .returned => return .returned,
270                     .none => {},
271                 }
272                 continue;
273             }
274         }
275         return .exhausted;
276     }
277 
278     fn operator(self: *Interpreter, op: u8, data: []const u8, offset: *usize, sink: anytype, depth: usize) Error!OperatorResult {
279         switch (op) {
280             1, 18, 3, 23 => self.consumeStems(),
281             19, 20 => try self.consumeMask(data, offset),
282             4 => try self.vmoveTo(sink),
283             5 => try self.rlineTo(sink),
284             6 => try self.alternatingLineTo(sink, true),
285             7 => try self.alternatingLineTo(sink, false),
286             8 => try self.rrcurveTo(sink),
287             10 => if (try self.callSubr(sink, depth)) return .ended,
288             11 => return .returned,
289             14 => {
290                 self.clear();
291                 try sink.close();
292                 return .ended;
293             },
294             21 => try self.rmoveTo(sink),
295             22 => try self.hmoveTo(sink),
296             24 => try self.rcurveLine(sink),
297             25 => try self.rlineCurve(sink),
298             26 => try self.vvcurveTo(sink),
299             27 => try self.hhcurveTo(sink),
300             29 => if (try self.callGlobalSubr(sink, depth)) return .ended,
301             30 => try self.alternatingCurveTo(sink, false),
302             31 => try self.alternatingCurveTo(sink, true),
303             else => return error.UnsupportedOutline,
304         }
305         return .none;
306     }
307 
308     fn escaped(self: *Interpreter, op: u8, sink: anytype) Error!void {
309         switch (op) {
310             34 => try self.hflex(sink),
311             35 => try self.flex(sink),
312             36 => try self.hflex1(sink),
313             37 => try self.flex1(sink),
314             else => return error.UnsupportedOutline,
315         }
316     }
317 
318     fn push(self: *Interpreter, value: i32) Error!void {
319         if (self.stack_len >= self.stack.len) return error.InvalidFont;
320         self.stack[self.stack_len] = value;
321         self.stack_len += 1;
322     }
323 
324     fn pop(self: *Interpreter) Error!i32 {
325         if (self.stack_len == 0) return error.InvalidFont;
326         self.stack_len -= 1;
327         return self.stack[self.stack_len];
328     }
329 
330     fn clear(self: *Interpreter) void {
331         self.stack_len = 0;
332     }
333 
334     fn noteWidth(self: *Interpreter, expected_operands: usize) Error!void {
335         if (!self.width_seen and self.stack_len > expected_operands) {
336             try self.dropFirst();
337         }
338         self.width_seen = true;
339     }
340 
341     fn dropFirst(self: *Interpreter) Error!void {
342         if (self.stack_len == 0) return error.InvalidFont;
343         std.mem.copyForwards(i32, self.stack[0 .. self.stack_len - 1], self.stack[1..self.stack_len]);
344         self.stack_len -= 1;
345     }
346 
347     fn consumeStems(self: *Interpreter) void {
348         var len = self.stack_len;
349         if (!self.width_seen and len % 2 == 1) {
350             std.mem.copyForwards(i32, self.stack[0 .. len - 1], self.stack[1..len]);
351             len -= 1;
352         }
353         self.width_seen = true;
354         self.stem_count += len / 2;
355         self.clear();
356     }
357 
358     fn consumeMask(self: *Interpreter, data: []const u8, offset: *usize) Error!void {
359         if (self.stack_len > 0) self.consumeStems();
360         const mask_len = (self.stem_count + 7) / 8;
361         if (mask_len > data.len - offset.*) return error.InvalidFont;
362         offset.* += mask_len;
363         self.clear();
364     }
365 
366     fn rmoveTo(self: *Interpreter, sink: anytype) Error!void {
367         try self.noteWidth(2);
368         if (self.stack_len != 2) return error.InvalidFont;
369         const dx = self.stack[0];
370         const dy = self.stack[1];
371         self.clear();
372         try self.moveRel(sink, dx, dy);
373     }
374 
375     fn hmoveTo(self: *Interpreter, sink: anytype) Error!void {
376         try self.noteWidth(1);
377         if (self.stack_len != 1) return error.InvalidFont;
378         const dx = self.stack[0];
379         self.clear();
380         try self.moveRel(sink, dx, 0);
381     }
382 
383     fn vmoveTo(self: *Interpreter, sink: anytype) Error!void {
384         try self.noteWidth(1);
385         if (self.stack_len != 1) return error.InvalidFont;
386         const dy = self.stack[0];
387         self.clear();
388         try self.moveRel(sink, 0, dy);
389     }
390 
391     fn rlineTo(self: *Interpreter, sink: anytype) Error!void {
392         if (self.stack_len == 0 or self.stack_len % 2 != 0) return error.InvalidFont;
393         var index: usize = 0;
394         while (index < self.stack_len) : (index += 2) {
395             try self.lineRel(sink, self.stack[index], self.stack[index + 1]);
396         }
397         self.clear();
398     }
399 
400     fn alternatingLineTo(self: *Interpreter, sink: anytype, horizontal_first: bool) Error!void {
401         if (self.stack_len == 0) return error.InvalidFont;
402         var horizontal = horizontal_first;
403         for (self.stack[0..self.stack_len]) |value| {
404             if (horizontal) {
405                 try self.lineRel(sink, value, 0);
406             } else {
407                 try self.lineRel(sink, 0, value);
408             }
409             horizontal = !horizontal;
410         }
411         self.clear();
412     }
413 
414     fn rrcurveTo(self: *Interpreter, sink: anytype) Error!void {
415         if (self.stack_len == 0 or self.stack_len % 6 != 0) return error.InvalidFont;
416         var index: usize = 0;
417         while (index < self.stack_len) : (index += 6) {
418             try self.curveRel(sink, self.stack[index], self.stack[index + 1], self.stack[index + 2], self.stack[index + 3], self.stack[index + 4], self.stack[index + 5]);
419         }
420         self.clear();
421     }
422 
423     fn rcurveLine(self: *Interpreter, sink: anytype) Error!void {
424         if (self.stack_len < 8 or (self.stack_len - 2) % 6 != 0) return error.InvalidFont;
425         var index: usize = 0;
426         while (index + 2 < self.stack_len) : (index += 6) {
427             try self.curveRel(sink, self.stack[index], self.stack[index + 1], self.stack[index + 2], self.stack[index + 3], self.stack[index + 4], self.stack[index + 5]);
428         }
429         try self.lineRel(sink, self.stack[self.stack_len - 2], self.stack[self.stack_len - 1]);
430         self.clear();
431     }
432 
433     fn rlineCurve(self: *Interpreter, sink: anytype) Error!void {
434         if (self.stack_len < 8 or (self.stack_len - 6) % 2 != 0) return error.InvalidFont;
435         var index: usize = 0;
436         while (index + 6 < self.stack_len) : (index += 2) {
437             try self.lineRel(sink, self.stack[index], self.stack[index + 1]);
438         }
439         try self.curveRel(sink, self.stack[index], self.stack[index + 1], self.stack[index + 2], self.stack[index + 3], self.stack[index + 4], self.stack[index + 5]);
440         self.clear();
441     }
442 
443     fn hhcurveTo(self: *Interpreter, sink: anytype) Error!void {
444         if (self.stack_len < 4) return error.InvalidFont;
445         if (self.stack_len % 4 != 0 and self.stack_len % 4 != 1) return error.InvalidFont;
446         var index: usize = 0;
447         var dy1: i32 = 0;
448         if (self.stack_len % 4 == 1) {
449             dy1 = self.stack[0];
450             index = 1;
451         }
452         while (index < self.stack_len) : (index += 4) {
453             try self.curveRel(sink, self.stack[index], dy1, self.stack[index + 1], self.stack[index + 2], self.stack[index + 3], 0);
454             dy1 = 0;
455         }
456         self.clear();
457     }
458 
459     fn vvcurveTo(self: *Interpreter, sink: anytype) Error!void {
460         if (self.stack_len < 4) return error.InvalidFont;
461         if (self.stack_len % 4 != 0 and self.stack_len % 4 != 1) return error.InvalidFont;
462         var index: usize = 0;
463         var dx1: i32 = 0;
464         if (self.stack_len % 4 == 1) {
465             dx1 = self.stack[0];
466             index = 1;
467         }
468         while (index < self.stack_len) : (index += 4) {
469             try self.curveRel(sink, dx1, self.stack[index], self.stack[index + 1], self.stack[index + 2], 0, self.stack[index + 3]);
470             dx1 = 0;
471         }
472         self.clear();
473     }
474 
475     fn alternatingCurveTo(self: *Interpreter, sink: anytype, horizontal_first: bool) Error!void {
476         if (self.stack_len < 4) return error.InvalidFont;
477         var index: usize = 0;
478         var horizontal = horizontal_first;
479         while (index < self.stack_len) {
480             const remaining = self.stack_len - index;
481             if (remaining != 4 and remaining != 5 and remaining < 8) return error.InvalidFont;
482             if (horizontal) {
483                 const dx1 = self.stack[index];
484                 const dx2 = self.stack[index + 1];
485                 const dy2 = self.stack[index + 2];
486                 const dy3 = self.stack[index + 3];
487                 const dx3 = if (remaining == 5) self.stack[index + 4] else 0;
488                 try self.curveRel(sink, dx1, 0, dx2, dy2, dx3, dy3);
489                 index += if (remaining == 5) 5 else 4;
490             } else {
491                 const dy1 = self.stack[index];
492                 const dx2 = self.stack[index + 1];
493                 const dy2 = self.stack[index + 2];
494                 const dx3 = self.stack[index + 3];
495                 const dy3 = if (remaining == 5) self.stack[index + 4] else 0;
496                 try self.curveRel(sink, 0, dy1, dx2, dy2, dx3, dy3);
497                 index += if (remaining == 5) 5 else 4;
498             }
499             horizontal = !horizontal;
500         }
501         self.clear();
502     }
503 
504     fn hflex(self: *Interpreter, sink: anytype) Error!void {
505         if (self.stack_len != 7) return error.InvalidFont;
506         const a = self.stack[0..7];
507         try self.curveRel(sink, a[0], 0, a[1], a[2], a[3], 0);
508         try self.curveRel(sink, a[4], 0, a[5], -a[2], a[6], 0);
509         self.clear();
510     }
511 
512     fn flex(self: *Interpreter, sink: anytype) Error!void {
513         if (self.stack_len != 13) return error.InvalidFont;
514         const a = self.stack[0..13];
515         try self.curveRel(sink, a[0], a[1], a[2], a[3], a[4], a[5]);
516         try self.curveRel(sink, a[6], a[7], a[8], a[9], a[10], a[11]);
517         self.clear();
518     }
519 
520     fn hflex1(self: *Interpreter, sink: anytype) Error!void {
521         if (self.stack_len != 9) return error.InvalidFont;
522         const a = self.stack[0..9];
523         try self.curveRel(sink, a[0], a[1], a[2], a[3], a[4], 0);
524         try self.curveRel(sink, a[5], 0, a[6], a[7], a[8], -(a[1] + a[3] + a[7]));
525         self.clear();
526     }
527 
528     fn flex1(self: *Interpreter, sink: anytype) Error!void {
529         if (self.stack_len != 11) return error.InvalidFont;
530         const a = self.stack[0..11];
531         const dx_sum = a[0] + a[2] + a[4] + a[6] + a[8];
532         const dy_sum = a[1] + a[3] + a[5] + a[7] + a[9];
533         const dx6 = if (@abs(dx_sum) > @abs(dy_sum)) a[10] else -dx_sum;
534         const dy6 = if (@abs(dx_sum) > @abs(dy_sum)) -dy_sum else a[10];
535         try self.curveRel(sink, a[0], a[1], a[2], a[3], a[4], a[5]);
536         try self.curveRel(sink, a[6], a[7], a[8], a[9], dx6, dy6);
537         self.clear();
538     }
539 
540     fn callSubr(self: *Interpreter, sink: anytype, depth: usize) Error!bool {
541         const subrs = self.font.local_subrs orelse return error.InvalidFont;
542         return try self.callIndexSubr(subrs, sink, depth);
543     }
544 
545     fn callGlobalSubr(self: *Interpreter, sink: anytype, depth: usize) Error!bool {
546         return try self.callIndexSubr(self.font.global_subrs, sink, depth);
547     }
548 
549     fn callIndexSubr(self: *Interpreter, subrs: Index, sink: anytype, depth: usize) Error!bool {
550         const raw = try self.pop();
551         const biased = raw + subrBias(subrs.count);
552         if (biased < 0) return error.InvalidFont;
553         const index: u32 = @intCast(biased);
554         if (index >= subrs.count) return error.InvalidFont;
555         const subr = try subrs.item(index);
556         return switch (try self.run(subr, sink, depth + 1)) {
557             .ended => true,
558             .returned => false,
559             .exhausted => error.InvalidFont,
560         };
561     }
562 
563     fn moveRel(self: *Interpreter, sink: anytype, dx: i32, dy: i32) Error!void {
564         self.x += dx;
565         self.y += dy;
566         try sink.moveTo(self.x, self.y);
567     }
568 
569     fn lineRel(self: *Interpreter, sink: anytype, dx: i32, dy: i32) Error!void {
570         self.x += dx;
571         self.y += dy;
572         try sink.lineTo(self.x, self.y);
573     }
574 
575     fn curveRel(self: *Interpreter, sink: anytype, dx1: i32, dy1: i32, dx2: i32, dy2: i32, dx3: i32, dy3: i32) Error!void {
576         const x1 = self.x + dx1;
577         const y1 = self.y + dy1;
578         const x2 = x1 + dx2;
579         const y2 = y1 + dy2;
580         self.x = x2 + dx3;
581         self.y = y2 + dy3;
582         try sink.curveTo(x1, y1, x2, y2, self.x, self.y);
583     }
584 };
585 
586 fn subrBias(count: u32) i32 {
587     if (count < 1240) return 107;
588     if (count < 33900) return 1131;
589     return 32768;
590 }
591 
592 fn isDictOperator(byte: u8) bool {
593     return byte <= 21 and byte != 28;
594 }
595 
596 fn readDictNumber(data: []const u8, offset: *usize, first: u8) Error!i32 {
597     return switch (first) {
598         28 => value: {
599             const value = try readI16(data, offset.*);
600             offset.* += 2;
601             break :value value;
602         },
603         29 => value: {
604             const value = try readI32(data, offset.*);
605             offset.* += 4;
606             break :value value;
607         },
608         30 => value: {
609             try skipReal(data, offset);
610             break :value 0;
611         },
612         32...246 => @as(i32, first) - 139,
613         247...250 => value: {
614             if (offset.* >= data.len) return error.InvalidFont;
615             const b1 = data[offset.*];
616             offset.* += 1;
617             break :value (@as(i32, first) - 247) * 256 + @as(i32, b1) + 108;
618         },
619         251...254 => value: {
620             if (offset.* >= data.len) return error.InvalidFont;
621             const b1 = data[offset.*];
622             offset.* += 1;
623             break :value -((@as(i32, first) - 251) * 256) - @as(i32, b1) - 108;
624         },
625         255 => value: {
626             const value = try readI32(data, offset.*);
627             offset.* += 4;
628             break :value value;
629         },
630         else => error.InvalidFont,
631     };
632 }
633 
634 fn readCharNumber(data: []const u8, offset: *usize, first: u8) Error!i32 {
635     return switch (first) {
636         28 => value: {
637             const value = try readI16(data, offset.*);
638             offset.* += 2;
639             break :value value;
640         },
641         32...246 => @as(i32, first) - 139,
642         247...250 => value: {
643             if (offset.* >= data.len) return error.InvalidFont;
644             const b1 = data[offset.*];
645             offset.* += 1;
646             break :value (@as(i32, first) - 247) * 256 + @as(i32, b1) + 108;
647         },
648         251...254 => value: {
649             if (offset.* >= data.len) return error.InvalidFont;
650             const b1 = data[offset.*];
651             offset.* += 1;
652             break :value -((@as(i32, first) - 251) * 256) - @as(i32, b1) - 108;
653         },
654         255 => value: {
655             const value = try readI32(data, offset.*);
656             offset.* += 4;
657             break :value value;
658         },
659         else => error.InvalidFont,
660     };
661 }
662 
663 fn skipReal(data: []const u8, offset: *usize) Error!void {
664     while (offset.* < data.len) {
665         const byte = data[offset.*];
666         offset.* += 1;
667         if ((byte >> 4) == 0xf or (byte & 0xf) == 0xf) return;
668     }
669     return error.InvalidFont;
670 }
671 
672 fn readIndexOffset(data: []const u8, offsets_offset: usize, off_size: u8, index: usize) Error!u32 {
673     const offset = offsets_offset + index * @as(usize, off_size);
674     if (offset > data.len or @as(usize, off_size) > data.len - offset) return error.InvalidFont;
675     var value: u32 = 0;
676     var i: usize = 0;
677     while (i < off_size) : (i += 1) {
678         value = (value << 8) | data[offset + i];
679     }
680     return value;
681 }
682 
683 fn readU16(data: []const u8, offset: usize) Error!u16 {
684     if (offset + 2 > data.len) return error.InvalidFont;
685     return std.mem.readInt(u16, data[offset..][0..2], .big);
686 }
687 
688 fn readI16(data: []const u8, offset: usize) Error!i16 {
689     return @bitCast(try readU16(data, offset));
690 }
691 
692 fn readI32(data: []const u8, offset: usize) Error!i32 {
693     if (offset + 4 > data.len) return error.InvalidFont;
694     return std.mem.readInt(i32, data[offset..][0..4], .big);
695 }