Encontre a melhor fonte para renderizar um ponto de código

16

Como encontrar a fonte apropriada para renderizar pontos de código unicode?

gnome-terminaldescobrimos que caracteres como «🉃 ⼼ 😻🕲🝤» podem ser renderizados com fontes como Symbola, em vez da fonte do meu terminal ou do fallback do ponto de código no quadrado (????). Como ?

Não
fonte
Relacionados: askubuntu.com/questions/27598/...
Nathaniel M. Beaver

Respostas:

14

Este não é necessariamente o melhor método, e com certeza não é fácil de usar, mas é fácil começar a trabalhar: aqui está um script Python para fazer isso.

Instale a biblioteca Python-fontconfig . Você pode obtê-lo da sua distribuição (por exemplo, sudo apt-get install python-fontconfigno Debian e derivados) ou instalá-lo em seu diretório pessoal ( pip install --user python-fontconfig). Então você pode executar este script (salve-o como fc-search-codepointem um diretório em seu PATH, por exemplo ~/bin, tipicamente , e torne-o executável):

#!/usr/bin/env python2
import re, sys
import fontconfig
if len(sys.argv) < 1:
    print('''Usage: ''' + sys.argv[0] + '''CHARS [REGEX]
Print the names of available fonts containing the code point(s) CHARS.
If CHARS contains multiple characters, they must all be present.
Alternatively you can use U+xxxx to search for a single character with
code point xxxx (hexadecimal digits).
If REGEX is specified, the font name must match this regular expression.''')
    sys.exit(0)
characters = sys.argv[1]
if characters.startswith('U+'):
    characters = unichr(int(characters[2:], 16))
else:
    characters = characters.decode(sys.stdout.encoding)
regexp = re.compile(sys.argv[2] if len(sys.argv) > 2 else '')

font_names = fontconfig.query()
found = False
for name in font_names:
    if not re.search(regexp, name): continue
    font = fontconfig.FcFont(name)
    if all(font.has_char(c) for c in characters):
        print(name)
        found = True

sys.exit(0 if found else 1)

Exemplo de uso:

$ fc-search-codepoint 🉃⼼😻🕲🝤
$ echo $?
1

Eu não tenho nenhuma fonte com todos esses caracteres.

$ fc-search-codepoint U+1F64D
/usr/share/fonts/truetype/unifont/unifont_upper.ttf
/usr/share/fonts/truetype/unifont/unifont_upper_csur.ttf
Gilles 'SO- parar de ser mau'
fonte
1
Esse é um script muito útil! No entanto, é compatível apenas com python2, e suponho que seja um pouco desagradável fazer exatamente esse portátil. Você se importaria de, pelo menos, mudar o #!/usr/bin/env pythonque #!/usr/bin/env python2de acordo com PEP 394.
Zulan
1
Obrigado por esta resposta! Foi muito útil. Tenho certeza de que as bibliotecas do sistema operacional ou do sistema que implementam fallback de fontes estão fazendo algo mais eficiente, mas isso funciona. @Zulan Também pode ser feito para trabalhar python3; Acabei de escrever uma versão menor disso na parte inferior desta resposta .
precisa
5

Usando fontconfig,

> fc-list ':charset=<hex_code1> <hex_code2>'

por exemplo

> fc-list ':charset=2713 2717'

exibirá qualquer nome de arquivo de fonte contendo ✓ e ✗.

Para obter o ponto de código correspondente ao caractere, use (por exemplo)

> printf "%x" \'✓
2713>

Isso usa um recurso um tanto obscuro do utilitário POSIXprintf :

Se o caractere principal for uma aspas simples ou aspas duplas, o valor será o valor numérico no conjunto de códigos subjacente do caractere após a aspas simples ou aspas simples.

Tomados em conjunto,

> printf '%x' \'✓ | xargs -I{} fc-list ":charset={}"

Isso usa o xargs -Isinalizador para substituir {}por nomes de stdin. Portanto, isso se resume a:

