Skip to content

在 SVG 中使用自定义字体

🏷️ SVG

今天通过 Excalidraw 画了一个 JVM 的内存架构图,但是当放到博客中时,字体的样式没有起作用。

打开 SVG 图片可以看到类似如下的字体样式定义:

xml
<defs>
    <style class="style-fonts">
        @font-face {
            font-family: "Virgil";
            src: url("https://excalidraw.com/Virgil.woff2");
        }
        @font-face {
            font-family: "Cascadia";
            src: url("https://excalidraw.com/Cascadia.woff2");
        }
    </style>
</defs>

在浏览器的标签中单独打开 SVG 图片时,可以正确的显示字体,但是当把 SVG 图片放入到网页中显示时,浏览器并不会加载自定义的字体。

之所以这样是出于安全的考虑,大多数浏览器不允许通过 <img> 标签加载外部资源。 [1]

下面的 No.1 ~ 4 使用 <img> 标签测试了几种 SVG 的显示效果,No.5 ~ 7 则是分别使用 <object><embed><svg> 将 SVG 图片的内容嵌入到了当前页面。另外还可以使用 <iframe> 和 CSS 的 background-image[2]

  1. Excalidraw 导出的原图

    SVG 图片没有应用自定义的字体,但是在单独的标签中打开时字体是对的

  2. 通过 Nano 压缩后的 SVG 文件

    效果和原图一样,只是文件变小了。本以为 Nano 会自动将引用的字体提取出来,结果并没有。

  3. 从原 SVG 文件中删除了字体定部分

    本以为会自动使用页面样式中的字体,但其实并没有。

    Font Family -- Virgil -- Sample

  4. SVG 中直接嵌入 base64 格式的字体,并使用 Nano 压缩后的图

    这种方式字体显示始终是对的,但是文件相对较大。

    Woff2 字体转 base64 工具:Woff2Base

  5. 使用 <object> 标签嵌入 SVG 文件

    字体显示也是对的。

    使用这种嵌入 SVG 的方式时没法使用轮播插件。不知道有没有支持这种嵌入式 SVG 的轮播插件。

    html
    <object type="image/svg+xml" data="jvm-memory.svg"></object>
  6. 使用 <embed> 标签嵌入 SVG 文件

    <object> 标签效果类似。

    不要使用这个标签。虽然很多浏览器都支持,但是这个标签主要用于实现 Flash 插件等。[2:1]

    html
    <embed type="image/svg+xml" src="jvm-memory.svg">
  7. 将 SVG 文件直接嵌入到网页中

    此时 SVG 已变成当前页面的元素,直接使用 @font-face 注册字体即可。

    这种样式貌似不大好控制,图片会显示成原始的尺寸,这里由于设置了最大宽度,所以没有显示全。

    html
    <style class="style-fonts">
        @font-face {
            font-family: "Virgil";
            src: url("https://excalidraw.com/Virgil.woff2");
        }
    </style>
    <svg xmlns="http://www.w3.org/2000/svg" 
        width="981.134" height="745.892"
        xmlns:v="https://vecta.io/nano"
        ...
    </svg>

  1. SVG For Fun and Phishing ↩︎

  2. The Best Way to Embed SVG on HTML (2021) ↩︎ ↩︎