如果你喜爱我的文章,心愿点赞👍 珍藏 📁 评论 💬 三连反对一下,谢谢你,这对我真的很重要!
「SVG 图片中字体生效」的修复计划很简略,只想看答案翻到最初看论断就行。如果想看我的排查思路和具体起因能够从头开始浏览。
起因
最近在做我的项目时,为了兼顾图片的体积和清晰度,局部图片应用 SVG 来展现。然而在理论应用中却发现一个奇怪的景象,SVG 外部的字体生效了。
理论显示 | 预期展现 |
---|---|
呈现问题就要解决问题,首先排查一下代码。
代码中的援用形式很简略,HTML 间接应用 img 标签援用 svg 内容(svg 文件和 HTML 在同一个域下,所以不存在跨域问题):
<p>...</p>
<img src="/static/skychx.svg">
<p>...</p>
<!-- 能够看到 SVG 应用了 Inter 字体,这个字体不是 Web 平安字体 -->
<svg>
<text x="0" y="15" fill="#F2F2F2">skychx Inter Font</text>
</svg>
因为用到非 Web 平安字体,我猜想是字体没有下载,于是在 HTML 里加了个 preload
(留神这个字体和 HTML SVG 都在同一个域,不存在跨域问题):
<head>
<link rel="preload" href="/static/Inter.woff2" as="font" type="font/woff2" crossorigin>
</head>
这些筹备工作都做好后,字体还是没有失效。
排查
这时候我意识到可能是命中一些奇怪的浏览器平安限度了,比如说外链援用的 svg 不能共用 HTML 里下载的字体(其实这个猜想曾经间隔假相很近了),于是换个思路,把字体申明在 svg 文件里不就行了(留神这时候的 svg 和 font 还是属于同一个域,所以也不存在跨域问题):
<svg>
<defs>
<style type="text/css">
@font-face {
font-family: Inter;
src: local('Inter'), url('/static/Inter.woff2') format('truetype');
}
</style>
</defs>
<text x="0" y="15" fill="red">skychx</text>
</svg>
字体外部申明后,却引发了一个奇怪的景象:
- svg 作为图片文件被 HTML 里的 img 标签援用时,字体不失效
- svg 独自在浏览器中关上,字体是失效的
为了排除门路烦扰,我还把 font url 改成了绝对路径,Google font 等门路,但还是一样的体现。
ChatGPT 问路
既然遇到问题,那为什么不问问神奇海螺 ChatGPT 呢?在我看来这是一个比拟常见的问题,它应该会答复的很好(然而没想到这个问题让我绕了两个小时的弯路,这是后话了)。
ChatGPT 给出了很多的答案,列出了以下可能:
- 字体格局是不是不对
- 援用门路是否有问题
- 代码会不会有谬误
- 或者会不会有跨域问题
在多轮的问询和尝试下,答案都回归到浏览器平安上,我把后面那几个起因排除后,它就十分笃定是跨域问题,并且再三倡议我做跨域查看。
这时候我停下来思考了一下,按情理来说如果呈现跨域问题,一般来说 Chrome 还是会 request source,只不过是在浏览器端 block 了 response,而且还会在 Chrome Devtool Console 面板 log 出跨域谬误的,然而这些景象都没有;而且后面我也再三确认了这些资源属于同一个域,不应该呈现跨域问题。
找到起因
既然 ChatGPT 答复不进去,那还是回归 Google 吧.我把关键词输出后,第一条 stackoverflow 就答复了我的疑难:
https://stackoverflow.com/questions/30466610/svg-doesnt-use-font-when-inside-html
它给出了起因和解决方案:为了浏览器平安,浏览器是不容许 img 援用的 svg 发动网络申请的,把字体以 base64 格局内嵌到 SVG 里能够解决这个问题,看下文的回复,这个计划的确无效。
这个答复只是给出了怎么办,然而并没有给出一手信息起源去解释「为什么」,再通过一些搜寻,我找到了 W3C 的相干阐明:SVG Security – W3C Wiki,外面说的很分明:
Markup languages like HTML (and SVG itself) can reference SVG as an image with the \<img> tag (HTML namespace) or \<image> tag (HTML or SVG namespace).
If an SVG cos.ap-shanghai is fetched as image, then certain requirements apply to this document:
Fonts shouldn’t be loaded as well.
就是说出于浏览器平安思考,SVG 被 HTML 以 <img>
标签援用时,SVG 外部援用的外链字体不应该被下载。
解决问题
找到起因后就要解决问题,目前支流的解决方案有两种:
- font rasterization: 把 text 以 path 的格局导出,这样能够保留 font 的轮廓,毛病是随着文字的减少,svg 文件的体积也会线性增长,而且 path 会看着很芜杂,难以保护难以更改
- font embedding: 把 font 文件以 base64 格局内嵌到 svg 中,毛病是体积会收缩不少(font 文件自身就比拟大,而且转为 base64 格局还会减少体积)
<!– ![两者格局的体积比照/path 和 text 的维护性比照]() –>
在 figma 中导出文件为 svg 时,勾选
Outline Text
将会以 path 模式导出文字,不勾选将会以 text 模式导出文字
好在我又进行了一些关联搜寻,找到一个不错的收费 SVG 压缩工具:nano,完满解决我的问题。
在他们的 Blog: Making SVG Easier to Use and the Reason We Built Nano 里,对下面两个解决方案做了更具体的比照,我挑一些我感觉有意思的点说一下。
应用 font rasterization 计划后,除了我之前说的问题,还有一个问题是在低分辨率的屏幕上,无奈应用操作系统/浏览器专门对字体做的优化,这会导致字体清晰度出问题,最直观的感触就是字体会发虚:
字体文件内嵌到 svg 导致的体积收缩问题,他们给出的计划是字体裁剪。
nano 会先剖析 svg 文件中应用的 文字/字体/字重,而后会对字体做裁剪,最初把裁剪后的字体转为 base64 内嵌到 SVG 中。
比如说你只用了 Inter 字体的 “skychx” 6 个字符,那么它会对 Inter 字体做裁剪,只把应用的 6 个字符的字体拿进去,其它字母和字重的字体都会被拆剪掉,最初转为 base64,这样做字体体积会大大缩减。
上面是它们的一个测试 demo:
上面的表格具体比照了对于同一张资源图,各个计划的文件体积:
Original | Font rasterization | Unoptimized font embedding | Nano font embedding | |
---|---|---|---|---|
size | 18.1KB | 106KB | 71.3KB | 20.1KB |
gzip | 4.03KB | 13.3KB | 44.3KB | 12.2KB |
同样的图片如果以 PNG 格局导出,各分辨率下的图片大小是这样的:
PNG @1X | PNG @2X | PNG @3X | |
---|---|---|---|
size | 38.9KB | 95.7KB | 171KB |
gzip | 38.2KB | 88.6KB | 153KB |
理论我的项目中,jpg/png 等图片格式自身已是压缩格局了,再 gzip 压缩一次成果十分无限,再加上 耗,整体其实是负向收益
从下面这个 demo 能够看出,svg 图片在体积上有十分大的劣势。
理论应用上,一个 18kb 左右的 SVG 文件,如果应用字体光栅化计划,大略会收缩到 100kb,这个体积曾经和 png 差不多了;如果应用 nano 优化,总体积大略 20kb 左右,增量并不是很大。
从下面能够看出,解决字体内嵌问题后,SVG 作为图片格式还是十分有劣势的。
论断
「SVG 图片中字体生效」的起因是,出于浏览器平安思考,SVG 被 HTML 以 <img>
标签援用时,SVG 外部援用的外链字体不会被下载,也不会应用内部资源(除了浏览器自身内置的字体),所以会产生个性化字体生效的景象。
目前的最佳解决方案是通过 nano 做一下字体内嵌的解决,保障图片失常显示的同时兼顾文件的小体积。
如果你喜爱我的文章,心愿点赞👍 珍藏 📁 评论 💬 三连反对一下,谢谢你,这对我真的很重要!
欢送大家关注我的微信公众号:卤蛋实验室,目前专一前端技术,对图形学也有一些渺小钻研。
原文链接 👉 🖼️ 如何解决 SVG 图片中字体生效的问题:更新更及时,浏览体验更佳
发表回复