跳转到内容

能力 · API

Local Font Access API

发布于 更新于

window.queryLocalFonts() 在 local-fonts 权限提示之后,兑现为描述用户设备上已安装字体的 FontData 数组。每个 FontData 暴露 PostScript 名、全名、家族名、样式名,以及返回原始 SFNT 字节的 blob() 方法;设计类工具要用用户自己的字体而不是 Web 字体来排版文字时,需要的正是这些数据。

支持仅限桌面 Chromium:Chrome 103,以及基于它的 Edge 与 Opera 版本。Android 上的 Chrome、Firefox、Safari 的任何版本都没有实现(BCD api.Window.queryLocalFonts);该方法带 [SecureContext],在 localhost 以外的 http:// 源上同样不存在。

window.queryLocalFonts()
window.queryLocalFonts(options)
fontData.blob()

queryLocalFonts() 返回 Promise<sequence<FontData>>,按 postscriptName 升序排列。blob() 返回 Promise<Blob>,其 type 为 application/octet-stream。规范写明浏览器不必报告每一个已安装字体,所以结果是用户代理愿意暴露、并经用户在选择器中(如果浏览器显示选择器)筛选过的集合。

queryLocalFonts() 接受一个可选的 options 参数,类型为只有一个成员的 QueryOptions 字典。

成员 类型 必填 说明
postscriptNames sequence<DOMString> 否 只返回 PostScript 名在列表内的字体,例如 ["Verdana-Bold", "Arial"]。空列表什么都不返回;不传该成员则返回全部可选字体。匹配是精确的,带空格的 "Verdana Bold" 不会匹配任何字体。

返回的每个 FontData 有四个只读 USVString 属性和一个方法。

成员 类型 说明
postscriptName USVString PostScript 名,如 "Arial-Bold";也是结果的排序键。
fullName USVString 家族名加子家族名,如 "Arial Bold"。
family USVString CSS font-family 使用的家族名,如 "Arial"。
style USVString 子家族或样式名,如 "Regular"、"Bold Italic"。
blob() Promise<Blob> 字体文件字节(SFNT 容器:TrueType、OpenType、WOFF 或 WOFF2)。

名称以字体 name 表的美式英语或用户语言本地化字符串单一返回;需要其他语言的页面要自己从 blob() 解析 name 表。

queryLocalFonts() 以下列 DOMException 名称拒绝,顺序即规范的检查顺序。

异常 条件
SecurityError 文档的源是 opaque origin(例如没有 allow-same-origin 的沙箱 iframe);或文档不允许使用 local-fonts 这一受策略控制的特性(默认允许列表为 'self');或调用时没有瞬时用户激活。
NotAllowedError 用户拒绝了 local-fonts 权限提示。

blob() 没有定义任何拒绝情形。Chromium 增加了规范未列出的拒绝:枚举后端缺失时以 NotSupportedError 和 Not yet supported on this platform. 拒绝,标签页隐藏时以 SecurityError 和 Page needs to be visible. 拒绝,DataError 和 Font data exceeds memory limit.,以及其他后端失败时的 UnknownError(权限被拒的情形 Chromium 也另有处理,见实测框)。

两个示例都在 click 处理函数中运行,先检测 window 上的 queryLocalFonts,API 缺失或用户拒绝时回退到页面自带的字体。

用设备字体填充字体选择器,并带一份内置回退列表

Section titled “用设备字体填充字体选择器,并带一份内置回退列表”

回退列表就是页面已经加载的 Web 字体,这样在 Firefox、Safari 或移动端 Chrome 中选择器也不会为空。鉴于上文所述的拒绝行为,Chrome 返回空结果时按同样方式处理。

const BUNDLED = ['Inter', 'Source Serif 4', 'JetBrains Mono'];
async function listFontFamilies() {
if (!('queryLocalFonts' in window)) return BUNDLED;
try {
const fonts = await window.queryLocalFonts();
if (fonts.length === 0) return BUNDLED; // Chrome 中被拒绝,或没有可选字体
return [...new Set(fonts.map((f) => f.family))];
} catch (err) {
if (err.name === 'SecurityError' || err.name === 'NotAllowedError') return BUNDLED;
throw err;
}
}
document.querySelector('#pick-fonts').addEventListener('click', async () => {
const select = document.querySelector('#font-family');
select.replaceChildren(...(await listFontFamilies()).map((name) => new Option(name)));
});

按 family 去重是必要的,因为一个家族的每种字重和样式都是单独一个 FontData:"Arial"、"Arial-Bold"、"Arial-Italic"、"Arial-BoldItalicMT" 是共享家族名 "Arial" 的四个条目。

渲染文档前确认某个特定字体是否存在

Section titled “渲染文档前确认某个特定字体是否存在”

postscriptNames 把提示和结果都收窄到你关心的字体。字体缺失时改用文档内嵌的 Web 字体渲染,并告知用户版面可能有差异。

async function hasLocalFont(postscriptName) {
if (!('queryLocalFonts' in window)) return false;
try {
const matches = await window.queryLocalFonts({ postscriptNames: [postscriptName] });
return matches.length === 1;
} catch {
return false; // 没有手势、被策略拦截,或用户拒绝
}
}
document.querySelector('#open-document').addEventListener('click', async () => {
const useLocal = await hasLocalFont('Verdana-Bold');
document.body.style.fontFamily = useLocal ? '"Verdana"' : '"Verdana Web", sans-serif';
document.querySelector('#font-note').hidden = useLocal;
});

只有当你必须自己读取字形轮廓或 name 表时(canvas 文本编辑器、字体检查器)才需要 blob();CSS 按家族名引用已安装字体并不需要它。

规范

规范状态
Local Font Access API: queryLocalFonts() methodWICG 草案
Local Font Access API: FontData interfaceWICG 草案