108 lines
3.3 KiB
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;
|
|
};
|