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 }