Files
tyx_AI_xhs/tests/unit/postv1-font-normalization.test.ts
T
suyx a70b9fc241
Dada P0-A isolated Windows CI / validate-and-package (push) Waiting to run
feat(POSTV1-ASSET-ALL-16): 完整还原归档文字模板素材
2026-08-06 16:20:24 +08:00

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);
});
});