
793 coppie confondibili mancanti da Unicode TR39, primo dataset cross-script al mondo, scoring SSIM consapevole dei font su 230 font e oltre 22.000 caratteri
Punteggio di similarità empirica dei glifi mediante raycasting su contorni vettoriali. Analizza coppie di caratteri Unicode confondibili su 245 font di sistema, misura la similarità strutturale direttamente dai contorni dei font (nessuna rasterizzazione) e produce artefatti JSON con punteggi di distanza continua per ogni font.
Risultati principali da 52,6 milioni di confronti a carattere singolo e 190 milioni a caratteri multipli su 22.581 caratteri e 12 sistemi di scrittura:
L'output alimenta direttamente namespace-guard per il rilevamento runtime di confondibili in nomi di pacchetti, nomi di dominio e identificatori.
RaySpace proietta raggi paralleli attraverso i contorni dei font a 36 angoli e cattura cinque strati di informazione per glifo: conteggi di attraversamento, posizioni degli attraversamenti, angoli di attraversamento, distanze ping (spessore del tratto a ogni attraversamento) e ping max (larghezza del controtratto tra gli attraversamenti). Questo produce una firma compatta per carattere per font. Due firme vengono confrontate con una distanza L1 pesata su tutti e cinque gli strati.
Una cascata di filtri a tre stadi rende il confronto esaustivo trattabile:
Il banco di firme (294.646+ voci su 245 font; di più con --include-uppercase) viene precalcolato una volta. La scoperta viene poi eseguita come aritmetica a singolo thread sul banco, completando 52,6 milioni di confronti di coppie in 31 minuti senza thread worker o GPU.
npm install
# 1. Costruisci il banco di firme ray (prerequisito, ~24 min)
npx tsx scripts/build-signature-bank.ts
# 1b. Includi maiuscole latine A-Z (opzionale)
# Per impostazione predefinita il banco include solo codepoint PVALID IDNA (minuscole,
# cifre, simboli). Usa --include-uppercase per aggiungere A-Z maiuscole, utile
# per l'identificazione dei font e il confronto visivo di marchi dove le forme
# delle maiuscole sono importanti. Il costruttore è riprendibile, quindi calcola
# solo i codepoint aggiuntivi.
npx tsx scripts/build-signature-bank.ts --include-uppercase
# 1c. Includi intervalli di codepoint arbitrari (opzionale)
npx tsx scripts/build-signature-bank.ts --extra-range=0041-005A
# 2. Scoperta a carattere singolo (22.581 caratteri, 12 scritture, ~36 min)
npx tsx scripts/discover-singlechar-sdf.ts --scorer=ray
# 3. Scoperta a caratteri multipli (bigrammi) (676 bigrammi, ~63 min)
npx tsx scripts/discover-multichar-sdf.ts --scorer=ray
# 4. Valuta i confondibili multi-carattere TR39 noti (~5 min)
npx tsx scripts/score-multichar-sdf.ts --scorer=ray
La pipeline originale basata su SSIM ha valutato 26,5 milioni di confronti su 230 font. Rimane funzionale ma è stata sostituita da RaySpace per tutte le attività di scoperta e valutazione.
# Valutazione coppie confondibili TR39
npx tsx scripts/build-index.ts # Indice di rendering (~160s, 11.370 PNG)
npx tsx scripts/score-all-pairs.ts # Valuta tutte le coppie (~65s, 235K confronti)
# Scoperta di nuovi confondibili
npx tsx scripts/build-candidates.ts # Insieme candidato (~23K caratteri)
npx tsx scripts/build-index.ts --candidates # Render candidati (~40min, 89K PNG)
npx tsx scripts/score-candidates.ts # Valuta contro target latini (~15min, 2,9M confronti)
# Estrai scoperte ad alto punteggio da entrambe le pipeline
npx tsx scripts/extract-discoveries.ts
"Zeri" = font in cui i contorni producono firme ray bit-identiche (distanza 0,000). Latino w/Cirillico ԝ è identico in tutti i 19 font che contengono entrambi i glifi.
Il Georgiano Coda (U+10FF) forma un anello confondibile a quattro vie con Latino o, Cirillico o e Omicron greco, tutti sotto distanza 0,08.
La scoperta oy/uk cirillico è il risultato inedito più rilevante: il bigramma latino "oy" è visivamente identico alla lettera digrafo cirillico uk (ѹ) a distanza 0,000 in Helvetica e 0,0005 in Arial Unicode MS.
Tre livelli operativi raccomandati:
Interroga quali coppie confondibili esistono per un font specifico. Utile per designer di font che rilasciano un nuovo carattere tipografico, produttori di browser che valutano un cambio di font di sistema, o chiunque scelga un font per display in contesti sensibili alla sicurezza come i domini IDN.
npx tsx scripts/query-font.ts --list-fonts # 218 font nei dati di scoperta
npx tsx scripts/query-font.ts "Arial" # Tutte le coppie per Arial (SSIM >= 0,7)
npx tsx scripts/query-font.ts "Arial" --threshold 0,8 # Solo alta confidenza
npx tsx scripts/query-font.ts "Arial" --compare "Georgia" # Differenza tra due font per delta SSIM
npx tsx scripts/query-font.ts "Arial" --json # JSON per elaborazione a valle
Il matching del nome del font è case-insensitive e parziale, quindi "arial" corrisponde ad Arial, Arial Black e Arial Unicode MS. La modalità di confronto ordina per le maggiori differenze SSIM, mostrando esattamente quali coppie migliorano o peggiorano cambiando font.
Richiede i file di scoperta dalla pipeline di valutazione (gitignorati, rigenerare localmente).
| File | Descrizione |
|---|---|
data/output/render-index/ | PNG render + indice (pipeline SSIM) |
confusable-weights-v2.json con record distribuzionali per coppia: media, p50, p90, conteggio font, conteggio distanza zero, frazione zero e livello raccomandato (stretto/standard/esplorativo). Le distanze RaySpace sostituiscono SSIM. 4.174 coppie a soglia standard.confusable-weights.json per valutazione del rischio visivo misurata tramite confusableDistance({ weights })Articoli su paultendo.github.io che descrivono i risultati e la metodologia alla base di questo progetto:
Metodologia RaySpace e risultati:
Risultati pipeline SSIM:
Articoli che trattano lo spazio problematico più ampio che ha motivato questo progetto:
| Sorgente | Target | Scritture | Media | Font | Zeri |
|---|
| w U+0077 | ԝ U+051D | Latino-Cirillico | 0,000 | 19 | 19 |
| j U+006A | ϳ U+03F3 | Latino-Greco | 0,013 | 21 | 18 |
| i U+0069 | і U+0456 | Latino-Cirillico | 0,018 | 62 | 50 |
| s U+0073 | ѕ U+0455 | Latino-Cirillico | 0,018 | 62 | 46 |
| c U+0063 | с U+0441 | Latino-Cirillico | 0,019 | 61 | 45 |
| o U+006F | о U+043E | Latino-Cirillico | 0,020 | 61 | 44 |
| j U+006A | ј U+0458 | Latino-Cirillico | 0,021 | 60 | 48 |
| x U+0078 | х U+0445 | Latino-Cirillico | 0,023 | 59 | 50 |
| p U+0070 | р U+0440 | Latino-Cirillico | 0,024 | 61 | 46 |
| e U+0065 | е U+0435 | Latino-Cirillico | 0,032 | 61 | 44 |
| a U+0061 | а U+0430 | Latino-Cirillico | 0,042 | 61 | 45 |
| Sorgente | Target | Scritture | Media | Font |
|---|
| ο U+03BF | ჿ U+10FF | Greco-Georgiano | 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 |
| Bigramma | Target | Media | Font | Note |
|---|
| ll | ॥ U+0965 (danda doppia devanagari) | 0,176 | 8 | Inter-scrittura |
| oy | ѹ U+0479 (uk cirillico) | 0,322 | 16 | Nuovo confondibile inter-scrittura bigramma |
| rn | m U+006D | 0,531 | 95 | 33 font sotto 0,40 |
| bl | ы U+044B (jeru cirillico) | 0,797 | 49 | Inter-scrittura |
| Soglia media |
|---|
| Coppie uniche carattere singolo |
|---|
| Coppie uniche multi-carattere |
|---|
| < 0,50 | 138 | 13 |
| < 1,00 | 4.174 | 1.631 |
| < 1,50 | 59.700 | (rumore) |
| < 2,00 | 249.976 | 2.524.275 |
| File | Descrizione |
|---|
data/output/confusable-discoveries.json | 110 coppie TR39 con SSIM alto (>= 0,7) o pixel-identiche |
data/output/candidate-discoveries.json | 793 coppie inedite non in TR39, SSIM medio >= 0,7 |
data/output/confusable-weights.json | 1.397 archi pesati per integrazione con namespace-guard |
data/output/cross-script-discoveries.json | 563 coppie confondibili inter-scrittura |
data/output/cross-script-summary.json | Riepilogo inter-scrittura per coppia di scritture |
data/output/multichar-discoveries.json | Scoperte confondibili multi-carattere |
data/output/singlechar-sdf-scores.jsonl | Punteggi RaySpace a carattere singolo |
data/output/multichar-rayspace-scores.jsonl | Punteggi RaySpace multi-carattere |
data/output/signature-bank/ | Banco firme ray (294.646 voci, 7,9GB compressi) |