misc/zig/comptimeFont.zig

108 lines
3.3 KiB
Zig

// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html
const std = @import("std");
const readInt = std.mem.readInt;
const assert = std.debug.assert;
// this expects a TrueType font with an interface like https://codeberg.org/andrewrk/TrueType/
const font = @import("main.zig").defaultFont;
fn sort(array: []const u16) []const u16 {
var tempArray: [1]u16 = undefined;
@setEvalBranchQuota(10000);
if(array.len == 1) {
tempArray[0] = array[0];
return &tempArray;
} else {
const indexofMin = std.mem.indexOfMin(u16, array);
tempArray[0] = array[indexofMin];
return &tempArray ++ sort(array[0..indexofMin] ++ array[indexofMin + 1..]);
}
}
const bytes = font.ttf_bytes;
const tableStart = font.index_map;
const format = std.mem.readInt(u16, bytes[tableStart..][0..2], .big);
// Apple: "The segCount is the number of contiguous code ranges in the font"
const seg_count = readInt(u16, bytes[tableStart + 6 ..][0..2], .big) >> 1;
const firstEndCode = tableStart + 14;
const lastEndCode = firstEndCode + (2 * seg_count);
const endCodes = eds: {
var codes: [seg_count]u16 = undefined;
// apple: "The segments are sorted in order of increasing endCode values."
for(0..seg_count) |i| {
codes[i] = readInt(u16, bytes[firstEndCode .. lastEndCode][(2 * i)..(2 * i) + 2], .big);
}
assert(codes[seg_count - 1] == 0xFFFF);
break :eds codes;
};
test "endCodes are sorted" {
var last = endCodes[0];
for(endCodes[1..]) |code| {
std.debug.assert(code > last);
last = code;
}
}
// 2 bytes reserved, then start codes
const reservedPad = readInt(u16, bytes[lastEndCode..lastEndCode + 2], .big);
const _ = blk: {
assert(reservedPad == 0);
break :blk;
};
const firstStartCode = lastEndCode + 4;
const startCodes = sts: {
var codes: [seg_count]u16 = undefined;
codes[0] = 0;
for(1..seg_count) |i| {
codes[i] = readInt(u16, bytes[firstStartCode .. firstStartCode + (2 * seg_count)][(2 * i)..(2 * i) + 2], .big);
}
const codesSorted = sort(&codes);
break :sts codesSorted;
};
test "startCodes are sorted" {
var last = startCodes[0];
for(startCodes[1..]) |code| {
std.debug.assert(code >= last);
last = code;
}
}
test "codepoint array lengths match" {
std.debug.assert(format == 4);
std.debug.assert(endCodes.len == startCodes.len);
@compileLog("endCodes len", endCodes.len);
@compileLog("startCodes len", startCodes.len);
const altEndCodes = std.mem.bytesAsSlice(u16, bytes[firstEndCode .. lastEndCode]);
std.debug.assert(!std.mem.eql(u16, &endCodes, @alignCast(@constCast(altEndCodes))));
@compileLog("altEndCodes (bytesAsSlice method) len", altEndCodes.len);
}
pub const codePoints = cps: {
const totalCodeCount = cnt: {
var count = 0;
for(startCodes, endCodes) |start, end| {
count += (end - start);
}
break :cnt count;
};
const allCodes = cds: {
var codes: [totalCodeCount]i32 = undefined;
var codesIdx = 0;
@setEvalBranchQuota(10000);
for(startCodes,endCodes) |start, end| {
for(start..end) |n| {
codes[codesIdx] = n;
codesIdx += 1;
}
}
break :cds codes;
};
break :cps allCodes;
};