svg 与字体

背景

继上一篇 《svg 图片的上传与下载》后,产品要求下载的 svg 需要使用 “OCR-B 10 BT” 这个字体。下面记录下遇到的坑。

实现

jsbarcode 这个库提供了一个 font 配置项。例如这里配置

JsBarcode(svg, barcode, {
  font: 'Arial'
});

这个配置的作用是在 svg 的 text 节点的 style 属性上添加font:Arial

那么问题如果是 OCR-B 10 BT,我们直接配置成

JsBarcode(svg, barcode, {
  font: 'OCR-B 10 BT'
});

那么结果会变成:

<text style="font: OCR-B 10 BT">xxx</text>

这将会导致无法识别出 font 的属性,而 jsbarcode 并没有单独提供 font-family 配置。正确的写法可以如下

JsBarcode(svg, barcode, {
  font: `"OCR-B 10 BT"`
});

到了这一步并没有完全解决问题,对于系统有安装过 OCR-B 10 BT 字体的用户是可以正常展示下载的 svg,然而对于没有安装过这个字体的则会展示默认字体。这里分成两种场景:1. 在我们的网站上展示 2. 下载后的 svg 展示

对于第一个场景,在 web 通过 font-face 引入字体文件

@font-face {
    font-family: 'OCR-B 10 BT';
    src: url('fonts/webfont.eot');
    src:url('fonts/webfont.woff') format('woff'), 
      	url('fonts/webfont.woff2') format('woff2'),
        url('fonts/webfont.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}

然而在第二个场景则需要将字体文件放到 svg 里面,注意这个会导致 svg 体积增大。

首先我们需要在 svg 中定义 style:

const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
const style = document.createElementNS('http://www.w3.org/2000/svg', 'style');
style.type = 'text/css';
// myFontStyle 是我们定义的样式,实际上就是一段定义 font-face 的字符串
style.innerHTML = myFontStyle;
defs.appendChild(style);
svgElement.appendChild(defs);

可以通过 https://www.fontsquirrel.com/tools/webfont-generator 这里选择 EXPERT 模式,在 css 的勾选栏选中 Base64 编码,生成的 zip 包中的 css 文件内容就包含我们编码成 base64 后的样式。将 myFontStyle 替换成 文件内容即可。

至此给 svg 定义字体的需求就算是完成了,不过真的不推荐将字体塞进 svg 中,否则文件体积将会飙升 ☠️