> fc-list ":charset=2713"
David Baynard
fonte
2
Observe que você precisa de uma versão fontconfigdisso 2.11.91ou posterior .
Nathaniel M. Beaver
1
note que traço printfe /bin/printfapoio não faça isso
Steven Penny
1
Impressionante! Estou procurando informações sobre isso há muito tempo. Observe que você também pode especificar intervalos e caracteres únicos, para encontrar todas as fontes que possuem todos os caracteres de desenho da caixa, por exemplo:fc-list --format='%{postscriptname}\n' ':charset=2500-257F'
Neil Mayhew
3

Por fim, o gnome-terminal usa o fontconfig para (entre outras coisas):

... encontre de maneira eficiente e rápida as fontes necessárias entre o conjunto de fontes que você instalou, mesmo que você tenha instalado milhares de fontes ...

Na documentação da API, você pode encontrar funções para consultar intervalos de caracteres de fontes e para operações em intervalos de caracteres, mas a documentação é tão enigmática que eu nunca consegui descobrir como diferentes conjuntos de funções se relacionam. Se eu precisasse me aprofundar, preferiria ver exemplos de uso em outro software, talvez o vte (a biblioteca de emulação de terminal usada no gnome-terminal).

Outra biblioteca entre vte e fontconfig é o pango "... uma biblioteca para disposição e renderização de texto, com ênfase na internacionalização ..." . Agora que penso nisso, soa como o único a conter a maior parte da lógica que você procura.

A funcionalidade de cobertura de caracteres no pango é implementada por mapas de cobertura ( "Muitas vezes é necessário no Pango determinar se uma fonte específica pode representar um caractere específico e também quão bem ele pode representar esse caractere. O PangoCoverage é uma estrutura de dados usada para representar essas informações. " ), mas provavelmente há detalhes mais complicados envolvidos na decisão de qual glifo renderizar com qual fonte. Eu acho que o VTE depende do pango para processar seqüências de caracteres com fontes apropriadas, enquanto o pango usa fontconfig (ou outro back-end de fonte suportado) para encontrar a fonte mais apropriada com base em várias partes da lógica no próprio pango e / ou no back-end.

artm
fonte
1

Alterei o código para verificar se uma fonte contém todos os caracteres de uma determinada string. Portanto, isso pode ser chamado fc-search-codepoint "$fontname" "$string"e retorna o código de saída 0 em caso de sucesso ou 1 em caso contrário. Os nomes das fontes podem ser recuperados fc-query /path/to/FontSandMonoBoldOblique.ttfou do Imagemagick convert -list font. Eu o uso para verificar se uma sequência selecionada pelo usuário pode ser renderizada com a fonte selecionada pelo usuário e se o comando falhar, uma fonte alternativa será usada.

#!/usr/bin/env python2
import re
import sys
import os
import fontconfig
if len(sys.argv) < 3:
    print("Usage: " + sys.argv[0] + " 'Fontname-Bold' 'String to check'")
    sys.exit(0)

font_name = sys.argv[1].decode('utf-8')
string = sys.argv[2].decode('utf-8')

if '-' in font_name:
        font_name = font_name.split('-')
        font_style = font_name[-1]
        font_name = ''.join(font_name[:-1])
else:
        font_style = ""

font_names = fontconfig.query()
for name in font_names:
    font = fontconfig.FcFont(name)
    if not len(font.family) > 0:
        continue
    for item in font.family:
        if item[1] == unicode(font_name):
            if len(font_style) == 0:
                match = "yes"
            else:
                for item in font.style:
                    if item[1] == unicode(font_style):
                        match = "yes"
            try:
                match
            except NameError:
                continue
            if all(font.has_char(c) for c in string):
                sys.exit(0)
            else:
                sys.exit(1)
print >> sys.stderr, "font not found: " + font_name + " " + font_style
sys.exit(1)
ladiko
fonte