Files
cjk-autospace/test/measure-bias.test.mjs
T
admin 3a4fa1c932 fix: 修复行缘、切分还原与语言解析并同步文档
保留逐字 margin/jiya.bias 字体后备方案;补齐 demo 重排、量测边界说明及共享规则 1.0.1 反例向量。

验证:完整构建通过,Node 376 项、WKWebView 64 项及原生 demo 9 项通过;Firefox 仍暂缓。
2026-10-03 21:15:54 +08:00

96 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import { test } from "node:test";
import { fileURLToPath } from "node:url";
const python = "python3";
const available = spawnSync(python, [ "-c", "import fontTools" ]).status === 0;
const tool = fileURLToPath(new URL("../tools/measure-bias.py", import.meta.url));
const fixture = `
from fontTools.fontBuilder import FontBuilder
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
import sys
fb = FontBuilder(1000, isTTF=True)
names = ['.notdef', 'comma', 'commaHant', 'fullstop']
fb.setupGlyphOrder(names)
fb.setupCharacterMap({0xff0c: 'comma', 0xff0e: 'fullstop'})
glyphs = {}
for name, left, right in [('.notdef', 0, 0), ('comma', 50, 250),
('commaHant', 450, 550), ('fullstop', 650, 850)]:
p = TTGlyphPen(None)
if right > left:
p.moveTo((left, 0)); p.lineTo((right, 0)); p.lineTo((right, 150))
p.lineTo((left, 150)); p.closePath()
glyphs[name] = p.glyph()
fb.setupGlyf(glyphs)
fb.setupHorizontalMetrics({'.notdef': (1000, 0), 'comma': (1000, 50),
'commaHant': (1000, 450), 'fullstop': (1000, 650)})
fb.setupHorizontalHeader(ascent=800, descent=-200)
fb.setupNameTable({'familyName': 'BiasFixture', 'styleName': 'Regular',
'uniqueFontIdentifier': 'BiasFixture', 'fullName': 'BiasFixture',
'psName': 'BiasFixture'})
fb.setupOS2(sTypoAscender=800, sTypoDescender=-200, usWinAscent=800, usWinDescent=200)
fb.setupPost(); fb.setupMaxp()
features = 'languagesystem hani ZHT; feature locl { script hani; language ZHT; sub comma by commaHant; } locl;'
if sys.argv[2] == 'halt':
features += ' feature halt { sub fullstop by comma; } halt;'
addOpenTypeFeaturesFromString(fb.font, features)
fb.save(sys.argv[1])
`;
function run(args)
{
const result = spawnSync(python, args, { encoding: "utf8" });
assert.equal(result.status, 0, result.stderr || String(result.error));
return result.stdout;
}
function withFont(feature, check)
{
const dir = mkdtempSync(join(tmpdir(), "juzhen-bias-test-"));
try
{
const font = join(dir, "fixture.ttf");
run([ "-c", fixture, font, feature ]);
check(font);
}
finally { rmSync(dir, { recursive: true, force: true }); }
}
function table(output)
{
return JSON.parse(output.slice(output.indexOf("\n{") + 1));
}
test("measure-bias:全角句點入表,lang 僅選鍵且輸出說明 cmap/locl 限制",
{ skip: available ? false : "需工具本身的 Python fonttools 依賴" }, () =>
{
withFont("none", (font) =>
{
for (const lang of [ "zh-Hans", "zh-Hant" ])
{
const output = run([ tool, font, lang ]);
assert.equal(table(output)[lang]["."], "right");
assert.equal(table(output)[lang][","], "left", "仍量預設 cmap,未套 ZHT locl 替代字形");
assert.match(output, /未做 shaping.*未應用 locl/);
assert.match(output, /語言參數僅選 JSON 分類鍵/);
assert.match(output, /瀏覽器實際字形逐字微調/);
}
});
});
test("measure-bias:halt feature 存在只報存在,不承諾逐字覆蓋",
{ skip: available ? false : "需工具本身的 Python fonttools 依賴" }, () =>
{
withFont("halt", (font) =>
{
const output = run([ tool, font ]);
assert.match(output, /halt:有.*不保證涵蓋所有標點/);
assert.equal(table(output)["zh-Hans"]["."], "right", "feature 偵測不改預設輪廓分類");
});
});