
Unicode TR39に欠けている793の紛らわしいペア、世界初のクロススクリプトデータセット、230フォント・22,000以上の文字を対象としたフォント対応SSIMスコアリング
ベクトルアウトラインレイキャスティングを用いた経験的なグリフ類似度スコアリング。245のシステムフォントにわたってUnicodeの紛らわしい文字ペアをレンダリングし、フォントアウトラインから直接構造的類似性を測定(ラスタライズなし)、フォントごとの連続距離スコアを持つスコア付きJSONアーティファクトを生成します。
22,581文字、12の書記体系にわたる5260万の単一文字比較と1億9000万の複数文字比較からの主な結果:
出力は直接namespace-guardに供給され、パッケージ名、ドメイン名、識別子のランタイム紛らわしい検出に使用されます。
RaySpaceは36角度でフォントアウトラインを通して平行光線を投射し、グリフごとに5層の情報を取得します:交差カウント、交差位置、交差角度、ping距離(各交差でのストローク幅)、ping最大値(交差間のカウンター幅)。これにより、フォントごとの文字ごとにコンパクトなシグネチャが生成されます。2つのシグネチャは、5層すべてにわたる重み付きL1距離で比較されます。
3段階のフィルターカスケードにより、網羅的な比較が実用的になります:
シグネチャバンク(245フォントにわたる294,646以上のエントリ;--include-uppercaseでさらに増加)は一度事前計算されます。その後、発見はバンク上でシングルスレッド演算として実行され、ワーカースレッドやGPUなしで31分で5260万のペア比較を完了します。
npm install
# 1. Build the ray signature bank (prerequisite, ~24 min)
npx tsx scripts/build-signature-bank.ts
# 1b. Include uppercase Latin A-Z (optional)
# By default the bank only includes IDNA PVALID codepoints (lowercase,
# digits, symbols). Use --include-uppercase to add uppercase A-Z, useful
# for font identification and trademark visual comparison where uppercase
# glyph shapes matter. The builder is resumable, so this only computes
# the additional codepoints.
npx tsx scripts/build-signature-bank.ts --include-uppercase
# 1c. Include arbitrary codepoint ranges (optional)
npx tsx scripts/build-signature-bank.ts --extra-range=0041-005A
# 2. Single-char discovery (22,581 chars, 12 scripts, ~36 min)
npx tsx scripts/discover-singlechar-sdf.ts --scorer=ray
# 3. Multi-char (bigram) discovery (676 bigrams, ~63 min)
npx tsx scripts/discover-multichar-sdf.ts --scorer=ray
# 4. Score known TR39 multi-char confusables (~5 min)
npx tsx scripts/score-multichar-sdf.ts --scorer=ray
元のSSIMベースのパイプラインは230フォントにわたって2650万の比較をスコアリングしました。現在も機能しますが、すべての発見およびスコアリングタスクでRaySpaceに置き換えられました。
# TR39 confusable pair scoring
npx tsx scripts/build-index.ts # Render index (~160s, 11,370 PNGs)
npx tsx scripts/score-all-pairs.ts # Score all pairs (~65s, 235K comparisons)
# Novel confusable discovery
npx tsx scripts/build-candidates.ts # Candidate set (~23K chars)
npx tsx scripts/build-index.ts --candidates # Render candidates (~40min, 89K PNGs)
npx tsx scripts/score-candidates.ts # Score against Latin targets (~15min, 2.9M comparisons)
# Extract high-scoring discoveries from both pipelines
npx tsx scripts/extract-discoveries.ts
"ゼロ" = アウトラインがビット同一のレイシグネチャ(距離0.000)を生成するフォント。ラテン文字wとキリル文字ԝは、両方のグリフを含む19フォントすべてで同一です。
ジョージア文字コーダ(U+10FF)はラテン文字o、キリル文字o、ギリシャ文字オミクロンと、すべて距離0.08未満で4方向の紛らわしいリングを形成します。
oy/キリル文字ukの発見は際立った新規発見です。ラテンバイグラム"oy"はキリル文字の合字uk(ѹ)と、Helveticaで距離0.000、Arial Unicode MSで0.0005で視覚的に同一です。
| 平均閾値 | 単一文字ユニークペア |
|---|
3つの推奨動作レベル:
特定のフォントに存在する紛らわしいペアをクエリします。新しいタイプフェイスを出荷するフォントデザイナー、システムフォントの変更を評価するブラウザベンダー、またはIDNドメインのようなセキュリティに敏感なコンテキストで表示フォントを選択する人に役立ちます。
npx tsx scripts/query-font.ts --list-fonts # 218 fonts in discovery data
npx tsx scripts/query-font.ts "Arial" # All pairs for Arial (SSIM >= 0.7)
npx tsx scripts/query-font.ts "Arial" --threshold 0.8 # High-confidence only
npx tsx scripts/query-font.ts "Arial" --compare "Georgia" # Diff two fonts by SSIM delta
npx tsx scripts/query-font.ts "Arial" --json # JSON for downstream processing
フォント名のマッチングは大文字小文字を区別しない部分文字列なので、"arial"はArial、Arial Black、Arial Unicode MSにマッチします。比較モードは最大のSSIM差で最初にソートし、フォントを切り替えたときにどのペアが良くなるか悪くなるかを正確に表示します。
スコアリングパイプラインからの発見ファイルが必要です(gitignoreされ、ローカルで再生成)。
| ファイル | 説明 |
|---|---|
data/output/render-index/ | レンダリングPNG + インデックス(SSIMパイプライン) |
data/output/singlechar-sdf-scores.jsonl |
confusable-weights-v2.jsonを生成。RaySpace距離がSSIMを置き換え。標準閾値で4,174ペア。confusableDistance({ weights })を介して測定された視覚的リスクスコアリングのためにconfusable-weights.jsonを消費します。paultendo.github.ioに掲載された、このプロジェクトの成果と方法論に関する記事:
RaySpaceの方法論と発見:
SSIMパイプラインの発見:
このプロジェクトの動機となったより広い問題領域をカバーする記事:
| 元 | 対象 | スクリプト | 平均 | フォント数 | ゼロ |
|---|
| w U+0077 | ԝ U+051D | Latin-Cyrillic | 0.000 | 19 | 19 |
| j U+006A | ϳ U+03F3 | Latin-Greek | 0.013 | 21 | 18 |
| i U+0069 | і U+0456 | Latin-Cyrillic | 0.018 | 62 | 50 |
| s U+0073 | ѕ U+0455 | Latin-Cyrillic | 0.018 | 62 | 46 |
| c U+0063 | с U+0441 | Latin-Cyrillic | 0.019 | 61 | 45 |
| o U+006F | о U+043E | Latin-Cyrillic | 0.020 | 61 | 44 |
| j U+006A | ј U+0458 | Latin-Cyrillic | 0.021 | 60 | 48 |
| x U+0078 | х U+0445 | Latin-Cyrillic | 0.023 | 59 | 50 |
| p U+0070 | р U+0440 | Latin-Cyrillic | 0.024 | 61 | 46 |
| e U+0065 | е U+0435 | Latin-Cyrillic | 0.032 | 61 | 44 |
| a U+0061 | а U+0430 | Latin-Cyrillic | 0.042 | 61 | 45 |
| 元 | 対象 | スクリプト | 平均 | フォント数 |
|---|
| ο U+03BF | ჿ U+10FF | Greek-Georgian | 0.057 | 2 |
| ヘ U+30D8 | へ U+3078 | Katakana-Hiragana | 0.122 | 11 |
| 丶 U+4E36 | ヽ U+30FD | Han-Katakana | 0.125 | 9 |
| 二 U+4E8C | ニ U+30CB | Han-Katakana | 0.249 | 11 |
| 口 U+53E3 | ロ U+30ED | Han-Katakana | 0.268 | 11 |
| バイグラム | 対象 | 平均 | フォント数 | 注 |
|---|
| ll | ॥ U+0965 (Devanagari double danda) | 0.176 | 8 | クロススクリプト |
| oy | ѹ U+0479 (Cyrillic uk) | 0.322 | 16 | 新規クロススクリプトバイグラム紛らわしい |
| rn | m U+006D | 0.531 | 95 | 33フォントが0.40未満 |
| bl | ы U+044B (Cyrillic 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 | 高いSSIM(>= 0.7)またはピクセル同一の110のTR39ペア |
data/output/candidate-discoveries.json | TR39に含まれない793の新規ペア、平均SSIM >= 0.7 |
data/output/confusable-weights.json | namespace-guard統合用の1,397の重み付きエッジ |
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圧縮) |