Local Font Access API 提供了一种机制来访问用户本地安装的字体数据,包括名称、样式和字体系列等更高级别的详细信息,以及底层字体文件的原始字节。了解 SVG 编辑应用 Boxy SVG 如何使用此 API。
简介
(本文也可以视频形式提供)。
Boxy SVG 是一款矢量图形编辑器。其主要用例是编辑 SVG 文件格式的绘图,以创建插图、徽标、图标和其他平面设计元素。该应用由波兰开发者 Jarosslaw Foksa 开发,最初于 2013 年 3 月 15 日发布。Jarosław 运营着 Boxy SVG 博客,在其中宣布他向应用添加的新功能。这位开发者是 Chromium 的 Project Fugu 的坚定支持者,甚至在该应用的想法跟踪器上设置了 Fugu 标记。
Boxy SVG 中的 Local Font Access API
Jarosław 在博客中提到的新增功能之一是 Local Font Access API。借助 Local Font Access API,用户可以访问本地安装的字体,包括名称、样式和系列等更高级别的详细信息,以及底层字体文件的原始字节。在下面的屏幕截图中,您可以看到我如何授权该应用访问我的 MacBook 上本地安装的字体,并为我的文本选择 Marker Felt 字体。
底层代码相当简单。当用户首次打开字体系列选择器时,应用会先检查网络浏览器是否支持 Local Font Access API。
此外,它还会检查 API 的旧实验版本,如果存在的话,则使用该版本。从 2023 年开始,您可以放心地忽略旧版 API,因为它仅通过实验性 Chrome 标志提供短期服务,但某些 Chromium 衍生产品可能仍会使用它。
let isLocalFontsApiEnabled = (
// Local Font Access API, Chrome >= 102
window.queryLocalFonts !== undefined ||
// Experimental Local Font Access API, Chrome < 102
navigator.fonts?.query !== undefined
);
如果 Local Font Access API 不可用,字体系列选择器将变灰。系统会向用户显示占位符文本,而不是字体列表:
if (isLocalFontsApiEnabled === false) {
showPlaceholder("no-local-fonts-api");
return;
}
否则,系统会使用 Local Font Access API 从操作系统检索所有字体的列表。请注意 try…catch
块,它是正确处理权限错误所必需的。
let localFonts;
if (isLocalFontsApiEnabled === true) {
try {
// Local Font Access API, Chrome >= 102
if (window.queryLocalFonts) {
localFonts = await window.queryLocalFonts();
}
// Experimental Local Font Access API, Chrome < 102
else if (navigator.fonts?.query) {
localFonts = await navigator.fonts.query({
persistentAccess: true,
});
}
} catch (error) {
showError(error.message, error.name);
}
}
检索到本地字体列表后,系统会根据该列表创建经过简化和标准化的 fontsIndex
:
let fontsIndex = [];
for (let localFont of localFonts) {
let face = "400";
// Determine the face name
{
let subfamily = localFont.style.toLowerCase();
subfamily = subfamily.replaceAll(" ", "");
subfamily = subfamily.replaceAll("-", "");
subfamily = subfamily.replaceAll("_", "");
if (subfamily.includes("thin")) {
face = "100";
} else if (subfamily.includes("extralight")) {
face = "200";
} else if (subfamily.includes("light")) {
face = "300";
} else if (subfamily.includes("medium")) {
face = "500";
} else if (subfamily.includes("semibold")) {
face = "600";
} else if (subfamily.includes("extrabold")) {
face = "800";
} else if (subfamily.includes("ultrabold")) {
face = "900";
} else if (subfamily.includes("bold")) {
face = "700";
}
if (subfamily.includes("italic")) {
face += "i";
}
}
let descriptor = fontsIndex.find((descriptor) => {
return descriptor.family === localFont.family);
});
if (descriptor) {
if (descriptor.faces.includes(face) === false) {
descriptor.faces.push(face);
}
} else {
let descriptor = {
family: localFont.family,
faces: [face],
};
fontsIndex.push(descriptor);
}
}
for (let descriptor of fontsIndex) {
descriptor.faces.sort();
}
然后,标准化字体索引会存储在 IndexedDB 数据库中,以便可以轻松查询、在应用实例之间共享以及在会话之间保留。Boxy SVG 使用 Dexie.js 来管理数据库:
let database = new Dexie("LocalFontsManager");
database.version(1).stores({cache: "family"}).
await database.cache.clear();
await database.cache.bulkPut(fontsIndex);
填充数据库后,字体选择器 widget 可以对其进行查询,并在屏幕上显示结果:
值得注意的是,Boxy SVG 会在名为 <bx-fontfamilypicker>
的自定义元素中呈现列表,并为每个字体列表项设置样式,以便以特定字体系列显示。为了与网页的其余部分隔离开来,Boxy SVG 在此自定义元素和其他自定义元素中使用了 Shadow DOM。
总结
本地字体功能非常受欢迎,用户喜欢在设计和作品中使用本地字体。当 API 形状发生变化并导致功能暂时无法使用时,用户会立即注意到。Jarosław 很快就将代码更改为上述代码段中显示的防御模式,该模式适用于最新版 Chrome 以及可能尚未改用最新版本的其他 Chromium 派生产品。试用一下 Boxy SVG,并务必查看您在本地安装的字体。您可能会发现一些早已被遗忘的经典字体,例如 Zapf Dingbats 或 Webdings。