网站字体加载之坑
今天一位博客朋友向我反映:在使用我站点主题后不能正常加载自定义字体。
定位问题
他已经预先进行了一些排查,但我也要从头检查一遍。
我的检查顺序为:
- 字体是否正常加载(网络是否正常)。
- 字体内容是否损坏。
@font-face
中定义的src
的url
是否正确。@font-face
中定义的font-display
是否正确。@font-face
中定义的font-family
和实际使用的font-family
是否对应。
我抵达他的站点,打开开发人员工具(F12),发现控制台提示 "The resource xxxxx.ttf was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate as
value and it is preloaded intentionally."。显然字体正常预加载了,但是未能正常使用。
接下来我下载了他的字体,在本地预览发现没有问题,排除了字体损坏的可能。之后我检查了 @font-face
中定义的 src
的 url
,对比预加载的链接,是保持一致且正确的。
确认 font-display
设置为 swap
值。有无限的交换周期,即使加载缓慢也不会影响字体显示。
最后我使用 Ctrl + Shift+ C
随便点击一个文字,进入开发人员工具的“元素”子页面,点击下方的“已计算”,搜索 "font-family"。对比后确认正确无误。与此同时,下方的“呈现的字体”这一栏说明了并没有正确加载字体。
我打开本地测试环境,经过对照实验后,发现 woff2/woff 格式能够正常显示,而 ttf
格式的字体无法正常显示。
发现问题
我从浏览器历史记录中找出 MDN 的 font-display 文档,它给的例子是:
@ font-face {
font-family: ExampleFont;
src: url(/path/to/fonts/examplefont.woff)format('woff'),
url(/path/to/fonts/examplefont.eot)format('eot');
font-weight: 400;
font-style: normal;
font-display: fallback;
}
其中声明了 eot 格式和 woff 格式的字体的 format 分别为 'eot' 和 'woff'。@
和 font-face
多了一个空格并且使用了全角括号和全角逗号,应该是笔误。
我查看其英文原始页面 font-display
它给的例子是:
@font-face {
font-family: ExampleFont;
src:
url("/path/to/fonts/example-font.woff") format("woff"),
url("/path/to/fonts/example-font.eot") format("eot");
font-weight: 400;
font-style: normal;
font-display: fallback;
}
我隐隐约约觉得哪里不对劲,但是说不上来。
再次检查主题代代码(以下是经过简化的相关主题代码):
<th:block
th:with="font=${theme.config?.styles?.custom_font_files)}"
>
<link
rel="preload"
th:href="${font}"
as="font"
th:type="'font/' + ${font.substring(font.lastIndexOf('.') + 1)}"
crossorigin
/>
<style th:inline="css">
@font-face {
font-display: swap;
font-family: "custom";
font-style: normal;
font-weight: 400;
src:
local("[(${theme.config?.styles?.custom_font_name})]"),
url("[(${font})]") format("[(${font.substring(font.lastIndexOf('.') + 1)})]");
}
:root {
--higan-font-family:
custom, ui-sans-serif, system-ui, -apple-system, blinkmacsystemfont, segoe ui, roboto, helvetica neue,
arial, noto sans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", segoe ui symbol, "Noto Color Emoji" !important;
}
</style>
</th:block>
忽然,发现我先入为主的认为 format
的值就是字体文件后缀。
我找出 MDN 在这方面的相关文档:src
关键字 | 字体格式 | 常见扩展名 |
---|---|---|
collection |
OpenType Collection | .otc、.ttc |
embedded-opentype |
Embedded OpenType | .eot |
opentype |
OpenType | .otf、.ttf |
svg |
SVG Font(已弃用) | .svg、.svgz |
truetype |
TrueType | .ttf |
woff |
WOFF 1.0 | .woff |
woff2 |
WOFF 2.0 | .woff2 |
恍然大悟,原来是 format
的值写错了!
解决问题
事实上,format
是可以省略的。MDN 对 format
的 说明为
紧跟
url()
值的可选声明,为用户代理提供有关字体格式的提示。如果该值不被支持或无效,浏览器可能不会下载该资源,从而可能节省带宽。如果省略,浏览器将下载资源,然后检测格式。如果为了向后兼容而包含了已定义关键字列表中没有的字体源,请将格式字符串用引号括起来。下面的字体格式部分介绍了可能的值。
主题暂时不支持同时加载多个格式的字体,为快速解决问题,直接省略掉 format
值。同时我使用 woff2_compress
命令行工具和 sfnt2woff
命令行工具,将那位博友的字体转换为 woff/woff2 格式,帮助其快速上线。
关于 MDN 的错误,对原仓库和翻译仓库分别提 PR 以修复:mdn/content#40890、mdn/translated-content#28578。
0