🖼️ 如何解决 SVG 图片中字体失效的问题 |
您所在的位置:网站首页 › css引入字体不显示 › 🖼️ 如何解决 SVG 图片中字体失效的问题 |
如果你喜欢我的文章,希望点赞👍 收藏 📁 评论 💬 三连支持一下,谢谢你,这对我真的很重要! 「SVG 图片中字体失效」的修复方案很简单,只想看答案翻到最后看结论就行。如果想看我的排查思路和具体原因可以从头开始阅读。 起因最近在做项目时,为了兼顾图片的体积和清晰度,部分图片使用 SVG 来展示。但是在实际使用中却发现一个奇怪的现象,SVG 内部的字体失效了。 实际显示预期展示出现问题就要解决问题,首先排查一下代码。 代码中的引用方式很简单,HTML 直接使用 img 标签引用 svg 内容(svg 文件和 HTML 在同一个域下,所以不存在跨域问题): ... ... skychx Inter Font因为用到非 Web 安全字体,我猜测是字体没有下载,于是在 HTML 里加了个 preload(注意这个字体和 HTML SVG 都在同一个域,不存在跨域问题): 这些准备工作都做好后,字体还是没有生效。 排查这时候我意识到可能是命中一些奇怪的浏览器安全限制了,比如说外链引用的 svg 不能共用 HTML 里下载的字体(其实这个猜测已经距离真相很近了),于是换个思路,把字体声明在 svg 文件里不就行了(注意这时候的 svg 和 font 还是属于同一个域,所以也不存在跨域问题): @font-face { font-family: Inter; src: local('Inter'), url('/static/Inter.woff2') format('truetype'); } skychx字体内部声明后,却引发了一个奇怪的现象: svg 作为图片文件被 HTML 里的 img 标签引用时,字体不生效 svg 单独在浏览器中打开,字体是生效的为了排除路径干扰,我还把 font url 改成了绝对路径,Google font 等路径,但还是一样的表现。 ChatGPT 问路既然遇到问题,那为什么不问问神奇海螺 ChatGPT 呢?在我看来这是一个比较常见的问题,它应该会回答的很好(但是没想到这个问题让我绕了两个小时的弯路,这是后话了)。 ChatGPT 给出了很多的答案,列出了以下可能: 字体格式是不是不对 引用路径是否有问题 代码会不会有错误 或者会不会有跨域问题在多轮的问询和尝试下,答案都回归到浏览器安全上,我把前面那几个原因排除后,它就非常笃定是跨域问题,并且再三建议我做跨域检查。 这时候我停下来思考了一下,按道理来说如果出现跨域问题,一般来说 Chrome 还是会 request source,只不过是在浏览器端 block 了 response,而且还会在 Chrome Devtool Console 面板 log 出跨域错误的,但是这些现象都没有;而且前面我也再三确认了这些资源属于同一个域,不应该出现跨域问题。 找到原因既然 ChatGPT 回答不出来,那还是回归 Google 吧.我把关键词输入后,第一条 stackoverflow 就回答了我的疑问: stackoverflow.com/questions/3… 它给出了原因和解决方案:为了浏览器安全,浏览器是不允许 img 引用的 svg 发起网络请求的,把字体以 base64 格式内嵌到 SVG 里可以解决这个问题,看下文的回复,这个方案确实有效。 这个回答只是给出了怎么办,但是并没有给出一手信息来源去解释「为什么」,再经过一些搜索,我找到了 W3C 的相关说明:SVG Security - W3C Wiki,里面说的很清楚: Markup languages like HTML (and SVG itself) can reference SVG as an image with the tag (HTML namespace) or 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 以 标签引用时,SVG 内部引用的外链字体不应该被下载。 解决问题找到原因后就要解决问题,目前主流的解决方案有两种: font rasterization: 把 text 以 path 的格式导出,这样可以保留 font 的轮廓,缺点是随着文字的增加,svg 文件的体积也会线性增长,而且 path 会看着很杂乱,难以维护难以更改 font embedding: 把 font 文件以 base64 格式内嵌到 svg 中,缺点是体积会膨胀不少(font 文件本身就比较大,而且转为 base64 格式还会增加体积)在 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: 下面的表格详细对比了对于同一张资源图,各个方案的文件体积: OriginalFont rasterizationUnoptimized font embeddingNano font embeddingsize18.1KB106KB71.3KB20.1KBgzip4.03KB13.3KB44.3KB12.2KB同样的图片如果以 PNG 格式导出,各分辨率下的图片大小是这样的: PNG @1XPNG @2XPNG @3Xsize38.9KB95.7KB171KBgzip38.2KB88.6KB153KB实际项目中,jpg/png 等图片格式本身已是压缩格式了,再 gzip 压缩一次效果非常有限,再加上 耗,整体其实是负向收益 从上面这个 demo 可以看出,svg 图片在体积上有非常大的优势。 实际使用上,一个 18kb 左右的 SVG 文件,如果使用字体光栅化方案,大概会膨胀到 100kb,这个体积已经和 png 差不多了;如果使用 nano 优化,总体积大概 20kb 左右,增量并不是很大。 从上面可以看出,解决字体内嵌问题后,SVG 作为图片格式还是非常有优势的。 结论「SVG 图片中字体失效」的原因是,出于浏览器安全考虑,SVG 被 HTML 以 标签引用时,SVG 内部引用的外链字体不会被下载,也不会使用外部资源(除了浏览器本身内置的字体),所以会产生个性化字体失效的现象。 目前的最佳解决方案是通过 nano 做一下字体内嵌的处理,保障图片正常显示的同时兼顾文件的小体积。 如果你喜欢我的文章,希望点赞👍 收藏 📁 评论 💬 三连支持一下,谢谢你,这对我真的很重要! 欢迎大家关注我的微信公众号:卤蛋实验室,目前专注前端技术,对图形学也有一些微小研究。 原文链接 👉 🖼️ 如何解决 SVG 图片中字体失效的问题:更新更及时,阅读体验更佳 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |