使用矢量轮廓射线投射进行经验性字形相似度评分。在245种系统字体中渲染Unicode易混淆字符对,直接从字体轮廓(无光栅化)测量结构相似度,并生成带有每个字体连续距离分数的JSON工件。
来自5260万单字符和1.9亿多字符比较的关键结果,涉及22,581个字符和12种书写系统:
输出直接供给namespace-guard,用于包名、域名和标识符的运行时易混淆检测。
RaySpace以36个角度通过字体轮廓投射平行射线,并捕获每个字形的五层信息:交叉计数、交叉位置、交叉角度、ping距离(每次交叉处的笔画宽度)和ping最大值(交叉之间的反白宽度)。这为每个字符在每个字体中生成紧凑的签名。两个签名通过所有五层的加权L1距离进行比较。
三级过滤器级联使得穷举比较可行:
签名库(294,646+条目跨245种字体;使用--include-uppercase可更多)预先计算一次。然后发现过程在库上作为单线程算术进行,在31分钟内完成5260万对比较,无需工作线程或GPU。
npm install
# 1. 构建射线签名库(前提条件,约24分钟)
npx tsx scripts/build-signature-bank.ts
# 1b. 包含大写拉丁字母A-Z(可选)
# 默认库只包含IDNA PVALID码点(小写字母、数字、符号)。使用--include-uppercase添加大写A-Z,
# 对于字体识别和商标视觉比较(大写字形形状重要)很有用。构建器可恢复,所以只计算额外码点。
npx tsx scripts/build-signature-bank.ts --include-uppercase
# 1c. 包含任意码点范围(可选)
npx tsx scripts/build-signature-bank.ts --extra-range=0041-005A
# 2. 单字符发现(22,581个字符,12种文字,约36分钟)
npx tsx scripts/discover-singlechar-sdf.ts --scorer=ray
# 3. 多字符(双字母)发现(676个双字母组,约63分钟)
npx tsx scripts/discover-multichar-sdf.ts --scorer=ray
# 4. 对已知TR39多字符易混淆集评分(约5分钟)
npx tsx scripts/score-multichar-sdf.ts --scorer=ray
原始基于SSIM的流水线对跨越230种字体的2650万比较进行了评分。它仍然可用,但所有发现和评分任务已由RaySpace取代。
# TR39易混淆对评分
npx tsx scripts/build-index.ts # 渲染索引(约160秒,11,370张PNG)
npx tsx scripts/score-all-pairs.ts # 评分所有对(约65秒,23.5万次比较)
# 新型易混淆发现
npx tsx scripts/build-candidates.ts # 候选集(约2.3万个字符)
npx tsx scripts/build-index.ts --candidates # 渲染候选(约40分钟,8.9万张PNG)
npx tsx scripts/score-candidates.ts # 对照拉丁目标评分(约15分钟,290万次比较)
# 从两个流水线提取高分发现
npx tsx scripts/extract-discoveries.ts
"零距离字体数" = 轮廓产生比特完全相同的射线签名的字体(距离0.000)。拉丁w/西里尔ԝ在所有19种同时包含这两个字形的字体中完全相同。
格鲁吉亚文Coda(U+10FF)与拉丁o、西里尔o和希腊omicron形成一个四向易混淆环,所有距离均低于0.08。
oy/西里尔uk 发现是最突出的新发现:拉丁双字母"oy"在Helvetica中与西里尔合体字母uk(ѹ)视觉上完全一致(距离0.000),在Arial Unicode MS中为0.0005。
| 平均阈值 | 单字符唯一对 |
|---|
三个推荐操作级别:
查询特定字体存在哪些易混淆对。对于发布新字体的字体设计师、评估系统字体变更的浏览器厂商,或任何为安全敏感场合(如IDN域名)选择显示字体的人有用。
npx tsx scripts/query-font.ts --list-fonts # 发现数据中的218种字体
npx tsx scripts/query-font.ts "Arial" # Arial的所有对(SSIM >= 0.7)
npx tsx scripts/query-font.ts "Arial" --threshold 0.8 # 仅高置信度
npx tsx scripts/query-font.ts "Arial" --compare "Georgia" # 通过SSIM差值比较两种字体
npx tsx scripts/query-font.ts "Arial" --json # JSON格式供下游处理
字体名称匹配不区分大小写且为子串匹配,因此"arial"匹配Arial、Arial Black和Arial Unicode MS。比较模式按最大SSIM差异排序,首先显示切换字体时哪些对变得更好或更差。
需要评分流水线生成的发现文件(已被git忽略,需在本地重新生成)。
| 文件 | 描述 |
|---|---|
data/output/render-index/ | 渲染的PNG + 索引(SSIM流水线) |
data/output/singlechar-sdf-scores.jsonl |
confusable-weights-v2.json,包含每对分布记录:均值、p50、p90、字体数、零距离数、零距离占比以及推荐级别(严格/标准/探索)。RaySpace距离替代SSIM。标准阈值下有4,174对。confusableDistance({ weights })消费confusable-weights.json,进行基于测量的视觉风险评分关于此项目发现和方法论的文章发布在paultendo.github.io上:
RaySpace方法论和发现:
SSIM流水线发现:
涵盖推动此项目的更广泛问题空间的文章:
| 源 | 目标 | 文字 | 平均 | 字体数 | 零距离字体数 |
|---|
| w U+0077 | ԝ U+051D | 拉丁-西里尔 | 0.000 | 19 | 19 |
| j U+006A | ϳ U+03F3 | 拉丁-希腊 | 0.013 | 21 | 18 |
| i U+0069 | і U+0456 | 拉丁-西里尔 | 0.018 | 62 | 50 |
| s U+0073 | ѕ U+0455 | 拉丁-西里尔 | 0.018 | 62 | 46 |
| c U+0063 | с U+0441 | 拉丁-西里尔 | 0.019 | 61 | 45 |
| o U+006F | о U+043E | 拉丁-西里尔 | 0.020 | 61 | 44 |
| j U+006A | ј U+0458 | 拉丁-西里尔 | 0.021 | 60 | 48 |
| x U+0078 | х U+0445 | 拉丁-西里尔 | 0.023 | 59 | 50 |
| p U+0070 | р U+0440 | 拉丁-西里尔 | 0.024 | 61 | 46 |
| e U+0065 | е U+0435 | 拉丁-西里尔 | 0.032 | 61 | 44 |
| a U+0061 | а U+0430 | 拉丁-西里尔 | 0.042 | 61 | 45 |
| 源 | 目标 | 文字 | 平均 | 字体数 |
|---|
| ο U+03BF | ჿ U+10FF | 希腊-格鲁吉亚 | 0.057 | 2 |
| ヘ U+30D8 | へ U+3078 | 片假名-平假名 | 0.122 | 11 |
| 丶 U+4E36 | ヽ U+30FD | 汉字-片假名 | 0.125 | 9 |
| 二 U+4E8C | ニ U+30CB | 汉字-片假名 | 0.249 | 11 |
| 口 U+53E3 | ロ U+30ED | 汉字-片假名 | 0.268 | 11 |
| 双字母 | 目标 | 平均 | 字体数 | 备注 |
|---|
| ll | ॥ U+0965(天城文双段符) | 0.176 | 8 | 跨文字 |
| oy | ѹ U+0479(西里尔uk) | 0.322 | 16 | 新型跨文字双字母易混淆 |
| rn | m U+006D | 0.531 | 95 | 33种字体低于0.40 |
| bl | ы U+044B(西里尔yeru) | 0.797 | 49 | 跨文字 |
| 多字符唯一对 |
|---|
| < 0.50 | 138 | 13 |
| < 1.00 | 4,174 | 1,631 |
| < 1.50 | 59,700 | (噪声) |
| < 2.00 | 249,976 | 2,524,275 |
| 文件 | 描述 |
|---|
data/output/confusable-discoveries.json | 110个TR39对,高SSIM(>= 0.7)或像素完全一致 |
data/output/candidate-discoveries.json | 793个不在TR39中的新对,平均SSIM >= 0.7 |
data/output/confusable-weights.json | 1,397个加权边,用于namespace-guard集成 |
data/output/cross-script-discoveries.json | 563个跨文字易混淆对 |
data/output/cross-script-summary.json | 按文字对分类的跨文字摘要 |
data/output/multichar-discoveries.json | 多字符易混淆发现 |
| 单字符RaySpace分数 |
data/output/multichar-rayspace-scores.jsonl | 多字符RaySpace分数 |
data/output/signature-bank/ | 射线签名库(294,646个条目,7.9GB压缩) |