一、前言
Excalidraw 是一款开源的在线白板工具。简单美观又好用,我自己博客里面大多数图都是用它画的。
不过有一点我不喜欢,Excalidraw 只自带 4 种字体,其中的中文手绘字体我又觉得不好看😫。而且不支持从前端直接导入自定义字体,想要添加字体只能自己改源码,好麻烦😭。

网上找的一些教程好像是老版本的,和现在的 Excalidraw 源码的项目结构有出入,对不上,只能自己照着代码改了😥。
这里记录如果修改源码添加自定义字体,然后构建容器镜像,使用 Docker 部署到自己的服务器上。
二、修改源码
1、克隆仓库到本地
Excalidraw 官方 GitHub 仓库:excalidraw/excalidraw: Virtual whiteboard for sketching hand-drawn like diagrams 。
选择一个工作目录打开命令行,使用 git clone https://github.com/excalidraw/excalidraw.git 将仓库克隆到本地。

2、在源代码中添加自定义字体
这里我使用添加霞鹜文楷作为示例。
霞鹜文楷的 GitHub 仓库地址:lxgw/LxgwWenKai: An unprofessional open-source Chinese font derived from Fontworks' Klee One. 一款非专业的开源中文字体,基于 FONTWORKS 出品字体 Klee One 衍生 。
在 Release 中下载 .ttf 字体文件(Light / Regular / Medium 代表 细 / 中 / 粗,Mono 代表等宽一般作为代码字体)。

将下载的 .ttf 格式字体文件转换为 .woff2 的,TTF to WOFF2 | CloudConvert 我用的这个网站转换的。
创建 packages\excalidraw\fonts\LXGWWenKai 目录,将转换好的 LXGWWenKai-Regular.woff2 文件放进去。
同时在目录下创建一个 index.ts 的文件,导出字体,内容如下(参考 packages\excalidraw\fonts\Virgil\index.ts 文件的写法):
import { type ExcalidrawFontFaceDescriptor } from "../Fonts";
import LXGWWenKai from "./LXGWWenKai-Regular.woff2";
export const LXGWWenKaiFontFaces: ExcalidrawFontFaceDescriptor[] = [
{
uri: LXGWWenKai,
},
];

打开 packages\common\src\constants.ts,找到 FONT_FAMILY,添加字体枚举:
export const FONT_FAMILY = {
Virgil: 1,
Helvetica: 2,
Cascadia: 3,
// leave 4 unused as it was historically used for Assistant (which we don't use anymore) or custom font (Obsidian)
Excalifont: 5,
Nunito: 6,
"Lilita One": 7,
"Comic Shanns": 8,
"Liberation Sans": 9,
Assistant: 10,
LXGWWenKai: 11,
};
数字随便选一个不冲突的就行。

在 packages\excalidraw\fonts\Fonts.ts 中注册字体:
-
顶部 import 导入字体 :
import { CascadiaFontFaces } from "./Cascadia"; import { ComicShannsFontFaces } from "./ComicShanns"; import { EmojiFontFaces } from "./Emoji"; import { ExcalidrawFontFace } from "./ExcalidrawFontFace"; import { ExcalifontFontFaces } from "./Excalifont"; import { HelveticaFontFaces } from "./Helvetica"; import { LiberationFontFaces } from "./Liberation"; import { LilitaFontFaces } from "./Lilita"; import { NunitoFontFaces } from "./Nunito"; import { VirgilFontFaces } from "./Virgil"; import { XiaolaiFontFaces } from "./Xiaolai"; import { LXGWWenKaiFontFaces } from "./LXGWWenKai";
-
找到其他字体被注册初始化的地方,照着注册字体:
init("Cascadia", ...CascadiaFontFaces); init("Comic Shanns", ...ComicShannsFontFaces); init("Excalifont", ...ExcalifontFontFaces); // keeping for backwards compatibility reasons, uses system font (Helvetica on MacOS, Arial on Win) init("Helvetica", ...HelveticaFontFaces); // used for server-side pdf & png export instead of helvetica (technically does not need metrics, but kept in for consistency) init("Liberation Sans", ...LiberationFontFaces); init("Lilita One", ...LilitaFontFaces); init("Nunito", ...NunitoFontFaces); init("Virgil", ...VirgilFontFaces); init("LXGWWenKai", ...LXGWWenKaiFontFaces);
这样就可以了,测试一下😋。
3、运行项目
这时候运行的项目是在开发环境运行用于测试的,不是最终的部署运行,如果你不想测试或者不想安装测试环境也可以跳过这一步(你真的相信我写的没错的话🐦⬛)。
运行项目需要安装 Node.js 环境。
Excalidraw 项目使用 yarn 管理,所以还需要安装 yarn:npm install -g yarn 。
项目目录下运行 yarn 命令安装依赖,再使用 yarn start 命令启动项目。

打开浏览器 localhost:3000 查看添加的字体。

完美🥳🥳🥳!
三、Docker 部署
1、构建镜像
需要安装 Docker。如果在 Windows 电脑上开发一般是没有安装 Docker 的,需要下载 Docker Desktop(我没找到 Windows 平台上的纯命令行版本🧐)。虽然最后部署的 Linux 服务器上肯定是安装过 Docker 的,可以把项目传到服务器上构建,但问题是构建过程太吃内存了,我试的时候(我的服务器是 4 核 4 G)直接就死机了😭。
如果是在 Windows 上使用 Docker Desktop 还有一个问题,Docker 的一些网站被墙了......需要换源才能正常使用。点右上角的设置,找到 Docker Engine,把镜像网站填进去。这里我就找到了 https://docker.1ms.run 这一个地址能用😥,如果挂了自己再去网上找找把。

进入项目目录,命令行中构建镜像(这个版本号随便填,可以和 Excalidraw GitHub 仓库中最新 Release 的版本保持一致,像我克隆镜像时最新 Release 版本是 1.5.21,就使用 docker build -t excalidraw:1.5.21 .,以后更新可以 git 合并一下分支再重新构建新的镜像)。
docker build -t /excalidraw:<版本号> .

2、上传镜像
把镜像上传到 Docker Hub 中。Docker Desktop 登录账号,在命令行中给镜像打标签。
docker tag excalidraw:<版本号> <用户名>/excalidraw:latest
给镜像添加前缀,告诉 Docker 将镜像上传到哪个账号,并且打上 latest 标签,代表最新版本。然后直接使用 Docker Desktop 上传。


3、下载镜像
登录自己的 Linux 服务器,拉取镜像。
docker pull ezekielx/excalidraw:latest
运行容器。
docker run -d -p <外部访问端口>:80 --name Excalidraw ezekielx/excalidraw:latest

绑定域名访问,运行成功了😋。
