找出给定字体支持哪些字符


68

如何在Linux上从TrueType或嵌入式OpenType字体中提取受支持的Unicode字符列表?

我是否可以使用工具或库来处理.ttf或.eot文件并构建字体提供的代码点列表(如U + 0123,U + 1234等)?


2
试试看fc-list :charset=1234,但仔细检查它的输出...(它确实对我
有用

1
@mirabilos这不是问的问题。它显示了包含给定字符(即1234)的字体。
尼尔·梅休

啊对。但是,这两个问题是交织在一起的(您会在“答案”部分找到许多对错误问题的答案)。
mirabilos

@mirabilos好点。我已经稍微修改了标题,以使问题的意图更加明显。
尼尔·梅

Answers:


45

这是使用FontTools模块的方法(可以使用来安装pip install fonttools):

#!/usr/bin/env python
from itertools import chain
import sys

from fontTools.ttLib import TTFont
from fontTools.unicode import Unicode

ttf = TTFont(sys.argv[1], 0, verbose=0, allowVID=0,
                ignoreDecompileErrors=True,
                fontNumber=-1)

chars = chain.from_iterable([y + (Unicode[y[0]],) for y in x.cmap.items()] for x in ttf["cmap"].tables)
print(list(chars))

# Use this for just checking if the font contains the codepoint given as
# second argument:
#char = int(sys.argv[2], 0)
#print(Unicode[char])
#print(char in (x[0] for x in chars))

ttf.close()

该脚本将字体路径作为参数:

python checkfont.py /path/to/font.ttf

1
int(sys.argv[2], 0)在大多数情况下,它将以“无效文字”失败,因为人们可能想查找特殊字符。使用ord(sys.argv[2].decode('string_escape').decode('utf-8'))代替。
Skippy le Grand Gourou

1
无论如何,此脚本基于的python-fontconfig速度似乎要快得多:unix.stackexchange.com/a/268286/26952
Skippy le Grand

@SkippyleGrandGourou这句话似乎正确吗?它传递sys.argv[1]TTFont()
马丁·图尔诺伊

1
您可以简化以下内容:chars = chain.from_iterable([y + (Unicode[y[0]],) for y in x.cmap.items()] for x in ttf["cmap"].tables)作者chars = list(y + (Unicode[y[0]],) for x in ttf["cmap"].tables for y in x.cmap.items())
Ismael EL ATIFI '19

38

X程序xfd可以执行此操作。要查看“ DejaVu Sans Mono”字体的所有字符,请运行:

xfd -fa "DejaVu Sans Mono"

它包含在Debian / Ubuntu的x11-utils软件包,Fedora / RHEL的xorg-x11-apps和Arch Linux的xorg-xfd中。


1
xfd还提供了十六进制值,您可以在其中输入它们以进行Unicode ala ctrl + shift + u
euxneks,2015年

22
打开GUI字符映射与列出支持的字符完全不一样。
rspeer

我不知道内置位图字体是否可以实现类似的功能6x13
domsson


11

这些fontconfig命令可以将字形列表输出为范围的紧凑列表,例如:

$ fc-match --format='%{charset}\n' OpenSans
20-7e a0-17f 192 1a0-1a1 1af-1b0 1f0 1fa-1ff 218-21b 237 2bc 2c6-2c7 2c9
2d8-2dd 2f3 300-301 303 309 30f 323 384-38a 38c 38e-3a1 3a3-3ce 3d1-3d2 3d6
400-486 488-513 1e00-1e01 1e3e-1e3f 1e80-1e85 1ea0-1ef9 1f4d 2000-200b
2013-2015 2017-201e 2020-2022 2026 2030 2032-2033 2039-203a 203c 2044 2070
2074-2079 207f 20a3-20a4 20a7 20ab-20ac 2105 2113 2116 2120 2122 2126 212e
215b-215e 2202 2206 220f 2211-2212 221a 221e 222b 2248 2260 2264-2265 25ca
fb00-fb04 feff fffc-fffd

使用fc-query.ttf文件和fc-match已安装的字体名称。

这可能不涉及安装任何额外的软件包,也不涉及翻译位图。

使用fc-match --format='%{file}\n'检查正确的字体是否被匹配。


这是谎言:它说“ Gentium Italic”具有“ 2150-2185”,但其中肯定没有2161。
mirabilos

@mirabilos我有万民法5.000,绝对不包含2161:ttx -t cmap -o - /usr/share/fonts/truetype/GentiumPlus-I.ttf | grep 0x2161回报<map code="0x2161" name="uni2161"/><!-- ROMAN NUMERAL TWO -->。FontConfig可能与其他字体匹配。在我安装之前gentiumfc-match 'Gentium Italic'退货了FreeMono.ttf: "FreeMono" "Regular"。如果是这样,的输出--format=%{charset}将不会显示您的期望。
尼尔·梅

我添加了一条注释,其中提到需要检查是否匹配了正确的字体
Neil Mayhew

Gentium Plus≠Gentium(我同时安装了三个,普通版,Basic版和Plus版,但是我想知道Gentium)–啊nvm,我看到了问题:$ fc-match --format ='%{file} \ n'Gentium /usr/share/fonts/truetype/gentium/Gentium-R.ttf $ fc-match --format ='%{file} \ n'Gentium \ Italic /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf $ fc-match --format ='%{file} \ fc-match --format='%{file} ⇒ %{charset}\n' Gentium:Italicn'Gentium :斜体/usr/share/fonts/truetype/gentium/Gentium-I.ttf和DTRT,太好了。
mirabilos

1
很高兴为您解决。也不错的提示,Gentium:Italic而不是Gentium Italic。感谢那。
尼尔·梅休

9

ttf / otf字体的字符代码点存储在CMAP表中。

您可以ttx用来生成CMAP表的XML表示形式。看这里

您可以运行命令ttx.exe -t cmap MyFont.ttf,它应该输出一个文件MyFont.ttx。在文本编辑器中将其打开,它应显示在字体中找到的所有字符代码。


请注意,这ttxfonttools已接受答案中提到的一部分。这是一个Python脚本,因此也可以在Mac和Linux上使用。
mivk

7

这是一个POSIX [1] shell脚本,可以fc-matchNeil Mayhew的答案中提到(它甚至可以处理最多8个十六进制的Unicode),以一种很好的方式轻松地打印代码点和字符。

#!/bin/sh
for range in $(fc-match --format='%{charset}\n' "$1"); do
    for n in $(seq "0x${range%-*}" "0x${range#*-}"); do
        n_hex=$(printf "%04x" "$n")
        # using \U for 5-hex-digits
        printf "%-5s\U$n_hex\t" "$n_hex"
        count=$((count + 1))
        if [ $((count % 10)) = 0 ]; then
            printf "\n"
        fi
    done
done
printf "\n"

您可以传递字体名称或任何fc-match可接受的名称:

$ ls-chars "DejaVu Sans"

更新内容:

我了解到子外壳非常耗时(printf脚本中的子外壳)。因此,我设法编写了一个改进的版本,速度提高了5到10倍!

#!/bin/sh
for range in $(fc-match --format='%{charset}\n' "$1"); do
    for n in $(seq "0x${range%-*}" "0x${range#*-}"); do
        printf "%04x\n" "$n"
    done
done | while read -r n_hex; do
    count=$((count + 1))
    printf "%-5s\U$n_hex\t" "$n_hex"
    [ $((count % 10)) = 0 ] && printf "\n"
done
printf "\n"

旧版本:

$ time ls-chars "DejaVu Sans" | wc
    592   11269   52740

real    0m2.876s
user    0m2.203s
sys     0m0.888s

新版本(行号指示5910个字符,在0.4秒内!):

$ time ls-chars "DejaVu Sans" | wc
    592   11269   52740

real    0m0.399s
user    0m0.446s
sys     0m0.120s

更新结束

样本输出(它在我的st端子better中排列得更好):

0020    0021 !  0022 "  0023 #  0024 $  0025 %  0026 &  0027 '  0028 (  0029 )
002a *  002b +  002c ,  002d -  002e .  002f /  0030 0  0031 1  0032 2  0033 3
0034 4  0035 5  0036 6  0037 7  0038 8  0039 9  003a :  003b ;  003c <  003d =
003e >  003f ?  0040 @  0041 A  0042 B  0043 C  0044 D  0045 E  0046 F  0047 G
...
1f61a😚 1f61b😛 1f61c😜 1f61d😝 1f61e😞 1f61f😟 1f620😠 1f621😡 1f622😢 1f623😣
1f625😥 1f626😦 1f627😧 1f628😨 1f629😩 1f62a😪 1f62b😫 1f62d😭 1f62e😮 1f62f😯
1f630😰 1f631😱 1f632😲 1f633😳 1f634😴 1f635😵 1f636😶 1f637😷 1f638😸 1f639😹
1f63a😺 1f63b😻 1f63c😼 1f63d😽 1f63e😾 1f63f😿 1f640🙀 1f643🙃

[1]似乎\Uprintf不POSIX标准?


4

我有同样的问题,并提出了HOWTO是更进了一步,烘烤所有支持Unicode代码点的正则表达式。

如果你只是想码点的阵列,你可以在你偷看时使用ttx的镀铬devtools XML,运行后ttx -t cmap myfont.ttf和可能,重新命名myfont.ttx,以myfont.xml调用浏览器的XML模式:

function codepoint(node) { return Number(node.nodeValue); }
$x('//cmap/*[@platformID="0"]/*/@code').map(codepoint);

(如果您使用的是ubuntu系统fonttools,也请参考gilamesh的建议sudo apt-get install fonttools。)


1

上面Janus的答案(https://stackoverflow.com/a/19438403/431528)起作用。但是python太慢了,特别是对于亚洲字体。在E5计算机上,文件大小为40MB的字体花费几分钟。

所以我写了一个C ++程序来做到这一点。它取决于FreeType2(https://www.freetype.org/)。这是一个vs2015项目,但由于它是一个控制台应用程序,因此很容易移植到linux。

可以在这里找到代码,https://github.com/zhk/AllCodePoints 对于40MB文件大小的亚洲字体,在我的E5计算机上花费大约30毫秒。


0

您可以使用Font :: TTF模块在Perl的Linux上执行此操作。


1
是的,应该有可能。但这是一个复杂的模块套件,带有痛苦的文档。因此,如果没有如何实现的示例,此答案似乎毫无用处。
mivk

0

如果您只想“查看”字体,则以下内容可能会有所帮助(如果您的终端支持相关字体):

#!/usr/bin/env python
import sys
from fontTools.ttLib import TTFont

with TTFont(sys.argv[1], 0, ignoreDecompileErrors=True) as ttf:
    for x in ttf["cmap"].tables:
        for (_, code) in x.cmap.items():
            point = code.replace('uni', '\\u').lower()
            print("echo -e '" + point + "'")

一种不安全但简单的查看方式:

python font.py my-font.ttf | sh

感谢Janus(https://stackoverflow.com/a/19438403/431528)提供了上述答案。


0

如果要获得字体支持的所有字符,则可以使用以下命令(基于Janus的回答)

from fontTools.ttLib import TTFont

def get_font_characters(font_path):
    with TTFont(font_path) as font:
        characters = {chr(y[0]) for x in font["cmap"].tables for y in x.cmap.items()}
    return characters

您将如何修改此脚本以使其也适用于otf字体?
Mayer Goldberg

0

FreeType的项目提供了演示应用程序,其中的一个演示称为“ ftdump”。然后,您可以执行以下操作:“ ftdump -V指向字体文件的路径”,您将获得所需的内容。要查看源代码,可以在此处关闭源代码:https : //www.freetype.org/developer.html

在Ubuntu上,可以使用“ sudo apt install freetype2-demos”进行安装。

注意:尝试使用“ -c”而不是“ -V”。我看到args在版本之间已更改。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.