lib/filigree/src/fixture/font.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

   1 const std = @import("std");
   2 const binary = @import("binary.zig");
   3 const gpos_bytes = @import("positioning.zig");
   4 const gsub_bytes = @import("substitution.zig");
   5 
   6 const assembleCollectionBytes = binary.assembleCollection;
   7 const align4 = binary.align4;
   8 const appendI16 = binary.appendI16;
   9 const appendTag = binary.appendTag;
  10 const appendU16 = binary.appendU16;
  11 const appendU24 = binary.appendU24;
  12 const appendU32 = binary.appendU32;
  13 const checksum = binary.checksum;
  14 const writeI16 = binary.writeI16;
  15 const writeU16 = binary.writeU16;
  16 const writeU32 = binary.writeU32;
  17 
  18 const TableBytes = struct {
  19     name: [4]u8,
  20     bytes: []u8,
  21 };
  22 
  23 const Options = @import("options.zig").Options;
  24 
  25 pub fn create(allocator: std.mem.Allocator) ![]u8 {
  26     return createWithOptions(allocator, .{});
  27 }
  28 
  29 pub fn createWithKern(allocator: std.mem.Allocator) ![]u8 {
  30     return createWithOptions(allocator, .{ .kern = true });
  31 }
  32 
  33 pub fn createWithVariationSequences(allocator: std.mem.Allocator) ![]u8 {
  34     return createWithOptions(allocator, .{ .variation_sequences = true });
  35 }
  36 
  37 pub fn createWithVariations(allocator: std.mem.Allocator) ![]u8 {
  38     return createWithOptions(allocator, .{ .variations = true });
  39 }
  40 
  41 pub fn createWithAxisVariations(allocator: std.mem.Allocator) ![]u8 {
  42     return createWithOptions(allocator, .{ .variations = true, .axis_variations = true });
  43 }
  44 
  45 pub fn createWithHorizontalMetricVariations(allocator: std.mem.Allocator) ![]u8 {
  46     return createWithOptions(allocator, .{ .variations = true, .horizontal_metric_variations = true });
  47 }
  48 
  49 pub fn createWithHorizontalMetricAxisVariations(allocator: std.mem.Allocator) ![]u8 {
  50     return createWithOptions(allocator, .{ .variations = true, .axis_variations = true, .horizontal_metric_variations = true });
  51 }
  52 
  53 pub fn createWithVerticalMetrics(allocator: std.mem.Allocator) ![]u8 {
  54     return createWithOptions(allocator, .{ .vertical_metrics = true });
  55 }
  56 
  57 pub fn createWithVerticalOrigins(allocator: std.mem.Allocator) ![]u8 {
  58     return createWithOptions(allocator, .{ .vertical_metrics = true, .vertical_origins = true });
  59 }
  60 
  61 pub fn createWithVerticalMetricVariations(allocator: std.mem.Allocator) ![]u8 {
  62     return createWithOptions(allocator, .{ .variations = true, .vertical_metrics = true, .vertical_metric_variations = true });
  63 }
  64 
  65 pub fn createWithVerticalMetricAxisVariations(allocator: std.mem.Allocator) ![]u8 {
  66     return createWithOptions(allocator, .{ .variations = true, .axis_variations = true, .vertical_metrics = true, .vertical_metric_variations = true });
  67 }
  68 
  69 pub fn createWithMetricVariations(allocator: std.mem.Allocator) ![]u8 {
  70     return createWithOptions(allocator, .{ .variations = true, .metric_variations = true });
  71 }
  72 
  73 pub fn createWithMetricAxisVariations(allocator: std.mem.Allocator) ![]u8 {
  74     return createWithOptions(allocator, .{ .variations = true, .axis_variations = true, .metric_variations = true });
  75 }
  76 
  77 pub fn createWithGdefGlyphClasses(allocator: std.mem.Allocator) ![]u8 {
  78     return createWithOptions(allocator, .{ .gdef_classes = true });
  79 }
  80 
  81 pub fn createWithGsubLigatureAndGdefGlyphClasses(allocator: std.mem.Allocator) ![]u8 {
  82     return createWithOptions(allocator, .{ .gsub_ligature = true, .gdef_classes = true });
  83 }
  84 
  85 pub fn createWithGsubLigatureAndGdefCarets(allocator: std.mem.Allocator) ![]u8 {
  86     return createWithOptions(allocator, .{ .gsub_ligature = true, .gdef_classes = true, .gdef_carets = true });
  87 }
  88 
  89 pub fn createWithOutlines(allocator: std.mem.Allocator) ![]u8 {
  90     return createWithOptions(allocator, .{ .outlines = true });
  91 }
  92 
  93 pub fn createFallbackWithOutlines(allocator: std.mem.Allocator) ![]u8 {
  94     return createWithOptions(allocator, .{
  95         .outlines = true,
  96         .notdef_outline = true,
  97         .fallback_coverage = true,
  98     });
  99 }
 100 
 101 pub fn createWithMathAssembly(allocator: std.mem.Allocator) ![]u8 {
 102     return createWithOptions(allocator, .{ .outlines = true, .math_assembly = true });
 103 }
 104 
 105 pub fn createWithCffOutlines(allocator: std.mem.Allocator) ![]u8 {
 106     return createWithOptions(allocator, .{ .cff_outlines = true });
 107 }
 108 
 109 pub fn createCollection(allocator: std.mem.Allocator) ![]u8 {
 110     const first = try create(allocator);
 111     defer allocator.free(first);
 112     const second = try createWithOptions(allocator, .{ .gsub_single = true });
 113     defer allocator.free(second);
 114     const fonts = [_][]const u8{ first, second };
 115     return assembleCollectionBytes(allocator, &fonts);
 116 }
 117 
 118 pub fn createWithOptions(allocator: std.mem.Allocator, options: Options) ![]u8 {
 119     var tables: [32]TableBytes = undefined;
 120     var table_count: usize = 0;
 121     defer {
 122         for (tables[0..table_count]) |table| allocator.free(table.bytes);
 123     }
 124 
 125     try appendCoreTables(allocator, &tables, &table_count, options);
 126     try appendOutlineTables(allocator, &tables, &table_count, options);
 127     try appendMetricTables(allocator, &tables, &table_count, options);
 128     try appendVariationTables(allocator, &tables, &table_count, options);
 129     try appendKernTable(allocator, &tables, &table_count, options);
 130     try appendGsubTables(allocator, &tables, &table_count, options);
 131     try appendGposTables(allocator, &tables, &table_count, options);
 132     try appendGdefTable(allocator, &tables, &table_count, options);
 133     try appendMathTable(allocator, &tables, &table_count, options);
 134 
 135     return assemble(allocator, tables[0..table_count], if (options.cff_outlines) 0x4f54544f else 0x00010000);
 136 }
 137 
 138 fn appendCoreTables(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 139     var cmap = std.ArrayListUnmanaged(u8).empty;
 140     errdefer cmap.deinit(allocator);
 141     try appendCmap(allocator, &cmap, options);
 142     try addTable(allocator, tables, table_count, "cmap", &cmap);
 143 
 144     var head = std.ArrayListUnmanaged(u8).empty;
 145     errdefer head.deinit(allocator);
 146     try appendHead(allocator, &head);
 147     try addTable(allocator, tables, table_count, "head", &head);
 148 
 149     var hhea = std.ArrayListUnmanaged(u8).empty;
 150     errdefer hhea.deinit(allocator);
 151     try appendHhea(allocator, &hhea);
 152     try addTable(allocator, tables, table_count, "hhea", &hhea);
 153 
 154     var hmtx = std.ArrayListUnmanaged(u8).empty;
 155     errdefer hmtx.deinit(allocator);
 156     try appendHmtx(allocator, &hmtx, options);
 157     try addTable(allocator, tables, table_count, "hmtx", &hmtx);
 158 
 159     var maxp = std.ArrayListUnmanaged(u8).empty;
 160     errdefer maxp.deinit(allocator);
 161     try appendMaxp(allocator, &maxp);
 162     try addTable(allocator, tables, table_count, "maxp", &maxp);
 163 
 164     var name = std.ArrayListUnmanaged(u8).empty;
 165     errdefer name.deinit(allocator);
 166     try appendName(allocator, &name);
 167     try addTable(allocator, tables, table_count, "name", &name);
 168 }
 169 
 170 fn appendOutlineTables(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 171     if (options.cff_outlines) {
 172         var cff = std.ArrayListUnmanaged(u8).empty;
 173         errdefer cff.deinit(allocator);
 174         try appendCff(allocator, &cff);
 175         try addTable(allocator, tables, table_count, "CFF ", &cff);
 176         return;
 177     }
 178 
 179     if (!options.outlines) return;
 180 
 181     var glyf = std.ArrayListUnmanaged(u8).empty;
 182     errdefer glyf.deinit(allocator);
 183     var offsets = @as([98]u32, @splat(0));
 184     try appendGlyf(allocator, &glyf, &offsets, options);
 185     try addTable(allocator, tables, table_count, "glyf", &glyf);
 186 
 187     var loca = std.ArrayListUnmanaged(u8).empty;
 188     errdefer loca.deinit(allocator);
 189     try appendLoca(allocator, &loca, &offsets);
 190     try addTable(allocator, tables, table_count, "loca", &loca);
 191 }
 192 
 193 fn appendMetricTables(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 194     if (options.metric_variations) {
 195         var os2 = std.ArrayListUnmanaged(u8).empty;
 196         errdefer os2.deinit(allocator);
 197         try appendOs2(allocator, &os2);
 198         try addTable(allocator, tables, table_count, "OS/2", &os2);
 199     }
 200 
 201     if (options.vertical_metrics or options.vertical_metric_variations) {
 202         var vhea = std.ArrayListUnmanaged(u8).empty;
 203         errdefer vhea.deinit(allocator);
 204         try appendVhea(allocator, &vhea);
 205         try addTable(allocator, tables, table_count, "vhea", &vhea);
 206 
 207         var vmtx = std.ArrayListUnmanaged(u8).empty;
 208         errdefer vmtx.deinit(allocator);
 209         try appendVmtx(allocator, &vmtx);
 210         try addTable(allocator, tables, table_count, "vmtx", &vmtx);
 211     }
 212 
 213     if (options.vertical_origins) {
 214         var vorg = std.ArrayListUnmanaged(u8).empty;
 215         errdefer vorg.deinit(allocator);
 216         try appendVorg(allocator, &vorg);
 217         try addTable(allocator, tables, table_count, "VORG", &vorg);
 218     }
 219 }
 220 
 221 fn appendVariationTables(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 222     if (needsVariations(options)) {
 223         var fvar = std.ArrayListUnmanaged(u8).empty;
 224         errdefer fvar.deinit(allocator);
 225         try appendFvar(allocator, &fvar);
 226         try addTable(allocator, tables, table_count, "fvar", &fvar);
 227     }
 228 
 229     if (options.axis_variations) {
 230         var avar = std.ArrayListUnmanaged(u8).empty;
 231         errdefer avar.deinit(allocator);
 232         try appendAvar(allocator, &avar);
 233         try addTable(allocator, tables, table_count, "avar", &avar);
 234     }
 235 
 236     if (options.horizontal_metric_variations) {
 237         var hvar = std.ArrayListUnmanaged(u8).empty;
 238         errdefer hvar.deinit(allocator);
 239         try appendHvar(allocator, &hvar);
 240         try addTable(allocator, tables, table_count, "HVAR", &hvar);
 241     }
 242 
 243     if (options.vertical_metric_variations) {
 244         var vvar = std.ArrayListUnmanaged(u8).empty;
 245         errdefer vvar.deinit(allocator);
 246         try appendVvar(allocator, &vvar);
 247         try addTable(allocator, tables, table_count, "VVAR", &vvar);
 248     }
 249 
 250     if (options.metric_variations) {
 251         var mvar = std.ArrayListUnmanaged(u8).empty;
 252         errdefer mvar.deinit(allocator);
 253         try appendMvar(allocator, &mvar);
 254         try addTable(allocator, tables, table_count, "MVAR", &mvar);
 255     }
 256 }
 257 
 258 fn appendKernTable(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 259     if (!options.kern) return;
 260     var kern = std.ArrayListUnmanaged(u8).empty;
 261     errdefer kern.deinit(allocator);
 262     try appendKern(allocator, &kern);
 263     try addTable(allocator, tables, table_count, "kern", &kern);
 264 }
 265 
 266 fn appendGsubTables(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 267     if (options.gsub_single) try appendGsubSingleTable(allocator, tables, table_count, 0);
 268     if (options.gsub_single_ignore_marks) try appendGsubSingleTable(allocator, tables, table_count, 0x0008);
 269     if (options.gsub_alternate) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubAlternateSubstitution);
 270     if (options.gsub_contextual) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubContextualSubstitution);
 271     if (options.gsub_class_contextual) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubClassContextualSubstitution);
 272     if (options.gsub_contextual_coverage) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubCoverageContextualSubstitution);
 273     if (options.gsub_chained_contextual) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubChainedContextualSubstitution);
 274     if (options.gsub_chained_simple_contextual) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubChainedSimpleContextualSubstitution);
 275     if (options.gsub_chained_class_contextual) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubChainedClassContextualSubstitution);
 276     if (options.gsub_ligature) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubLigature);
 277     if (options.gsub_contextual_ligature) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubContextualLigature);
 278     if (options.gsub_multiple) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubMultipleSubstitution);
 279     if (options.gsub_reverse) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubReverseChainingSingleSubstitution);
 280     if (options.gsub_localized_forms) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubLocalizedForms);
 281     if (options.gsub_contextual_alternates) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubContextualAlternates);
 282     if (options.gsub_required_contextual_alternates) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubRequiredContextualAlternates);
 283     if (options.gsub_left_to_right_alternates) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubLeftToRightAlternates);
 284     if (options.gsub_left_to_right_mirrored_forms) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubLeftToRightMirroredForms);
 285     if (options.gsub_right_to_left_alternates) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubRightToLeftAlternates);
 286     if (options.gsub_right_to_left_mirrored_forms) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubRightToLeftMirroredForms);
 287     if (options.gsub_fractions) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubFractions);
 288     if (options.gsub_fraction_components) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubFractionComponents);
 289     if (options.gsub_arabic_joining_forms) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubArabicJoiningForms);
 290     if (options.gsub_feature_variations) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubFeatureVariations);
 291     if (options.gsub_required_variation_alternates) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubRequiredVariationAlternates);
 292     if (options.gsub_vertical_alternates) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubVerticalAlternates);
 293     if (options.gsub_vertical_rotation_alternates) try appendGsubTable(allocator, tables, table_count, gsub_bytes.appendGsubVerticalRotationAlternates);
 294 }
 295 
 296 fn appendGposTables(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 297     if (options.gpos_single) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposSingleAdjustment);
 298     if (options.gpos_single_variation) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposSingleVariationAdjustment);
 299     if (options.gpos_feature_variations) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposFeatureVariations);
 300     if (options.gpos_contextual_simple_single) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposSimpleContextualSingleAdjustment);
 301     if (options.gpos_contextual_class_single) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposClassContextualSingleAdjustment);
 302     if (options.gpos_contextual_single) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposContextualSingleAdjustment);
 303     if (options.gpos_chained_contextual_single) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposChainedContextualSingleAdjustment);
 304     if (options.gpos_chained_simple_contextual_single) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposChainedSimpleContextualSingleAdjustment);
 305     if (options.gpos_chained_class_contextual_single) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposChainedClassContextualSingleAdjustment);
 306     if (options.gpos_contextual_simple_pair) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposSimpleContextualPairAdjustment);
 307     if (options.gpos_contextual_class_pair) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposClassContextualPairAdjustment);
 308     if (options.gpos_contextual_pair) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposContextualPairAdjustment);
 309     if (options.gpos_chained_contextual_pair) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposChainedContextualPairAdjustment);
 310     if (options.gpos_chained_simple_contextual_pair) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposChainedSimpleContextualPairAdjustment);
 311     if (options.gpos_chained_class_contextual_pair) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposChainedClassContextualPairAdjustment);
 312     if (options.gpos_pair) try appendGposPairTable(allocator, tables, table_count, 0);
 313     if (options.gpos_vertical_pair) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposVerticalPairAdjustment);
 314     if (options.gpos_pair_ignore_marks) try appendGposPairTable(allocator, tables, table_count, 0x0008);
 315     if (options.gpos_cursive) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposCursiveAttachment);
 316     if (options.gpos_mark_to_base) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposMarkToBase);
 317     if (options.gpos_mark_to_base_then_pair) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposMarkToBaseThenPair);
 318     if (options.gpos_above_base_mark) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposAboveBaseMarkPositioning);
 319     if (options.gpos_below_base_mark) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposBelowBaseMarkPositioning);
 320     if (options.gpos_mark_to_ligature) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposMarkToLigature);
 321     if (options.gpos_mark_to_mark) try appendGposTable(allocator, tables, table_count, gpos_bytes.appendGposMarkToMark);
 322 }
 323 
 324 fn appendGdefTable(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 325     if (!options.gdef_classes and !options.gdef_carets and !options.gdef_variations) return;
 326     var gdef = std.ArrayListUnmanaged(u8).empty;
 327     errdefer gdef.deinit(allocator);
 328     try appendGdef(allocator, &gdef, options.gdef_carets, options.gpos_mark_to_mark, options.gdef_variations);
 329     try addTable(allocator, tables, table_count, "GDEF", &gdef);
 330 }
 331 
 332 fn appendMathTable(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, options: Options) !void {
 333     if (!options.math_assembly) return;
 334     var math = std.ArrayListUnmanaged(u8).empty;
 335     errdefer math.deinit(allocator);
 336     try appendMathAssembly(allocator, &math);
 337     try addTable(allocator, tables, table_count, "MATH", &math);
 338 }
 339 
 340 fn appendGsubSingleTable(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, lookup_flag: u16) !void {
 341     var gsub = std.ArrayListUnmanaged(u8).empty;
 342     errdefer gsub.deinit(allocator);
 343     try gsub_bytes.appendGsubSingleSubstitution(allocator, &gsub, lookup_flag);
 344     try addTable(allocator, tables, table_count, "GSUB", &gsub);
 345 }
 346 
 347 fn appendGsubTable(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, comptime build: anytype) !void {
 348     var gsub = std.ArrayListUnmanaged(u8).empty;
 349     errdefer gsub.deinit(allocator);
 350     try build(allocator, &gsub);
 351     try addTable(allocator, tables, table_count, "GSUB", &gsub);
 352 }
 353 
 354 fn appendGposPairTable(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, lookup_flag: u16) !void {
 355     var gpos = std.ArrayListUnmanaged(u8).empty;
 356     errdefer gpos.deinit(allocator);
 357     try gpos_bytes.appendGposPairAdjustment(allocator, &gpos, lookup_flag);
 358     try addTable(allocator, tables, table_count, "GPOS", &gpos);
 359 }
 360 
 361 fn appendGposTable(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, comptime build: anytype) !void {
 362     var gpos = std.ArrayListUnmanaged(u8).empty;
 363     errdefer gpos.deinit(allocator);
 364     try build(allocator, &gpos);
 365     try addTable(allocator, tables, table_count, "GPOS", &gpos);
 366 }
 367 
 368 fn addTable(allocator: std.mem.Allocator, tables: *[32]TableBytes, table_count: *usize, comptime name: []const u8, out: *std.ArrayListUnmanaged(u8)) !void {
 369     tables[table_count.*] = .{ .name = name[0..4].*, .bytes = try out.toOwnedSlice(allocator) };
 370     table_count.* += 1;
 371 }
 372 
 373 fn needsVariations(options: Options) bool {
 374     return options.variations or
 375         options.axis_variations or
 376         options.horizontal_metric_variations or
 377         options.vertical_metric_variations or
 378         options.metric_variations or
 379         options.gpos_single_variation or
 380         options.gdef_variations or
 381         options.gsub_feature_variations or
 382         options.gsub_required_variation_alternates or
 383         options.gpos_feature_variations;
 384 }
 385 
 386 fn assemble(allocator: std.mem.Allocator, tables: []const TableBytes, sfnt_version: u32) ![]u8 {
 387     const table_count = tables.len;
 388     const header_len = 12 + table_count * 16;
 389     var total_len = header_len;
 390     for (tables) |table| {
 391         total_len = align4(total_len);
 392         total_len += table.bytes.len;
 393     }
 394 
 395     const bytes = try allocator.alloc(u8, total_len);
 396     @memset(bytes, 0);
 397 
 398     writeU32(bytes, 0, sfnt_version);
 399     writeU16(bytes, 4, @intCast(table_count));
 400 
 401     var data_offset = header_len;
 402     for (tables, 0..) |table, i| {
 403         data_offset = align4(data_offset);
 404         const record = 12 + i * 16;
 405         bytes[record..][0..4].* = table.name;
 406         writeU32(bytes, record + 4, checksum(table.bytes));
 407         writeU32(bytes, record + 8, @intCast(data_offset));
 408         writeU32(bytes, record + 12, @intCast(table.bytes.len));
 409         @memcpy(bytes[data_offset..][0..table.bytes.len], table.bytes);
 410         data_offset += table.bytes.len;
 411     }
 412 
 413     return bytes;
 414 }
 415 
 416 fn appendCmap(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), options: Options) !void {
 417     try appendU16(allocator, out, 0);
 418     try appendU16(allocator, out, if (options.variation_sequences) 2 else 1);
 419     if (options.fallback_coverage) {
 420         try appendU16(allocator, out, 3);
 421         try appendU16(allocator, out, 1);
 422         try appendU32(allocator, out, 12);
 423         try appendCmapFormat4Fallback(allocator, out);
 424         return;
 425     } else if (options.math_assembly) {
 426         writeU16(out.items, 2, 1);
 427         try appendU16(allocator, out, 3);
 428         try appendU16(allocator, out, 10);
 429         try appendU32(allocator, out, 12);
 430         try appendCmapFormat12MathAssembly(allocator, out);
 431         return;
 432     } else if (options.gsub_arabic_joining_forms) {
 433         writeU16(out.items, 2, 1);
 434         try appendU16(allocator, out, 3);
 435         try appendU16(allocator, out, 10);
 436         try appendU32(allocator, out, 12);
 437         try appendCmapFormat12ArabicJoining(allocator, out);
 438         return;
 439     } else if (options.variation_sequences) {
 440         try appendU16(allocator, out, 0);
 441         try appendU16(allocator, out, 5);
 442         try appendU32(allocator, out, 20);
 443         try appendU16(allocator, out, 3);
 444         try appendU16(allocator, out, 1);
 445         try appendU32(allocator, out, 50);
 446         try appendCmapFormat14VariationSequences(allocator, out);
 447     } else {
 448         try appendU16(allocator, out, 3);
 449         try appendU16(allocator, out, 1);
 450         try appendU32(allocator, out, 12);
 451     }
 452     try appendCmapFormat4Ascii(allocator, out);
 453 }
 454 
 455 fn appendCmapFormat12MathAssembly(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 456     try appendU16(allocator, out, 12);
 457     try appendU16(allocator, out, 0);
 458     try appendU32(allocator, out, 76);
 459     try appendU32(allocator, out, 0);
 460     try appendU32(allocator, out, 5);
 461     try appendU32(allocator, out, 0x20);
 462     try appendU32(allocator, out, 0x7e);
 463     try appendU32(allocator, out, 1);
 464     try appendU32(allocator, out, 0x2192);
 465     try appendU32(allocator, out, 0x2192);
 466     try appendU32(allocator, out, 'A' - 31);
 467     try appendU32(allocator, out, 0x2211);
 468     try appendU32(allocator, out, 0x2211);
 469     try appendU32(allocator, out, 'A' - 31);
 470     try appendU32(allocator, out, 0x221a);
 471     try appendU32(allocator, out, 0x221a);
 472     try appendU32(allocator, out, 'R' - 31);
 473     try appendU32(allocator, out, 0x25cc);
 474     try appendU32(allocator, out, 0x25cc);
 475     try appendU32(allocator, out, 96);
 476 }
 477 
 478 fn appendCmapFormat12ArabicJoining(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 479     try appendU16(allocator, out, 12);
 480     try appendU16(allocator, out, 0);
 481     try appendU32(allocator, out, 64);
 482     try appendU32(allocator, out, 0);
 483     try appendU32(allocator, out, 4);
 484     try appendU32(allocator, out, 0x20);
 485     try appendU32(allocator, out, 0x7e);
 486     try appendU32(allocator, out, 1);
 487     try appendU32(allocator, out, 0x627);
 488     try appendU32(allocator, out, 0x627);
 489     try appendU32(allocator, out, 'B' - 31);
 490     try appendU32(allocator, out, 0x628);
 491     try appendU32(allocator, out, 0x628);
 492     try appendU32(allocator, out, 'A' - 31);
 493     try appendU32(allocator, out, 0x25cc);
 494     try appendU32(allocator, out, 0x25cc);
 495     try appendU32(allocator, out, 96);
 496 }
 497 
 498 fn appendCmapFormat4Ascii(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 499     try appendU16(allocator, out, 4);
 500     try appendU16(allocator, out, 40);
 501     try appendU16(allocator, out, 0);
 502     try appendU16(allocator, out, 6);
 503     try appendU16(allocator, out, 4);
 504     try appendU16(allocator, out, 1);
 505     try appendU16(allocator, out, 2);
 506     try appendU16(allocator, out, 0x007e);
 507     try appendU16(allocator, out, 0x25cc);
 508     try appendU16(allocator, out, 0xffff);
 509     try appendU16(allocator, out, 0);
 510     try appendU16(allocator, out, 0x0020);
 511     try appendU16(allocator, out, 0x25cc);
 512     try appendU16(allocator, out, 0xffff);
 513     try appendU16(allocator, out, 0xffe1);
 514     try appendU16(allocator, out, 0xda94);
 515     try appendU16(allocator, out, 1);
 516     try appendU16(allocator, out, 0);
 517     try appendU16(allocator, out, 0);
 518     try appendU16(allocator, out, 0);
 519 }
 520 
 521 fn appendCmapFormat4Fallback(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 522     try appendU16(allocator, out, 4);
 523     try appendU16(allocator, out, 48);
 524     try appendU16(allocator, out, 0);
 525     try appendU16(allocator, out, 8);
 526     try appendU16(allocator, out, 8);
 527     try appendU16(allocator, out, 2);
 528     try appendU16(allocator, out, 0);
 529     for ([_]u16{ 0x007e, 0x03b2, 0x25cc, 0xffff }) |value| try appendU16(allocator, out, value);
 530     try appendU16(allocator, out, 0);
 531     for ([_]u16{ 0x0020, 0x03b2, 0x25cc, 0xffff }) |value| try appendU16(allocator, out, value);
 532     for ([_]u16{ 0xffe1, 0xfc71, 0xda94, 1 }) |value| try appendU16(allocator, out, value);
 533     for (0..4) |_| try appendU16(allocator, out, 0);
 534 }
 535 
 536 fn appendCmapFormat14VariationSequences(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 537     try appendU16(allocator, out, 14);
 538     try appendU32(allocator, out, 30);
 539     try appendU32(allocator, out, 1);
 540     try appendU24(allocator, out, 0x00fe0f);
 541     try appendU32(allocator, out, 0);
 542     try appendU32(allocator, out, 21);
 543     try appendU32(allocator, out, 1);
 544     try appendU24(allocator, out, 'A');
 545     try appendU16(allocator, out, 96);
 546 }
 547 
 548 fn appendHead(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 549     try out.appendNTimes(allocator, 0, 54);
 550     writeU32(out.items, 0, 0x00010000);
 551     writeU32(out.items, 12, 0x5f0f3cf5);
 552     writeU16(out.items, 18, 1000);
 553     writeI16(out.items, 36, 0);
 554     writeI16(out.items, 38, -200);
 555     writeI16(out.items, 40, 1000);
 556     writeI16(out.items, 42, 800);
 557 }
 558 
 559 fn appendHhea(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 560     try out.appendNTimes(allocator, 0, 36);
 561     writeU32(out.items, 0, 0x00010000);
 562     writeI16(out.items, 4, 800);
 563     writeI16(out.items, 6, -200);
 564     writeI16(out.items, 8, 0);
 565     writeU16(out.items, 10, 500);
 566     writeI16(out.items, 12, 0);
 567     writeI16(out.items, 14, 0);
 568     writeI16(out.items, 16, 500);
 569     writeI16(out.items, 18, 1);
 570     writeI16(out.items, 20, 0);
 571     writeI16(out.items, 22, 0);
 572     writeI16(out.items, 32, 0);
 573     writeU16(out.items, 34, 97);
 574 }
 575 
 576 fn appendOs2(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 577     try out.appendNTimes(allocator, 0, 78);
 578     writeU16(out.items, 0, 0);
 579     writeI16(out.items, 2, 500);
 580     writeU16(out.items, 4, 400);
 581     writeU16(out.items, 6, 5);
 582     writeU16(out.items, 62, 0x0080);
 583     writeU16(out.items, 64, 0x0020);
 584     writeU16(out.items, 66, 0x007e);
 585     writeI16(out.items, 68, 800);
 586     writeI16(out.items, 70, -200);
 587     writeI16(out.items, 72, 40);
 588     writeU16(out.items, 74, 800);
 589     writeU16(out.items, 76, 200);
 590 }
 591 
 592 fn appendHmtx(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), options: Options) !void {
 593     for (0..97) |glyph_id| {
 594         const zero_mark_to_base = (options.gpos_mark_to_base or options.gpos_mark_to_base_then_pair or options.gpos_above_base_mark or options.gpos_below_base_mark) and glyph_id == '^' - 31;
 595         const zero_mark_to_ligature = options.gpos_mark_to_ligature and glyph_id == '^' - 31;
 596         const zero_mark_to_mark = options.gpos_mark_to_mark and (glyph_id == '^' - 31 or glyph_id == '_' - 31);
 597         const advance: u16 = if (zero_mark_to_base or zero_mark_to_ligature or zero_mark_to_mark) 0 else 500;
 598         try appendU16(allocator, out, advance);
 599         try appendU16(allocator, out, 0);
 600     }
 601 }
 602 
 603 fn appendGlyf(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), offsets: *[98]u32, options: Options) !void {
 604     for (0..97) |glyph_id| {
 605         offsets[glyph_id] = @intCast(out.items.len);
 606         if (glyph_id == 0 and options.notdef_outline) {
 607             try appendRectangleGlyph(allocator, out);
 608         } else {
 609             switch (glyph_id + 31) {
 610                 'A', 'B', '?', '`' => try appendRectangleGlyph(allocator, out),
 611                 'i' => try appendCompositeGlyph(allocator, out),
 612                 else => {},
 613             }
 614         }
 615         if (out.items.len % 2 != 0) try out.append(allocator, 0);
 616     }
 617     offsets[97] = @intCast(out.items.len);
 618 }
 619 
 620 fn appendCff(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 621     const name = "TinyFixtureCFF";
 622     const name_index_len = 2 + 1 + 2 + name.len;
 623     const top_dict_len = 6;
 624     const top_index_len = 2 + 1 + 8 + top_dict_len;
 625     const charstrings_offset: u32 = @intCast(4 + name_index_len + top_index_len + 2 + 2);
 626 
 627     try out.appendSlice(allocator, &.{ 1, 0, 4, 4 });
 628     try appendCffNameIndex(allocator, out, name);
 629 
 630     var top = std.ArrayListUnmanaged(u8).empty;
 631     defer top.deinit(allocator);
 632     try appendDictInt(allocator, &top, @intCast(charstrings_offset));
 633     try top.append(allocator, 17);
 634     try appendCffSingleIndex(allocator, out, top.items);
 635 
 636     try appendU16(allocator, out, 0);
 637     try appendU16(allocator, out, 0);
 638     try appendCffCharStringsIndex(allocator, out);
 639 }
 640 
 641 fn appendCffNameIndex(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), name: []const u8) !void {
 642     try appendU16(allocator, out, 1);
 643     try out.append(allocator, 1);
 644     try out.append(allocator, 1);
 645     try out.append(allocator, @intCast(name.len + 1));
 646     try out.appendSlice(allocator, name);
 647 }
 648 
 649 fn appendCffSingleIndex(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), data: []const u8) !void {
 650     try appendU16(allocator, out, 1);
 651     try out.append(allocator, 4);
 652     try appendU32(allocator, out, 1);
 653     try appendU32(allocator, out, @intCast(data.len + 1));
 654     try out.appendSlice(allocator, data);
 655 }
 656 
 657 fn appendCffCharStringsIndex(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 658     var payload = std.ArrayListUnmanaged(u8).empty;
 659     defer payload.deinit(allocator);
 660     var offsets = @as([98]u32, @splat(0));
 661     for (0..97) |glyph_id| {
 662         offsets[glyph_id] = @intCast(payload.items.len + 1);
 663         switch (glyph_id + 31) {
 664             'A', 'B', '?' => try appendCffRectangleCharString(allocator, &payload),
 665             else => try payload.append(allocator, 14),
 666         }
 667     }
 668     offsets[97] = @intCast(payload.items.len + 1);
 669 
 670     try appendU16(allocator, out, 97);
 671     try out.append(allocator, 4);
 672     for (offsets) |offset| {
 673         try appendU32(allocator, out, offset);
 674     }
 675     try out.appendSlice(allocator, payload.items);
 676 }
 677 
 678 fn appendCffRectangleCharString(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 679     try appendType2Int(allocator, out, 50);
 680     try appendType2Int(allocator, out, 0);
 681     try out.append(allocator, 21);
 682     try appendType2Int(allocator, out, 400);
 683     try appendType2Int(allocator, out, 0);
 684     try appendType2Int(allocator, out, 0);
 685     try appendType2Int(allocator, out, 700);
 686     try appendType2Int(allocator, out, -400);
 687     try appendType2Int(allocator, out, 0);
 688     try out.append(allocator, 5);
 689     try out.append(allocator, 14);
 690 }
 691 
 692 fn appendDictInt(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), value: i32) !void {
 693     try out.append(allocator, 29);
 694     try appendU32(allocator, out, @bitCast(value));
 695 }
 696 
 697 fn appendType2Int(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), value: i16) !void {
 698     try out.append(allocator, 28);
 699     try appendI16(allocator, out, value);
 700 }
 701 
 702 fn appendLoca(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), offsets: *const [98]u32) !void {
 703     for (offsets) |offset| {
 704         try appendU16(allocator, out, @intCast(offset / 2));
 705     }
 706 }
 707 
 708 fn appendRectangleGlyph(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 709     try appendI16(allocator, out, 1);
 710     try appendI16(allocator, out, 50);
 711     try appendI16(allocator, out, 0);
 712     try appendI16(allocator, out, 450);
 713     try appendI16(allocator, out, 700);
 714     try appendU16(allocator, out, 3);
 715     try appendU16(allocator, out, 0);
 716     try out.appendNTimes(allocator, 1, 4);
 717     try appendI16(allocator, out, 50);
 718     try appendI16(allocator, out, 400);
 719     try appendI16(allocator, out, 0);
 720     try appendI16(allocator, out, -400);
 721     try appendI16(allocator, out, 0);
 722     try appendI16(allocator, out, 0);
 723     try appendI16(allocator, out, 700);
 724     try appendI16(allocator, out, 0);
 725 }
 726 
 727 fn appendCompositeGlyph(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 728     try appendI16(allocator, out, -1);
 729     try appendI16(allocator, out, 50);
 730     try appendI16(allocator, out, 0);
 731     try appendI16(allocator, out, 950);
 732     try appendI16(allocator, out, 700);
 733     try appendU16(allocator, out, 0x0023);
 734     try appendU16(allocator, out, 'A' - 31);
 735     try appendI16(allocator, out, 0);
 736     try appendI16(allocator, out, 0);
 737     try appendU16(allocator, out, 0x0003);
 738     try appendU16(allocator, out, 'B' - 31);
 739     try appendI16(allocator, out, 500);
 740     try appendI16(allocator, out, 0);
 741 }
 742 
 743 fn appendVhea(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 744     try out.appendNTimes(allocator, 0, 36);
 745     writeU32(out.items, 0, 0x00011000);
 746     writeI16(out.items, 4, 500);
 747     writeI16(out.items, 6, -500);
 748     writeI16(out.items, 8, 0);
 749     writeU16(out.items, 10, 900);
 750     writeI16(out.items, 12, 0);
 751     writeI16(out.items, 14, 0);
 752     writeI16(out.items, 16, 900);
 753     writeI16(out.items, 18, 0);
 754     writeI16(out.items, 20, 1);
 755     writeI16(out.items, 22, 0);
 756     writeI16(out.items, 32, 0);
 757     writeU16(out.items, 34, 97);
 758 }
 759 
 760 fn appendVmtx(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 761     for (0..97) |glyph_id| {
 762         const advance: u16 = if (glyph_id == 'B' - 31) 900 else 700;
 763         try appendU16(allocator, out, advance);
 764         try appendU16(allocator, out, 0);
 765     }
 766 }
 767 
 768 fn appendVorg(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 769     try appendU16(allocator, out, 1);
 770     try appendU16(allocator, out, 0);
 771     try appendI16(allocator, out, 880);
 772     try appendU16(allocator, out, 1);
 773     try appendU16(allocator, out, 'A' - 31);
 774     try appendI16(allocator, out, 760);
 775 }
 776 
 777 fn appendMaxp(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 778     try appendU32(allocator, out, 0x00010000);
 779     try appendU16(allocator, out, 97);
 780 }
 781 
 782 fn appendName(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 783     const family = "Tiny Fixture";
 784     const full = "Tiny Fixture Regular";
 785     const record_count: u16 = 2;
 786     const storage_offset: u16 = 6 + record_count * 12;
 787     try appendU16(allocator, out, 0);
 788     try appendU16(allocator, out, record_count);
 789     try appendU16(allocator, out, storage_offset);
 790     try appendNameRecord(allocator, out, 1, 0, family.len * 2);
 791     try appendNameRecord(allocator, out, 4, family.len * 2, full.len * 2);
 792     try appendUtf16BeAscii(allocator, out, family);
 793     try appendUtf16BeAscii(allocator, out, full);
 794 }
 795 
 796 fn appendNameRecord(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), name_id: u16, offset: usize, len: usize) !void {
 797     try appendU16(allocator, out, 3);
 798     try appendU16(allocator, out, 1);
 799     try appendU16(allocator, out, 0x0409);
 800     try appendU16(allocator, out, name_id);
 801     try appendU16(allocator, out, @intCast(len));
 802     try appendU16(allocator, out, @intCast(offset));
 803 }
 804 
 805 fn appendUtf16BeAscii(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), text: []const u8) !void {
 806     for (text) |byte| {
 807         try appendU16(allocator, out, byte);
 808     }
 809 }
 810 
 811 fn appendFvar(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 812     try appendU16(allocator, out, 1);
 813     try appendU16(allocator, out, 0);
 814     try appendU16(allocator, out, 16);
 815     try appendU16(allocator, out, 2);
 816     try appendU16(allocator, out, 2);
 817     try appendU16(allocator, out, 20);
 818     try appendU16(allocator, out, 0);
 819     try appendU16(allocator, out, 12);
 820     try appendTag(allocator, out, "wght");
 821     try appendFixed16(allocator, out, 100);
 822     try appendFixed16(allocator, out, 400);
 823     try appendFixed16(allocator, out, 900);
 824     try appendU16(allocator, out, 0);
 825     try appendU16(allocator, out, 256);
 826     try appendTag(allocator, out, "wdth");
 827     try appendFixed16(allocator, out, 50);
 828     try appendFixed16(allocator, out, 100);
 829     try appendFixed16(allocator, out, 200);
 830     try appendU16(allocator, out, 1);
 831     try appendU16(allocator, out, 257);
 832 }
 833 
 834 fn appendAvar(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 835     try appendU16(allocator, out, 1);
 836     try appendU16(allocator, out, 0);
 837     try appendU16(allocator, out, 0);
 838     try appendU16(allocator, out, 2);
 839     try appendU16(allocator, out, 5);
 840     try appendF2Dot14(allocator, out, -16384);
 841     try appendF2Dot14(allocator, out, -16384);
 842     try appendF2Dot14(allocator, out, -8192);
 843     try appendF2Dot14(allocator, out, -4096);
 844     try appendF2Dot14(allocator, out, 0);
 845     try appendF2Dot14(allocator, out, 0);
 846     try appendF2Dot14(allocator, out, 8192);
 847     try appendF2Dot14(allocator, out, 12288);
 848     try appendF2Dot14(allocator, out, 16384);
 849     try appendF2Dot14(allocator, out, 16384);
 850     try appendU16(allocator, out, 0);
 851 }
 852 
 853 fn appendHvar(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 854     try appendU16(allocator, out, 1);
 855     try appendU16(allocator, out, 0);
 856     try appendU32(allocator, out, 20);
 857     try appendU32(allocator, out, 0);
 858     try appendU32(allocator, out, 0);
 859     try appendU32(allocator, out, 0);
 860 
 861     try appendU16(allocator, out, 1);
 862     try appendU32(allocator, out, 12);
 863     try appendU16(allocator, out, 1);
 864     try appendU32(allocator, out, 28);
 865 
 866     try appendU16(allocator, out, 2);
 867     try appendU16(allocator, out, 1);
 868     try appendF2Dot14(allocator, out, 0);
 869     try appendF2Dot14(allocator, out, 16384);
 870     try appendF2Dot14(allocator, out, 16384);
 871     try appendF2Dot14(allocator, out, 0);
 872     try appendF2Dot14(allocator, out, 0);
 873     try appendF2Dot14(allocator, out, 0);
 874 
 875     try appendU16(allocator, out, 97);
 876     try appendU16(allocator, out, 1);
 877     try appendU16(allocator, out, 1);
 878     try appendU16(allocator, out, 0);
 879     for (0..97) |glyph_id| {
 880         const delta: i16 = if (glyph_id == 'A' - 31) 100 else 0;
 881         try appendI16(allocator, out, delta);
 882     }
 883 }
 884 
 885 fn appendVvar(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 886     try appendU16(allocator, out, 1);
 887     try appendU16(allocator, out, 0);
 888     try appendU32(allocator, out, 24);
 889     try appendU32(allocator, out, 0);
 890     try appendU32(allocator, out, 0);
 891     try appendU32(allocator, out, 0);
 892     try appendU32(allocator, out, 0);
 893 
 894     try appendU16(allocator, out, 1);
 895     try appendU32(allocator, out, 12);
 896     try appendU16(allocator, out, 1);
 897     try appendU32(allocator, out, 28);
 898 
 899     try appendU16(allocator, out, 2);
 900     try appendU16(allocator, out, 1);
 901     try appendF2Dot14(allocator, out, 0);
 902     try appendF2Dot14(allocator, out, 16384);
 903     try appendF2Dot14(allocator, out, 16384);
 904     try appendF2Dot14(allocator, out, 0);
 905     try appendF2Dot14(allocator, out, 0);
 906     try appendF2Dot14(allocator, out, 0);
 907 
 908     try appendU16(allocator, out, 97);
 909     try appendU16(allocator, out, 1);
 910     try appendU16(allocator, out, 1);
 911     try appendU16(allocator, out, 0);
 912     for (0..97) |glyph_id| {
 913         const delta: i16 = if (glyph_id == 'A' - 31) 100 else 0;
 914         try appendI16(allocator, out, delta);
 915     }
 916 }
 917 
 918 fn appendMvar(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 919     try appendU16(allocator, out, 1);
 920     try appendU16(allocator, out, 0);
 921     try appendU16(allocator, out, 0);
 922     try appendU16(allocator, out, 8);
 923     try appendU16(allocator, out, 3);
 924     try appendU16(allocator, out, 36);
 925     try appendTag(allocator, out, "hasc");
 926     try appendU16(allocator, out, 0);
 927     try appendU16(allocator, out, 0);
 928     try appendTag(allocator, out, "hdsc");
 929     try appendU16(allocator, out, 0);
 930     try appendU16(allocator, out, 1);
 931     try appendTag(allocator, out, "hlgp");
 932     try appendU16(allocator, out, 0);
 933     try appendU16(allocator, out, 2);
 934 
 935     try appendU16(allocator, out, 1);
 936     try appendU32(allocator, out, 12);
 937     try appendU16(allocator, out, 1);
 938     try appendU32(allocator, out, 28);
 939 
 940     try appendU16(allocator, out, 2);
 941     try appendU16(allocator, out, 1);
 942     try appendF2Dot14(allocator, out, 0);
 943     try appendF2Dot14(allocator, out, 16384);
 944     try appendF2Dot14(allocator, out, 16384);
 945     try appendF2Dot14(allocator, out, 0);
 946     try appendF2Dot14(allocator, out, 0);
 947     try appendF2Dot14(allocator, out, 0);
 948 
 949     try appendU16(allocator, out, 3);
 950     try appendU16(allocator, out, 1);
 951     try appendU16(allocator, out, 1);
 952     try appendU16(allocator, out, 0);
 953     try appendI16(allocator, out, 120);
 954     try appendI16(allocator, out, -80);
 955     try appendI16(allocator, out, 20);
 956 }
 957 
 958 fn appendMathAssembly(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
 959     const delimiter_glyph = '(' - 31;
 960     const radical_glyph = 'R' - 31;
 961     const base_glyph = 'A' - 31;
 962     const arrow_glyph = base_glyph;
 963     const grave_glyph = '`' - 31;
 964     const constants_offset = 10;
 965     const glyph_info_offset = constants_offset + 214;
 966     const variants_offset = glyph_info_offset + 28;
 967     const script_percent_scale_down_offset = 0;
 968     const script_script_percent_scale_down_offset = 2;
 969     const delimited_sub_formula_min_height_offset = 4;
 970     const display_operator_min_height_offset = 6;
 971     const axis_height = 1;
 972     const subscript_shift_down = 4;
 973     const subscript_top_max = 5;
 974     const subscript_baseline_drop_min = 6;
 975     const superscript_shift_up = 7;
 976     const superscript_bottom_min = 9;
 977     const superscript_baseline_drop_max = 10;
 978     const sub_superscript_gap_min = 11;
 979     const superscript_bottom_max_with_subscript = 12;
 980     const space_after_script = 13;
 981     const upper_limit_gap_min = 14;
 982     const upper_limit_baseline_rise_min = 15;
 983     const lower_limit_gap_min = 16;
 984     const lower_limit_baseline_drop_min = 17;
 985     const stack_top_shift_up = 18;
 986     const stack_top_display_style_shift_up = 19;
 987     const stack_bottom_shift_down = 20;
 988     const stack_bottom_display_style_shift_down = 21;
 989     const stack_gap_min = 22;
 990     const stack_display_style_gap_min = 23;
 991     const stretch_stack_top_shift_up = 24;
 992     const stretch_stack_bottom_shift_down = 25;
 993     const stretch_stack_gap_above_min = 26;
 994     const stretch_stack_gap_below_min = 27;
 995     const fraction_numerator_shift_up = 28;
 996     const fraction_numerator_display_style_shift_up = 29;
 997     const fraction_denominator_shift_down = 30;
 998     const fraction_denominator_display_style_shift_down = 31;
 999     const fraction_numerator_gap_min = 32;
1000     const fraction_num_display_style_gap_min = 33;
1001     const fraction_rule_thickness = 34;
1002     const fraction_denominator_gap_min = 35;
1003     const fraction_denom_display_style_gap_min = 36;
1004     const overbar_vertical_gap = 39;
1005     const overbar_rule_thickness = 40;
1006     const overbar_extra_ascender = 41;
1007     const underbar_vertical_gap = 42;
1008     const underbar_rule_thickness = 43;
1009     const underbar_extra_descender = 44;
1010     const radical_display_style_vertical_gap = 46;
1011     const radical_rule_thickness = 47;
1012     const radical_extra_ascender = 48;
1013     const radical_kern_before_degree = 49;
1014     const radical_kern_after_degree = 50;
1015     const radical_degree_bottom_raise_percent_offset = 212;
1016 
1017     try appendU16(allocator, out, 1);
1018     try appendU16(allocator, out, 0);
1019     try appendU16(allocator, out, constants_offset);
1020     try appendU16(allocator, out, glyph_info_offset);
1021     try appendU16(allocator, out, variants_offset);
1022     try out.appendNTimes(allocator, 0, 214);
1023     writeI16(out.items, constants_offset + script_percent_scale_down_offset, 85);
1024     writeI16(out.items, constants_offset + script_script_percent_scale_down_offset, 65);
1025     writeU16(out.items, constants_offset + delimited_sub_formula_min_height_offset, 2200);
1026     writeU16(out.items, constants_offset + display_operator_min_height_offset, 2200);
1027     writeMathValue(out.items, constants_offset, axis_height, 800);
1028     writeMathValue(out.items, constants_offset, subscript_shift_down, 600);
1029     writeMathValue(out.items, constants_offset, subscript_top_max, 1000);
1030     writeMathValue(out.items, constants_offset, subscript_baseline_drop_min, 1500);
1031     writeMathValue(out.items, constants_offset, superscript_shift_up, 900);
1032     writeMathValue(out.items, constants_offset, superscript_bottom_min, 350);
1033     writeMathValue(out.items, constants_offset, superscript_baseline_drop_max, 120);
1034     writeMathValue(out.items, constants_offset, sub_superscript_gap_min, 240);
1035     writeMathValue(out.items, constants_offset, superscript_bottom_max_with_subscript, 1100);
1036     writeMathValue(out.items, constants_offset, space_after_script, 80);
1037     writeMathValue(out.items, constants_offset, upper_limit_gap_min, 900);
1038     writeMathValue(out.items, constants_offset, upper_limit_baseline_rise_min, 2400);
1039     writeMathValue(out.items, constants_offset, lower_limit_gap_min, 850);
1040     writeMathValue(out.items, constants_offset, lower_limit_baseline_drop_min, 2300);
1041     writeMathValue(out.items, constants_offset, stack_top_shift_up, 1600);
1042     writeMathValue(out.items, constants_offset, stack_top_display_style_shift_up, 2400);
1043     writeMathValue(out.items, constants_offset, stack_bottom_shift_down, 1500);
1044     writeMathValue(out.items, constants_offset, stack_bottom_display_style_shift_down, 2300);
1045     writeMathValue(out.items, constants_offset, stack_gap_min, 500);
1046     writeMathValue(out.items, constants_offset, stack_display_style_gap_min, 1100);
1047     writeMathValue(out.items, constants_offset, stretch_stack_top_shift_up, 2600);
1048     writeMathValue(out.items, constants_offset, stretch_stack_bottom_shift_down, 2500);
1049     writeMathValue(out.items, constants_offset, stretch_stack_gap_above_min, 1000);
1050     writeMathValue(out.items, constants_offset, stretch_stack_gap_below_min, 950);
1051     writeMathValue(out.items, constants_offset, fraction_numerator_shift_up, 950);
1052     writeMathValue(out.items, constants_offset, fraction_numerator_display_style_shift_up, 1450);
1053     writeMathValue(out.items, constants_offset, fraction_denominator_shift_down, 900);
1054     writeMathValue(out.items, constants_offset, fraction_denominator_display_style_shift_down, 1400);
1055     writeMathValue(out.items, constants_offset, fraction_numerator_gap_min, 700);
1056     writeMathValue(out.items, constants_offset, fraction_num_display_style_gap_min, 760);
1057     writeMathValue(out.items, constants_offset, fraction_rule_thickness, 260);
1058     writeMathValue(out.items, constants_offset, fraction_denominator_gap_min, 680);
1059     writeMathValue(out.items, constants_offset, fraction_denom_display_style_gap_min, 720);
1060     writeMathValue(out.items, constants_offset, overbar_vertical_gap, 620);
1061     writeMathValue(out.items, constants_offset, overbar_rule_thickness, 260);
1062     writeMathValue(out.items, constants_offset, overbar_extra_ascender, 500);
1063     writeMathValue(out.items, constants_offset, underbar_vertical_gap, 580);
1064     writeMathValue(out.items, constants_offset, underbar_rule_thickness, 260);
1065     writeMathValue(out.items, constants_offset, underbar_extra_descender, 450);
1066     writeMathValue(out.items, constants_offset, radical_display_style_vertical_gap, 900);
1067     writeMathValue(out.items, constants_offset, radical_rule_thickness, 260);
1068     writeMathValue(out.items, constants_offset, radical_extra_ascender, 500);
1069     writeMathValue(out.items, constants_offset, radical_kern_before_degree, 400);
1070     writeMathValue(out.items, constants_offset, radical_kern_after_degree, 500);
1071     writeI16(out.items, constants_offset + radical_degree_bottom_raise_percent_offset, 85);
1072     try appendMathGlyphInfo(allocator, out, base_glyph, grave_glyph);
1073 
1074     try appendU16(allocator, out, 50);
1075     try appendU16(allocator, out, 16);
1076     try appendU16(allocator, out, 24);
1077     try appendU16(allocator, out, 2);
1078     try appendU16(allocator, out, 1);
1079     try appendU16(allocator, out, 30);
1080     try appendU16(allocator, out, 70);
1081     try appendU16(allocator, out, 110);
1082     try appendU16(allocator, out, 1);
1083     try appendU16(allocator, out, 2);
1084     try appendU16(allocator, out, delimiter_glyph);
1085     try appendU16(allocator, out, radical_glyph);
1086     try appendU16(allocator, out, 1);
1087     try appendU16(allocator, out, 1);
1088     try appendU16(allocator, out, arrow_glyph);
1089     try appendMathVerticalConstruction(allocator, out);
1090     try appendMathVerticalConstruction(allocator, out);
1091     try appendMathVerticalConstruction(allocator, out);
1092 }
1093 
1094 fn appendMathGlyphInfo(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), base_glyph: u16, accent_glyph: u16) !void {
1095     try appendU16(allocator, out, 0);
1096     try appendU16(allocator, out, 8);
1097     try appendU16(allocator, out, 0);
1098     try appendU16(allocator, out, 0);
1099     try appendU16(allocator, out, 12);
1100     try appendU16(allocator, out, 2);
1101     try appendI16(allocator, out, 420);
1102     try appendU16(allocator, out, 0);
1103     try appendI16(allocator, out, 80);
1104     try appendU16(allocator, out, 0);
1105     try appendU16(allocator, out, 1);
1106     try appendU16(allocator, out, 2);
1107     try appendU16(allocator, out, base_glyph);
1108     try appendU16(allocator, out, accent_glyph);
1109 }
1110 
1111 fn appendMathVerticalConstruction(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
1112     try appendU16(allocator, out, 4);
1113     try appendU16(allocator, out, 0);
1114     try appendI16(allocator, out, 0);
1115     try appendU16(allocator, out, 0);
1116     try appendU16(allocator, out, 3);
1117     try appendMathPart(allocator, out, 'A' - 31, 0, 200, 500, 0);
1118     try appendMathPart(allocator, out, '?' - 31, 200, 200, 300, 1);
1119     try appendMathPart(allocator, out, 'B' - 31, 200, 0, 500, 0);
1120 }
1121 
1122 fn appendMathPart(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), glyph_id: u16, start_connector_length: u16, end_connector_length: u16, full_advance: u16, flags: u16) !void {
1123     try appendU16(allocator, out, glyph_id);
1124     try appendU16(allocator, out, start_connector_length);
1125     try appendU16(allocator, out, end_connector_length);
1126     try appendU16(allocator, out, full_advance);
1127     try appendU16(allocator, out, flags);
1128 }
1129 
1130 fn writeMathValue(bytes: []u8, constants_offset: usize, field_index: usize, value: i16) void {
1131     const offset = constants_offset + 8 + field_index * 4;
1132     writeI16(bytes, offset, value);
1133 }
1134 
1135 fn appendFixed16(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), value: i32) !void {
1136     try appendU32(allocator, out, @bitCast(value * 65536));
1137 }
1138 
1139 fn appendF2Dot14(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), value: i16) !void {
1140     try appendI16(allocator, out, value);
1141 }
1142 
1143 fn appendKern(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
1144     try appendU16(allocator, out, 0);
1145     try appendU16(allocator, out, 1);
1146     try appendU16(allocator, out, 0);
1147     try appendU16(allocator, out, 20);
1148     try appendU16(allocator, out, 1);
1149     try appendU16(allocator, out, 1);
1150     try appendU16(allocator, out, 6);
1151     try appendU16(allocator, out, 0);
1152     try appendU16(allocator, out, 0);
1153     try appendU16(allocator, out, 'A' - 31);
1154     try appendU16(allocator, out, 'V' - 31);
1155     try appendI16(allocator, out, -80);
1156 }
1157 
1158 fn appendGdef(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8), include_carets: bool, underscore_is_mark: bool, include_variations: bool) !void {
1159     const glyph_class_offset: u16 = if (include_variations) 18 else 12;
1160     const lig_caret_offset: u16 = if (!include_carets) 0 else if (include_variations) 46 else 40;
1161     const item_variation_store_offset: u32 = if (include_carets) 66 else 46;
1162 
1163     try appendU16(allocator, out, 1);
1164     try appendU16(allocator, out, if (include_variations) 3 else 0);
1165     try appendU16(allocator, out, glyph_class_offset);
1166     try appendU16(allocator, out, 0);
1167     try appendU16(allocator, out, lig_caret_offset);
1168     try appendU16(allocator, out, 0);
1169     if (include_variations) {
1170         try appendU16(allocator, out, 0);
1171         try appendU32(allocator, out, item_variation_store_offset);
1172     }
1173 
1174     try appendU16(allocator, out, 2);
1175     try appendU16(allocator, out, 4);
1176     try appendU16(allocator, out, 'A' - 31);
1177     try appendU16(allocator, out, 'Z' - 31);
1178     try appendU16(allocator, out, 1);
1179     try appendU16(allocator, out, '^' - 31);
1180     try appendU16(allocator, out, '^' - 31);
1181     try appendU16(allocator, out, 3);
1182     try appendU16(allocator, out, '_' - 31);
1183     try appendU16(allocator, out, '_' - 31);
1184     try appendU16(allocator, out, if (underscore_is_mark) 3 else 4);
1185     try appendU16(allocator, out, 96);
1186     try appendU16(allocator, out, 96);
1187     try appendU16(allocator, out, 2);
1188 
1189     if (include_carets) {
1190         try appendU16(allocator, out, 14);
1191         try appendU16(allocator, out, 1);
1192         try appendU16(allocator, out, 6);
1193 
1194         try appendU16(allocator, out, 1);
1195         try appendU16(allocator, out, 4);
1196 
1197         try appendU16(allocator, out, 1);
1198         try appendI16(allocator, out, 250);
1199 
1200         try appendU16(allocator, out, 1);
1201         try appendU16(allocator, out, 1);
1202         try appendU16(allocator, out, 96);
1203     }
1204 
1205     if (include_variations) {
1206         try appendGdefItemVariationStore(allocator, out);
1207     }
1208 }
1209 
1210 fn appendGdefItemVariationStore(allocator: std.mem.Allocator, out: *std.ArrayListUnmanaged(u8)) !void {
1211     try appendU16(allocator, out, 1);
1212     try appendU32(allocator, out, 12);
1213     try appendU16(allocator, out, 1);
1214     try appendU32(allocator, out, 28);
1215 
1216     try appendU16(allocator, out, 2);
1217     try appendU16(allocator, out, 1);
1218     try appendF2Dot14(allocator, out, 0);
1219     try appendF2Dot14(allocator, out, 16384);
1220     try appendF2Dot14(allocator, out, 16384);
1221     try appendF2Dot14(allocator, out, 0);
1222     try appendF2Dot14(allocator, out, 0);
1223     try appendF2Dot14(allocator, out, 0);
1224 
1225     try appendU16(allocator, out, 1);
1226     try appendU16(allocator, out, 1);
1227     try appendU16(allocator, out, 1);
1228     try appendU16(allocator, out, 0);
1229     try appendI16(allocator, out, 80);
1230 }
1231 
1232 test "create builds an sfnt header" {
1233     const bytes = try create(std.testing.allocator);
1234     defer std.testing.allocator.free(bytes);
1235     try std.testing.expect(bytes.len > 12);
1236     try std.testing.expectEqual(@as(u8, 0), bytes[0]);
1237     try std.testing.expectEqual(@as(u8, 1), bytes[1]);
1238 }