Todavía no me queda claro cómo lo hace el propio MacOS, pero mientras tanto esto es lo que terminé haciendo.
Las soluciones que encontré eran todas de la siguiente forma:
- Obtenga una lista de todas las fuentes disponibles.
- Recorre la lista para encontrar las fuentes que contienen el carácter seleccionado.
Listado de todas las fuentes
A partir de esta pregunta Hay dos enfoques (más un tercero que encontré) aquí ):
-
system_profiler SPFontsDataType
al que se puede añadir -xml
para obtener la salida en XML,
-
fc-list
que puede tomar un patrón ( :
es el patrón vacío que coincide con todas las fuentes) y un especificador de formato.
-
Al instante python-fontconfig
y, a continuación, ejecute import fontconfig; fontconfig.query()
para obtener una lista de rutas de fuentes.
La comparación de los dos enfoques (escribí esto antes de haberme fijado en el tercero) es interesante:
-
La velocidad: En mi ordenador y para mi conjunto de fuentes, fc-list
tarda unos 24 segundos la primera vez y 0,04 segundos cada vez después, mientras que system_profiler
tarda sistemáticamente unos 3 segundos cada vez.
-
Amplitud: En mi sistema actual, system_profiler
tiene una lista de 702 fuentes, mientras que fc-list
listas 770: todos esos 702 más 68 más. Por un lado, system_profiler
parece ser la forma "oficial", y coincide con las fuentes visibles en el Libro de fuentes, las que aparecen en "Variación de fuentes" en el visor de caracteres/símbolos (como en la pregunta), el menú en TextEdit, etc. Por otro lado, al menos algunas de las fuentes que se pierden son fuentes realmente utilizables. Esto incluye no sólo las 5 fuentes /Library/Fonts/{Athelas.ttc,Charter.ttc,Marion.ttc,Seravek.ttc,SuperClarendon.ttc}
sobre el que se pueden encontrar algunas páginas confusas en Internet (por ejemplo este y este ), pero también /Library/Fonts/{DIN Alternate Bold.ttf,DIN Condensed Bold.ttf,Iowan Old Style.ttc}
y 57 de los 177 Noto Sans fuentes que tengo instaladas en mi sistema. Por ejemplo, tengo instalada la Noto Sans Brahmi, pero esta fuente no aparece en el Libro de fuentes ni en "Variación de fuentes" cuando busco una letra Brahmi (digamos ), pero sí se utiliza en TextEdit (y se muestra en mi navegador). Sea cual sea la razón de esta rareza, estoy contento de poder obtener la lista completa con fc-list
.
-
Facilidad de uso: con cualquiera de los dos métodos se requiere un poco de análisis de la salida. Con fc-list
Puedo especificar el formato (por ejemplo fc-list --format="%{family}\n%{file}\n%{lang}\n\n"
pero no pude encontrar una referencia para los nombres de los campos); con system_profiler
Puedo simplemente grep para Location:
o la salida a XML y parsear el XML (ejemplos con xml.etree.ElementTree , con plistlib ).
¿Este tipo de letra cubre este carácter?
Sea como sea que obtengamos la lista de fuentes, a continuación tenemos que comprobar si un carácter está cubierto por una fuente específica (dada por el nombre o la ruta). De nuevo, las formas que he encontrado:
-
Utilice uno de los enlaces de FreeType . En el caso de Python, existe freetype-py pero no he podido averiguar en pocos minutos cómo utilizarlo.
-
Volcar la tabla cmap de la fuente con ttx/fonttools , y luego hacer un bucle sobre la tabla. Esto es ciertamente factible y he utilizado este tipo de dumping muchas veces (uno puede simplemente ttx foo.ttf
para obtener el foo.ttx
xml que es incluso legible para los humanos), pero para este caso de uso (buscar en todas las fuentes), no es lo mejor, ya que tarda segundos por fuente.
-
Busca la tabla cmap en una biblioteca escrita para ello: use Font::TTF::Font
en Perl , from fontTools.ttLib import TTFont
en Python -- esto sería algo así como:
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
Por desgracia, falla en demasiadas fuentes para ser útil.
-
Si utiliza la solución Python-fontconfig Hay un has_char
usado como..: font = fontconfig.FcFont(path); return font.has_char(c)
Resumen
Acabé utilizando la solución de aquí que he reescrito ligeramente para que sea mínimo:
#!/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)
Ejemplo de uso:
% 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
(Funciona tanto con python3
y python2
, cualquiera que sea python
que tienes. Tarda unos 29 segundos en mi ordenador, para el conjunto de fuentes que tengo instaladas).