
793 paires ambiguës manquantes dans Unicode TR39, premier jeu de données inter-écritures au monde, notation SSIM tenant compte de la police parmi 230 polices et plus de 22 000 caractères
Évaluation empirique de la similarité des glyphes par raycasting vectoriel des contours. Rend les paires de caractères Unicode confusables dans 245 polices système, mesure la similarité structurelle directement à partir des contours (sans pixellisation), et produit des artefacts JSON notés avec des scores de distance continue par police.
Résultats clés issus de 52,6 millions de comparaisons mono-caractère et 190 millions de comparaisons multi-caractères sur 22 581 caractères et 12 systèmes d'écriture :
La sortie alimente directement namespace-guard pour la détection de confusables à l'exécution dans les noms de paquets, les noms de domaine et les identifiants.
RaySpace projette des rayons parallèles à travers les contours des polices à 36 angles et capture cinq couches d'information par glyphe : nombre d'intersections, positions des intersections, angles d'intersection, distances de ping (épaisseur de trait à chaque intersection) et ping max (largeur de contre-forme entre les intersections). Cela produit une signature compacte par caractère et par police. Deux signatures sont comparées avec une distance L1 pondérée sur les cinq couches.
Un filtre en cascade à trois étapes rend la comparaison exhaustive réalisable :
La banque de signatures (294 646+ entrées sur 245 polices ; plus avec --include-uppercase) est précalculée une fois. La découverte s'effectue ensuite par arithmétique mono-thread sur la banque, réalisant 52,6 millions de comparaisons en 31 minutes sans threads ouvriers ni GPU.
npm install
# 1. Construire la banque de signatures de rayons (prérequis, ~24 min)
npx tsx scripts/build-signature-bank.ts
# 1b. Inclure les lettres majuscules latines A-Z (optionnel)
# Par défaut la banque n'inclut que les points de code IDNA PVALID (minuscules,
# chiffres, symboles). Utilisez --include-uppercase pour ajouter A-Z majuscules,
# utile pour l'identification de polices et la comparaison visuelle de marques
# où les formes des majuscules comptent. Le constructeur est repréable, donc
# seuls les points de code supplémentaires sont calculés.
npx tsx scripts/build-signature-bank.ts --include-uppercase
# 1c. Inclure des plages arbitraires de points de code (optionnel)
npx tsx scripts/build-signature-bank.ts --extra-range=0041-005A
# 2. Découverte mono-caractère (22 581 caractères, 12 écritures, ~36 min)
npx tsx scripts/discover-singlechar-sdf.ts --scorer=ray
# 3. Découverte multi-caractère (bigrammes) (676 bigrammes, ~63 min)
npx tsx scripts/discover-multichar-sdf.ts --scorer=ray
# 4. Noter les confusables multi-caractères TR39 connus (~5 min)
npx tsx scripts/score-multichar-sdf.ts --scorer=ray
Le pipeline d'origine basé sur SSIM a noté 26,5 millions de comparaisons sur 230 polices. Il reste fonctionnel mais est remplacé par RaySpace pour toutes les tâches de découverte et de notation.
# Notation des paires confusables TR39
npx tsx scripts/build-index.ts # Construire l'index (~160s, 11 370 PNG)
npx tsx scripts/score-all-pairs.ts # Noter toutes les paires (~65s, 235 000 comparaisons)
# Découverte de nouveaux confusables
npx tsx scripts/build-candidates.ts # Ensemble candidat (~23 000 caractères)
npx tsx scripts/build-index.ts --candidates # Rendre les candidats (~40min, 89 000 PNG)
npx tsx scripts/score-candidates.ts # Noter par rapport aux cibles latines (~15min, 2,9 millions de comparaisons)
# Extraire les découvertes les mieux notées des deux pipelines
npx tsx scripts/extract-discoveries.ts
« Zéros » = polices où les contours produisent des signatures de rayons bit-identiques (distance 0,000). Le w latin / ԝ cyrillique est identique dans les 19 polices contenant les deux glyphes.
La codavie géorgienne (U+10FF) forme un anneau de confusables à quatre avec le o latin, le o cyrillique et l'omicron grec, tous en dessous d'une distance de 0,08.
La découverte oy/uk cyrillique est le résultat le plus marquant : le bigramme latin « oy » est visuellement identique à la lettre digraphe cyrillique uk (ѹ) à une distance de 0,000 dans Helvetica et 0,0005 dans Arial Unicode MS.
Trois niveaux d'exploitation recommandés :
Interrogez les paires confusables existant pour une police donnée. Utile pour les concepteurs de polices livrant une nouvelle police, les éditeurs de navigateurs évaluant un changement de police système, ou toute personne choisissant une police d'affichage pour des contextes sensibles à la sécurité comme les domaines IDN.
npx tsx scripts/query-font.ts --list-fonts # 218 polices dans les données de découverte
npx tsx scripts/query-font.ts "Arial" # Toutes les paires pour Arial (SSIM >= 0,7)
npx tsx scripts/query-font.ts "Arial" --threshold 0,8 # Haute confiance uniquement
npx tsx scripts/query-font.ts "Arial" --compare "Georgia" # Différence entre deux polices par delta SSIM
npx tsx scripts/query-font.ts "Arial" --json # JSON pour traitement en aval
La correspondance du nom de police est insensible à la casse et partielle, donc « arial » correspond à Arial, Arial Black et Arial Unicode MS. Le mode comparaison trie par les plus grands écarts SSIM en premier, révélant exactement quelles paires s'améliorent ou se dégradent en changeant de police.
Nécessite les fichiers de découverte issus du pipeline de notation (gitignorés, à régénérer localement).
| Fichier | Description |
|---|---|
data/output/render-index/ | PNG rendus + index (pipeline SSIM) |
confusable-weights-v2.json avec enregistrements distributionnels par paire : moyenne, p50, p90, nombre de polices, nombre de zéros, fraction de zéros et niveau recommandé (strict/standard/exploratoire). Les distances RaySpace remplacent SSIM. 4 174 paires au seuil standard.confusable-weights.json pour l'évaluation mesurée du risque visuel via confusableDistance({ weights })Écrits disponibles sur paultendo.github.io couvrant les découvertes et la méthodologie derrière ce projet :
Méthodologie et découvertes RaySpace :
Découvertes du pipeline SSIM :
Articles couvrant le problème plus large qui a motivé ce projet :
| Source | Cible | Écritures | Moyenne | Polices | Zéros |
|---|
| w U+0077 | ԝ U+051D | Latin-Cyrillique | 0,000 | 19 | 19 |
| j U+006A | ϳ U+03F3 | Latin-Grec | 0,013 | 21 | 18 |
| i U+0069 | і U+0456 | Latin-Cyrillique | 0,018 | 62 | 50 |
| s U+0073 | ѕ U+0455 | Latin-Cyrillique | 0,018 | 62 | 46 |
| c U+0063 | с U+0441 | Latin-Cyrillique | 0,019 | 61 | 45 |
| o U+006F | о U+043E | Latin-Cyrillique | 0,020 | 61 | 44 |
| j U+006A | ј U+0458 | Latin-Cyrillique | 0,021 | 60 | 48 |
| x U+0078 | х U+0445 | Latin-Cyrillique | 0,023 | 59 | 50 |
| p U+0070 | р U+0440 | Latin-Cyrillique | 0,024 | 61 | 46 |
| e U+0065 | е U+0435 | Latin-Cyrillique | 0,032 | 61 | 44 |
| a U+0061 | а U+0430 | Latin-Cyrillique | 0,042 | 61 | 45 |
| Source | Cible | Écritures | Moyenne | Polices |
|---|
| ο U+03BF | ჿ U+10FF | Grec-Géorgien | 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 |
| Bigramme | Cible | Moyenne | Polices | Notes |
|---|
| ll | ॥ U+0965 (double danda devanagari) | 0,176 | 8 | Inter-écriture |
| oy | ѹ U+0479 (uk cyrillique) | 0,322 | 16 | Nouveau bigramme confusable inter-écriture |
| rn | m U+006D | 0,531 | 95 | 33 polices en dessous de 0,40 |
| bl | ы U+044B (yeru cyrillique) | 0,797 | 49 | Inter-écriture |
| Seuil moyen |
|---|
| Paires mono-caractère uniques |
|---|
| Paires multi-caractère uniques |
|---|
| < 0,50 | 138 | 13 |
| < 1,00 | 4 174 | 1 631 |
| < 1,50 | 59 700 | (bruit) |
| < 2,00 | 249 976 | 2 524 275 |
| Fichier | Description |
|---|
data/output/confusable-discoveries.json | 110 paires TR39 avec SSIM élevé (>= 0,7) ou identiques au pixel |
data/output/candidate-discoveries.json | 793 nouvelles paires absentes de TR39, SSIM moyen >= 0.7 |
data/output/confusable-weights.json | 1 397 arêtes pondérées pour intégration dans namespace-guard |
data/output/cross-script-discoveries.json | 563 paires confusables inter-écritures |
data/output/cross-script-summary.json | Résumé inter-écritures par paire d'écritures |
data/output/multichar-discoveries.json | Découvertes confusables multi-caractères |
data/output/singlechar-sdf-scores.jsonl | Scores mono-caractère RaySpace |
data/output/multichar-rayspace-scores.jsonl | Scores multi-caractère RaySpace |
data/output/signature-bank/ | Banque de signatures Ray (294 646 entrées, 7,9 Go compressés) |