lib/ui/src/asset/embedded.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const alloc_phase = @import("alloc_phase");
  3 const font_bytes = @import("ui_fonts");
  4 
  5 const model = @import("model.zig");
  6 const registry = @import("registry.zig");
  7 
  8 const noto_bytes = font_bytes.noto;
  9 const iosevka_bytes = font_bytes.iosevka;
 10 
 11 pub const Face = struct {
 12     family: []const u8,
 13     descriptor: model.FontDescriptor,
 14     bytes: []const u8,
 15 };
 16 
 17 pub const faces = [_]Face{
 18     .{
 19         .family = "Noto Sans",
 20         .descriptor = .{
 21             .family_len = "Noto Sans".len,
 22             .units_per_em = 1000,
 23             .weight = 400,
 24             .width = 5,
 25             .ascent = 1.069,
 26             .descent = -0.293,
 27             .coverage = .{ 0xa000020f, 0x00000002, 0, 0, 0, 0, 0, 0 },
 28         },
 29         .bytes = noto_bytes,
 30     },
 31     .{
 32         .family = "Iosevka",
 33         .descriptor = .{
 34             .family_len = "Iosevka".len,
 35             .units_per_em = 1000,
 36             .weight = 400,
 37             .width = 5,
 38             .monospace = 1,
 39             .ascent = 0.965,
 40             .descent = -0.285,
 41             .coverage = .{ 0xa000020f, 0x00000002, 0, 0, 0, 0, 0, 0 },
 42         },
 43         .bytes = iosevka_bytes,
 44     },
 45 };
 46 
 47 pub const Embedded = struct {
 48     pub const Limits = struct {
 49         font_bytes: usize,
 50         descriptor_bytes: usize,
 51         family_bytes: usize,
 52     };
 53 
 54     pub const Capacity = struct {
 55         retained_bytes: usize,
 56 
 57         pub fn derive(limits: Limits) error{CapacityOverflow}!Capacity {
 58             const fonts = std.math.add(usize, limits.font_bytes, limits.descriptor_bytes) catch return error.CapacityOverflow;
 59             return .{ .retained_bytes = std.math.add(usize, fonts, limits.family_bytes) catch return error.CapacityOverflow };
 60         }
 61     };
 62 
 63     pub fn defaultLimits() Limits {
 64         return .{
 65             .font_bytes = noto_bytes.len + iosevka_bytes.len + faces.len,
 66             .descriptor_bytes = @sizeOf(@TypeOf(faces)),
 67             .family_bytes = faces[0].family.len + faces[1].family.len + faces.len,
 68         };
 69     }
 70 
 71     pub const claim: alloc_phase.capacity.Declaration = .{
 72         .source = .{
 73             .id = "ui.asset_registry",
 74             .kind = .startup_static,
 75             .limit_source = .application_default,
 76             .storage = .{
 77                 .covered = &.{
 78                     .{ .id = "embedded_font_bytes", .lifetime = .steady, .detail = "the two pinned TrueType subsets in executable read-only storage" },
 79                     .{ .id = "embedded_descriptors", .lifetime = .steady, .detail = "the constant font traits and family names registered before UI readiness" },
 80                 },
 81                 .excluded = &.{ "caller-owned registry table slots and owned payload pool", "registered system faces" },
 82             },
 83             .capacity = .{
 84                 .inputs = &.{
 85                     alloc_phase.capacity.bindInput(Limits, "font_bytes", "font_bytes"),
 86                     alloc_phase.capacity.bindInput(Limits, "descriptor_bytes", "descriptor_bytes"),
 87                     alloc_phase.capacity.bindInput(Limits, "family_bytes", "family_bytes"),
 88                 },
 89                 .type_selectors = &.{},
 90                 .nodes = &.{
 91                     .{ .input = 0 },
 92                     .{ .input = 1 },
 93                     .{ .input = 2 },
 94                     .{ .add = .{ .left = 0, .right = 1 } },
 95                     .{ .add = .{ .left = 3, .right = 2 } },
 96                 },
 97                 .assertions = &.{.{
 98                     .scope = .closure_total,
 99                     .measure = .retained,
100                     .relation = .upper_bound,
101                     .expression = 4,
102                 }},
103             },
104             .overload = .{ .kind = .not_applicable, .detail = "the two read-only faces have a fixed executable image size" },
105             .risks = .{
106                 .transitive = .{ .status = .excluded, .detail = "the embedded font bytes and descriptors require no allocator" },
107                 .foreign = .{ .status = .excluded, .detail = "the startup corpus reads no host font service" },
108             },
109             .obligations = &.{
110                 .{ .key = "ui_registry_startup", .role = .acquisition },
111                 .{ .key = "ui_registry_descriptors", .role = .integration },
112                 .{ .key = "ui_registry_teardown", .role = .teardown },
113             },
114         },
115         .bindings = .{
116             .owner = @This(),
117             .default_limits = alloc_phase.capacity.selector(@This().defaultLimits),
118         },
119     };
120 
121     /// Registers the two regular faces before a UI session begins publishing.
122     /// A bold request matches the regular face until a bold face is registered.
123     pub fn register(table: *registry.Registry) model.Error![faces.len]model.AssetHandle {
124         std.debug.assert(table.phase == .steady);
125         if (table.limits.fonts - table.font_used < faces.len) return error.FontCapacityExceeded;
126         var handles: [faces.len]model.AssetHandle = undefined;
127         for (faces, 0..) |face, index| {
128             handles[index] = try table.registerFont(face.family, face.descriptor, .{ .borrowed = face.bytes });
129         }
130         return handles;
131     }
132 
133     /// Returns a ready registry with both embedded faces already registered.
134     pub fn open(allocator: std.mem.Allocator, limits: registry.Registry.Limits) (registry.Registry.InitError || model.Error)!registry.Registry {
135         if (limits.fonts < faces.len) return error.FontCapacityExceeded;
136         var table = try registry.Registry.init(allocator, limits);
137         errdefer table.deinit(allocator);
138         table.activate();
139         _ = try register(&table);
140         return table;
141     }
142 };