For the complete documentation index, see llms.txt. This page is also available as Markdown.

Electron后台静默运行本地字体HTTP服务

设计工具、画布编辑器、在线海报工具经常会遇到一个问题:浏览器页面不能直接读取用户本机字体文件。

如果只是列出字体名,可以依赖浏览器已有字体渲染。但如果要做字体预览、字体文件加载、服务端生成图片,或者设计器里读取本机字体,就需要一个本地辅助服务。

我这里的方案是用 Electron 启动一个后台 HTTP 服务:

  • 应用启动后不显示窗口。

  • 默认监听 127.0.0.1:3838

  • 提供字体列表接口。

  • 下载字体文件时做路径校验。

  • 支持开机自启。

  • macOS 下自动尝试清除下载隔离属性。

字体目录扫描

不同系统的字体目录不一样,可以先按平台区分:

function getSystemFonts({ forceRefresh = false } = {}) {
  const fonts = [];
  const seen = new Set();
  const platform = process.platform;

  if (platform === "darwin") {
    const systemDirs = ["/System/Library/Fonts", "/Library/Fonts"];
    const userDirs = [path.join(os.homedir(), "Library/Fonts")];
    systemDirs.forEach((dir) => fonts.push(...scanFontsDir(dir, true, seen)));
    userDirs.forEach((dir) => fonts.push(...scanFontsDir(dir, false, seen)));
  } else if (platform === "win32") {
    const systemDir = path.join(process.env.WINDIR || "C:\\Windows", "Fonts");
    const userDir = path.join(os.homedir(), "AppData", "Local", "Microsoft", "Windows", "Fonts");
    fonts.push(...scanFontsDir(systemDir, true, seen));
    fonts.push(...scanFontsDir(userDir, false, seen));
  } else {
    const systemDirs = ["/usr/share/fonts", "/usr/local/share/fonts"];
    const userDirs = [path.join(os.homedir(), ".fonts")];
    systemDirs.forEach((dir) => fonts.push(...scanFontsDir(dir, true, seen)));
    userDirs.forEach((dir) => fonts.push(...scanFontsDir(dir, false, seen)));
  }

  return fonts;
}

递归扫描时只收集字体文件:

加一层缓存

字体目录扫描可能比较慢,尤其 Windows 字体目录文件多时。可以加一个短缓存:

接口里可以通过 ?refresh=1 强制刷新:

下载字体文件必须做白名单校验

不要因为本地服务就直接 sendFile(req.query.path),否则会变成任意文件读取。

正确做法是:传入路径后,先确认它存在于扫描出的字体列表中。

Electron 静默启动

Electron 主进程里不创建窗口,只启动服务:

macOS 隔离属性处理

没有签名的 Electron App,从浏览器下载后 macOS 可能提示“已损坏”。如果是 DMG 拖拽安装,没有安装脚本可以执行,所以可以在应用启动时尝试清除自身隔离属性:

总结

本地字体服务看起来是一个小工具,但里面有几个工程点很值得注意:

  • 只监听 127.0.0.1,避免暴露到局域网。

  • 下载字体文件前必须做白名单校验。

  • 字体扫描要做缓存。

  • Electron 要做单实例运行。

  • macOS/Windows 打包安装体验要单独处理。

这类本地辅助服务很适合配合网页设计器、低代码编辑器、海报工具使用。