diff --git a/zig/comptimeFont.zig b/zig/comptimeFont.zig new file mode 100644 index 0000000..19c9692 --- /dev/null +++ b/zig/comptimeFont.zig @@ -0,0 +1,106 @@ +// 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; +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; +};