lib/filigree/src/font/directory.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const binary = @import("binary.zig");
 3 const model = @import("model.zig");
 4 
 5 const FontError = model.FontError;
 6 const Table = binary.Table;
 7 const Tag = model.Tag;
 8 const readU16 = binary.readU16;
 9 const readU32 = binary.readU32;
10 const tag = model.tag;
11 
12 pub fn table(data: []const u8, comptime name: []const u8) FontError!Table {
13     return tableAt(data, 0, name);
14 }
15 
16 pub fn tableAt(data: []const u8, directory_offset: usize, comptime name: []const u8) FontError!Table {
17     return tableAtTag(data, directory_offset, tag(name));
18 }
19 
20 pub fn tableAtTag(data: []const u8, directory_offset: usize, wanted: Tag) FontError!Table {
21     if (directory_offset > data.len or data.len - directory_offset < 12) return error.InvalidFont;
22     const num_tables = try readU16(data, directory_offset + 4);
23     if (@as(usize, num_tables) > (data.len - directory_offset - 12) / 16) return error.InvalidFont;
24     for (0..num_tables) |i| {
25         const record = directory_offset + 12 + i * 16;
26         if (try readU32(data, record) != wanted) continue;
27         const offset = try readU32(data, record + 8);
28         const len = try readU32(data, record + 12);
29         const start: usize = @intCast(offset);
30         const size: usize = @intCast(len);
31         if (start > data.len or size > data.len - start) return error.InvalidFont;
32         return .{ .offset = start, .len = size };
33     }
34     return error.MissingTable;
35 }
36 
37 pub fn optional(data: []const u8, comptime name: []const u8) ?Table {
38     return table(data, name) catch null;
39 }
40 
41 pub fn optionalAt(data: []const u8, directory_offset: usize, comptime name: []const u8) ?Table {
42     return tableAt(data, directory_offset, name) catch null;
43 }
44 
45 pub fn optionalAtTag(data: []const u8, directory_offset: usize, table_tag: Tag) ?Table {
46     return tableAtTag(data, directory_offset, table_tag) catch null;
47 }
48 
49 pub fn fontDirectoryOffset(data: []const u8, face_index: u32) FontError!usize {
50     if (data.len < 12) return error.InvalidFont;
51     if (try readU32(data, 0) != tag("ttcf")) {
52         if (face_index != 0) return error.InvalidFont;
53         return 0;
54     }
55 
56     const version = try readU32(data, 4);
57     if (version != 0x00010000 and version != 0x00020000) return error.InvalidFont;
58     const face_count = try readU32(data, 8);
59     if (face_count == 0 or face_index >= face_count) return error.InvalidFont;
60     const face_count_usize: usize = @intCast(face_count);
61     if (face_count_usize > (data.len - 12) / 4) return error.InvalidFont;
62     const record = 12 + @as(usize, @intCast(face_index)) * 4;
63     const offset = try readU32(data, record);
64     const directory_offset: usize = @intCast(offset);
65     if (directory_offset > data.len or data.len - directory_offset < 12) return error.InvalidFont;
66     return directory_offset;
67 }
68 
69 test "table lookup rejects empty directory" {
70     try std.testing.expectError(error.InvalidFont, table(&.{}, "head"));
71 }