| 字体名称在哪里?
Font Names And Where To Find Them
by Duramente
缘起 #
为了使 Excalidraw 支持中文,我写了一个油猴脚本,来将默认的仅支持英文的 Virgil 字体替换为其他中文字体。原理很简单,就是通过 @font-face
来覆盖掉原有的字体。
@font-face {
font-family: "Virgil";
src: local("YouYuan");
}
一切都非常完美,直到我试着用其他字体。例如,当我打算使用手写字体 也字工厂苍南手迹
(YeZiGongChangCangNanShouJi-2.ttf
):
@font-face {
font-family: "Virgil";
src: local("也字工厂苍南手迹");
}
控制台打印出了一条错误信息:
Uncaught (in promise) DOMException: A network error occurred.
n Fonts.ts:87
Lu Fonts.ts:81
这很奇怪,毕竟我并没有使用网络资源,而是使用了本地字体。不过不管怎样,这个错误的产生原因很明显:local
并没有找到我指定的字体。
那么字体名称是什么呢?
探索 #
在 MDN 中写道:
For OpenType and TrueType fonts, is used to match either the Postscript name or the full font name in the name table of locally available fonts.
Windows #
为了得到所谓的“full font name”,我的第一想法便是查看系统的字体管理器。但令人伤心的是,Windows 并没有给出正确答案。在系统的各个位置,Windows 显示了多种多样的字体名称,但没有一个是有用的。
![]() |
![]() |
![]() |
---|---|---|
字体安装?没用。 | 字体选择?没用。 | 控制面板?没用。 |
浏览器字体 API #
于是我放弃了相信 Windows,打算使用浏览器提供的 Local Font Access API。
虽然它在 Firefox 中不能用,但即使浏览器不同,字体总归应该是一样的。因此我打开了 Edge,成功得到了我需要的信息:
(await window.queryLocalFonts())
.filter(font => font.fullName.indexOf("也字工廠蒼南手跡") != -1)[0]
// result:
{
family: "YEFONTCangNanShouJi",
fullName: "也字工廠蒼南手跡 標準",
postscriptName: "YEFONTCangNanShouJi-Regylar",
style: "标准"
}
皆大欢喜……是这样吗?虽然对于这个字体来说,API返回了正确的结果,但对于其他字体,它依然无法给出正确的结果。如“杨任东竹石体”,虽然返回如下,
{
postscriptName: 'YRDZST-Light',
fullName: '杨任东竹石体-Light',
family: 'YRDZST',
style: 'Light'
}
但在 Firefox 中使用 local(杨任东竹石体-Light)
并无法找到这个字体。
What the Fuck?
根源 #
这些结果让人震惊,也更让我打算将其弄个明白所谓的 “full font name” 和 “postscript name” 到底是什么。
对于 postscript name,StackOverflow 上一个回答写道:
you would find this in the “name” table of the font. It would be nameId 0x0006
听起来不错,正如 OpenType 标准与 TrueType 标准所告诉我们的一样1。
因此只需解析 TTF 文件,便可以得到字体的名称了。Python 恰好有 fontTools 库来做这个事情。
from fontTools import ttLib
font = ttLib.TTFont('YangRenDongZhuShiTi-Light-2.ttf')
for record in font['name'].names:
if record.nameID == 4:
print(f'full name: 0x{record.langID:04X}, {record}')
if record.nameID == 6:
print(f'postscript: 0x{record.langID:04X}, {record}')
输出如下:
# 0x0409 = en, 0x0804 = zh
'''
full name: 0x0000, YRDZST-Light
postscript: 0x0000, YRDZST-Light
full name: 0x0409, YRDZST-Light
postscript: 0x0409, YRDZST-Light
full name: 0x0804, 杨任东竹石体-Light
'''
这样我们就得到了正确的结果。正如我们所见,字体的名称是有多种语言版本的。Firefox 只能识别英文版本的名称(如“YRDZST-Light”),而 Edge 则只识别中文版本的名称(“杨任东竹石体-Light”)。
实际上,翻看代码后可以发现,Firefox 会查找字体的英文名或第一个名字2:
1// for use in reading postscript or fullname
2static HRESULT GetDirectWriteFaceName(IDWriteFont* aFont,
3 DWRITE_INFORMATIONAL_STRING_ID aWhichName,
4 nsACString& aFontName) {
5 HRESULT hr;
6
7 BOOL exists;
8 RefPtr<IDWriteLocalizedStrings> infostrings;
9 hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings),
10 &exists);
11 if (FAILED(hr) || !exists) {
12 return E_FAIL;
13 }
14
15 if (!GetEnglishOrFirstName(aFontName, infostrings)) {
16 return E_FAIL;
17 }
18
19 return S_OK;
20}
Chromium 系列浏览器则是使用系统 API 来获取本地化后的字体名称3:
1absl::optional<std::string> GetFontFullName(IDWriteFont* font,
2 const std::string& locale) {
3 absl::optional<std::string> full_name;
4
5 Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> full_names =
6 GetFontInformation(font, DWRITE_INFORMATIONAL_STRING_FULL_NAME);
7 if (!full_names)
8 return full_name;
9
10 full_name = GetLocalizedString(full_names.Get(), locale);
11 return full_name;
12}
尾声 #
为什么会有这种差异呢?这是 Firefox 的 bug 吗?还是 Edge 的 bug?或者是标准的问题?
让我们来看看 CSS Fonts Module Level 3 中的定义:
For OpenType fonts with multiple localizations of the full font name, the US English version is used or the first localization when a US English full font name is not available. User agents that also match other full font names are considered non-conformant. This is done not to prefer English but to avoid matching inconsistencies across font versions and OS localizations.
这就是原因所在。Firefox 严格遵循了标准,而 Edge 则没有。
但是,这个标准是不是有问题呢?我认为是的。因为这样的话,其他语言的用户就无法使用他们看到的,更加直观的字体名称了。这就是在偏爱英文,即使它声称自己不是。
-
OpenType 是 TrueType 的超集,TTF 为包含 TrueType 字体的 OpenType 文件后缀名。 ↩︎
-
https://searchfox.org/mozilla-central/source/gfx/thebes/gfxDWriteFontList.cpp#132 ↩︎
-
https://source.chromium.org/chromium/chromium/src/+/main:content/browser/font_access/font_enumeration_data_source_win.cc;l=127;drc=287ee7c19a714f1386591c5bc99e94000bba0d2d;bpv=0;bpt=1 ↩︎