124 lines
4.6 KiB
TypeScript
124 lines
4.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { normalizeBrowserFontBytes } from "../../scripts/lib/text-template-assets.mjs";
|
|
|
|
function checksum(bytes: Buffer) {
|
|
let value = 0;
|
|
for (let index = 0; index < bytes.length; index += 4) {
|
|
let word = 0;
|
|
for (let byte = 0; byte < 4; byte += 1) word = (word << 8) | (bytes[index + byte] ?? 0);
|
|
value = (value + (word >>> 0)) >>> 0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function fontFixture(verticalVersion: number) {
|
|
const bytes = Buffer.alloc(80);
|
|
bytes.writeUInt32BE(0x00010000, 0);
|
|
bytes.writeUInt16BE(3, 4);
|
|
bytes.write("head", 12, "ascii");
|
|
bytes.writeUInt32BE(60, 20);
|
|
bytes.writeUInt32BE(12, 24);
|
|
bytes.write("post", 28, "ascii");
|
|
bytes.writeUInt32BE(72, 36);
|
|
bytes.writeUInt32BE(4, 40);
|
|
bytes.write("vhea", 44, "ascii");
|
|
bytes.writeUInt32BE(76, 52);
|
|
bytes.writeUInt32BE(4, 56);
|
|
bytes.writeUInt32BE(0x00030000, 72);
|
|
bytes.writeUInt32BE(verticalVersion, 76);
|
|
return bytes;
|
|
}
|
|
|
|
function validFontFixture(verticalVersion: number) {
|
|
const bytes = fontFixture(verticalVersion);
|
|
bytes.writeUInt32BE((0xB1B0AFBA - checksum(bytes)) >>> 0, 68);
|
|
return bytes;
|
|
}
|
|
|
|
function tableRecord(bytes: Buffer, wantedTag: string) {
|
|
const tableCount = bytes.readUInt16BE(4);
|
|
for (let index = 0; index < tableCount; index += 1) {
|
|
const recordOffset = 12 + index * 16;
|
|
const tag = bytes.toString("ascii", recordOffset, recordOffset + 4);
|
|
if (tag === wantedTag) {
|
|
return {
|
|
length: bytes.readUInt32BE(recordOffset + 12),
|
|
offset: bytes.readUInt32BE(recordOffset + 8),
|
|
};
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function invalidGlyphBoundsFixture() {
|
|
const bytes = Buffer.alloc(156);
|
|
bytes.writeUInt32BE(0x00010000, 0);
|
|
bytes.writeUInt16BE(4, 4);
|
|
const records = [
|
|
{ length: 10, offset: 76, tag: "glyf" },
|
|
{ length: 54, offset: 88, tag: "head" },
|
|
{ length: 4, offset: 144, tag: "loca" },
|
|
{ length: 6, offset: 148, tag: "maxp" },
|
|
];
|
|
records.forEach((record, index) => {
|
|
const directoryOffset = 12 + index * 16;
|
|
bytes.write(record.tag, directoryOffset, 4, "ascii");
|
|
bytes.writeUInt32BE(record.offset, directoryOffset + 8);
|
|
bytes.writeUInt32BE(record.length, directoryOffset + 12);
|
|
});
|
|
bytes.writeInt16BE(1, 76);
|
|
bytes.writeInt16BE(43, 78);
|
|
bytes.writeInt16BE(757, 80);
|
|
bytes.writeInt16BE(246, 82);
|
|
bytes.writeInt16BE(17, 84);
|
|
bytes.writeInt16BE(0, 138);
|
|
bytes.writeUInt16BE(0, 144);
|
|
bytes.writeUInt16BE(5, 146);
|
|
bytes.writeUInt32BE(0x00010000, 148);
|
|
bytes.writeUInt16BE(1, 152);
|
|
bytes.writeUInt32BE((0xB1B0AFBA - checksum(bytes)) >>> 0, 96);
|
|
return bytes;
|
|
}
|
|
|
|
describe("POSTV1-ASSET-ALL-16 browser font normalization", () => {
|
|
it("removes the captured malformed vertical metrics and recalculates SFNT checksums", () => {
|
|
const original = fontFixture(0x00010001);
|
|
const normalized = normalizeBrowserFontBytes(original);
|
|
expect(normalized).toBeDefined();
|
|
expect(original.readUInt32BE(76)).toBe(0x00010001);
|
|
expect(tableRecord(normalized!, "vhea")).toBeUndefined();
|
|
expect(tableRecord(normalized!, "vmtx")).toBeUndefined();
|
|
expect(tableRecord(normalized!, "head")).toEqual({ length: 12, offset: 44 });
|
|
expect(tableRecord(normalized!, "post")).toEqual({ length: 32, offset: 56 });
|
|
expect(normalized!.readUInt32BE(56)).toBe(0x00030000);
|
|
expect(checksum(normalized!)).toBe(0xB1B0AFBA);
|
|
});
|
|
|
|
it("does not rewrite fonts whose vertical header is already valid", () => {
|
|
expect(normalizeBrowserFontBytes(validFontFixture(0x00010000))).toBeUndefined();
|
|
expect(normalizeBrowserFontBytes(validFontFixture(0x00011000))).toBeUndefined();
|
|
});
|
|
|
|
it("repairs a stale whole-font checksum without changing a valid vertical header", () => {
|
|
const normalized = normalizeBrowserFontBytes(fontFixture(0x00011000));
|
|
expect(normalized).toBeDefined();
|
|
const verticalHeader = tableRecord(normalized!, "vhea");
|
|
expect(verticalHeader).toBeDefined();
|
|
expect(normalized!.readUInt32BE(verticalHeader!.offset)).toBe(0x00011000);
|
|
expect(checksum(normalized!)).toBe(0xB1B0AFBA);
|
|
});
|
|
|
|
it("repairs reversed glyph bounds without changing the glyph outline payload", () => {
|
|
const normalized = normalizeBrowserFontBytes(invalidGlyphBoundsFixture());
|
|
expect(normalized).toBeDefined();
|
|
const glyphs = tableRecord(normalized!, "glyf");
|
|
expect(glyphs).toBeDefined();
|
|
expect(normalized!.readInt16BE(glyphs!.offset + 2)).toBe(43);
|
|
expect(normalized!.readInt16BE(glyphs!.offset + 4)).toBe(17);
|
|
expect(normalized!.readInt16BE(glyphs!.offset + 6)).toBe(246);
|
|
expect(normalized!.readInt16BE(glyphs!.offset + 8)).toBe(757);
|
|
expect(checksum(normalized!)).toBe(0xB1B0AFBA);
|
|
});
|
|
});
|