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 }