Local Font Access API 提供一種機制,可存取使用者本機安裝的字型資料,包括更高層級的詳細資料 (例如名稱、樣式和系列),以及基礎字型檔案的原始位元組。瞭解 SVG 編輯應用程式 Boxy SVG 如何使用這個 API。
簡介
(這篇文章也可以影片的形式提供)。
Boxy SVG 是向量圖形編輯器。以 SVG 檔案格式編輯繪圖,以便建立插圖、標誌、圖示和其他圖形設計元素。該應用程式是由波蘭開發人員 Jarosław Foksa 所開發,最初發布日期為 2013 年 3 月 15 日。Jarosław 經營 Boxy SVG 網誌,介紹他為應用程式新增的新功能。開發人員不僅是 Chromium Project Fugu 的支持,也在應用程式的創意追蹤工具上安裝 Fugu 標記。
Boxy SVG 中的本機字型存取 API
其中一項功能是 Jarosław 所公布的網誌,是 Local Font Access API。Local Font Access API 可讓使用者存取本機安裝的字型,包括層級更高的詳細資料 (例如名稱、樣式和系列),以及基礎字型檔案的原始位元組。在下方螢幕截圖中,您可以看到我如何授予應用程式存取 MacBook 本機字型的權限,並為我的文字選擇「Marker Felt」字型。
基礎程式碼相當簡單,當使用者首次開啟字型系列挑選器時,應用程式會先檢查網路瀏覽器是否支援 Local Font Access API。
此外,這個 API 也會檢查是否有舊版 API 實驗版本,如果有,則會使用該 API。自 2023 年起,由於舊版 API 只能透過實驗性 Chrome 旗標取得,因此您可以放心忽略舊版 API,但部分 Chromium 衍生工具可能仍會使用該 API。
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);
資料庫填入後,字型選擇器小工具即可查詢該資料庫,並在螢幕上顯示結果:
值得一提的是,Boxy SVG 會在名為 <bx-fontfamilypicker>
的自訂元素中算繪清單,並為各個字型清單項目設定樣式,以便在特定的字型系列中顯示。為了與其他自訂元素區隔開來,Boxy SVG 會使用 Shadow DOM 以及其他自訂元素。
結論
本機字型功能真的很受歡迎,使用者喜歡在設計和創作時選用當地字型。當 API 形狀變更且功能故障時,使用者會立即收到通知。Jarosław 迅速地將程式碼變更成防禦模式,而上面程式碼片段中,可搭配最新版 Chrome,以及其他可能未切換至最新版的 Chromium 衍生工具。旋轉 Boxy SVG 時,請務必查看本機安裝的字型。你可能會發現一些先前不見的經典作品,例如 Zapf Dingbats 或 Webdings。