Je ne sais toujours pas comment MacOS procède, mais en attendant, voici ce que j'ai fini par faire.
Les solutions que j'ai trouvées étaient toutes de la forme suivante :
- Obtenez une liste de toutes les polices disponibles.
- Boucle sur la liste pour trouver les polices qui contiennent le caractère sélectionné.
Liste de toutes les polices
En date du cette question Il existe deux approches (plus une troisième que j'ai trouvée). ici ) :
-
system_profiler SPFontsDataType
à laquelle vous pouvez ajouter -xml
pour obtenir une sortie en XML,
-
fc-list
qui peut prendre un motif ( :
est le motif vide qui correspond à toutes les polices) et un spécificateur de format.
-
Instantanément python-fontconfig
puis exécutez import fontconfig; fontconfig.query()
pour obtenir une liste de chemins de polices.
La comparaison des deux approches (j'ai écrit ceci avant d'avoir remarqué la troisième) est intéressante :
-
Vitesse : Sur mon ordinateur et pour mon jeu de polices, fc-list
prend environ 24 secondes la première fois et 0,04 seconde chaque fois après cela, tandis que system_profiler
prend systématiquement environ 3 secondes à chaque fois.
-
L'exhaustivité : Sur mon système actuel, system_profiler
énumère 702 polices de caractères, tandis que fc-list
listes 770 : toutes ces 702 plus 68 autres. D'un côté, system_profiler
semble être la méthode "officielle", et correspond aux polices visibles dans le Livre des polices, celles qui apparaissent dans "Variation des polices" dans le visualiseur de caractères/symboles (comme dans la question), le menu dans TextEdit, etc. D'un autre côté, au moins certaines des polices qui lui manquent sont des polices réellement utilisables. Cela inclut non seulement les 5 polices /Library/Fonts/{Athelas.ttc,Charter.ttc,Marion.ttc,Seravek.ttc,SuperClarendon.ttc}
à propos duquel vous pouvez trouver quelques pages confuses en ligne (par ex. ce y ce ), mais aussi /Library/Fonts/{DIN Alternate Bold.ttf,DIN Condensed Bold.ttf,Iowan Old Style.ttc}
et 57 des 177 Noto Sans les polices que j'ai installées sur mon système. Par exemple, j'ai installé Noto Sans Brahmi, mais cette police n'apparaît pas dans Font Book ou dans "Font Variation" lorsque je recherche une lettre Brahmi (disons ), mais elle est utilisée dans TextEdit (et affichée dans mon navigateur). Quelle que soit la raison de cette bizarrerie, je suis content de pouvoir obtenir la liste complète avec fc-list
.
-
Facilité d'utilisation : avec l'une ou l'autre des méthodes, il est nécessaire d'analyser un peu la sortie. Avec fc-list
Je peux spécifier le format (par ex. fc-list --format="%{family}\n%{file}\n%{lang}\n\n"
mais je n'ai pas trouvé de référence pour les noms des champs !); avec system_profiler
Je peux soit juste chercher Location:
ou sortie en XML et analyse du XML (exemples avec xml.etree.ElementTree , avec plistlib ).
Cette police couvre-t-elle ce personnage ?
Quelle que soit la façon dont nous obtenons la liste des polices, nous devons ensuite vérifier si un caractère est couvert par une police spécifique (donnée par son nom ou son chemin). Encore une fois, les moyens que j'ai trouvés :
-
Utilisez l'une des liaisons FreeType . Pour Python, il existe freetype-py mais je n'ai pas réussi à comprendre en quelques minutes comment l'utiliser.
-
Vide la table cmap de la police avec ttx/fonttools puis boucle sur la table. C'est certainement faisable et j'ai utilisé ce type de vidage à plusieurs reprises (on peut juste ttx foo.ttf
pour obtenir le foo.ttx
xml qui est même lisible par l'homme), mais pour ce cas d'utilisation (recherche sur toutes les polices), ce n'est pas le meilleur car cela prend des secondes par police.
-
Recherchez la table cmap dans une bibliothèque écrite à cet effet : use Font::TTF::Font
en Perl , from fontTools.ttLib import TTFont
en Python -- ce serait quelque chose comme :
def has_char(font_path, c):
"""Does font at `font_path` contain the character `c`?"""
from fontTools.ttLib import TTFont
from fontTools.unicode import Unicode
try:
font = TTFont(font_path)
for table in font['cmap'].tables:
for char_code, glyph_name in table.cmap.items():
if char_code == ord(c):
font.close()
return True
except Exception as e:
print('Error while looking at font %s: %s' % (font_path, e))
pass
return False
Malheureusement, il échoue sur beaucoup trop de polices pour être utile.
-
Si vous utilisez la solution Python-fontconfig il y a un has_char
utilisé comme : font = fontconfig.FcFont(path); return font.has_char(c)
Résumé
J'ai fini par utiliser la solution de ici que j'ai légèrement réécrite pour la rendre minimale :
#!/usr/bin/env python
def find_fonts(c):
"""Finds fonts containing the (Unicode) character c."""
import fontconfig
fonts = fontconfig.query()
for path in sorted(fonts):
font = fontconfig.FcFont(path)
if font.has_char(c):
yield path
if __name__ == '__main__':
import sys
search = sys.argv[1]
char = search.decode('utf-8') if isinstance(search, bytes) else search
for path in find_fonts(char):
print(path)
Exemple d'utilisation :
% python3 find_fonts.py ''
/Library/Fonts/Arial Unicode.ttf
/Library/Fonts/Kannada MN.ttc
/Library/Fonts/Kannada MN.ttc
/Library/Fonts/Kannada Sangam MN.ttc
/Library/Fonts/Kannada Sangam MN.ttc
/System/Library/Fonts/LastResort.ttf
/Users/shreevatsa/Library/Fonts/Kedage-b.TTF
/Users/shreevatsa/Library/Fonts/Kedage-i.TTF
/Users/shreevatsa/Library/Fonts/Kedage-n.TTF
/Users/shreevatsa/Library/Fonts/Kedage-t.TTF
/Users/shreevatsa/Library/Fonts/NotoSansKannada-Bold.ttf
/Users/shreevatsa/Library/Fonts/NotoSansKannada-Regular.ttf
/Users/shreevatsa/Library/Fonts/NotoSansKannadaUI-Bold.ttf
/Users/shreevatsa/Library/Fonts/NotoSansKannadaUI-Regular.ttf
/Users/shreevatsa/Library/Fonts/NotoSerifKannada-Bold.ttf
/Users/shreevatsa/Library/Fonts/NotoSerifKannada-Regular.ttf
/Users/shreevatsa/Library/Fonts/akshar.ttf
(Fonctionne avec les deux python3
y python2
, quel que soit python
vous avez. Cela prend environ 29 secondes sur mon ordinateur, pour le jeu de polices que j'ai installé).