API Local Font Access предоставляет механизм доступа к локально установленным данным шрифтов пользователя, включая более высокоуровневые данные, такие как имена, стили и семейства, а также необработанные байты базовых файлов шрифтов. Узнайте, как приложение для редактирования SVG Boxy SVG использует этот API.
Введение
(Эта статья также доступна в виде видео.)
Boxy SVG — векторный графический редактор. Его основная сфера применения — редактирование рисунков в формате SVG для создания иллюстраций, логотипов, иконок и других элементов графического дизайна. Он разработан польским разработчиком Ярославом Фоксой и был первоначально выпущен 15 марта 2013 года. Ярослав ведет блог Boxy SVG, в котором он объявляет о новых функциях, которые он добавляет в приложение. Разработчик является ярым сторонником проекта Fugu от Chromium и даже имеет тег Fugu на трекере идей приложения.
API локального доступа к шрифтам в Boxy SVG
Одной из дополнительных функций, о которой писал Ярослав в своем блоге, был Local Font Access API . Local Font Access API позволяет пользователям получать доступ к локально установленным шрифтам, включая более высокоуровневые данные, такие как имена, стили и семейства, а также необработанные байты базовых файлов шрифтов. На следующем снимке экрана вы можете увидеть, как я предоставил приложению доступ к локально установленным шрифтам на моем MacBook и выбрал шрифт Marker Felt для своего текста.
Базовый код довольно прост. Когда пользователь впервые открывает средство выбора семейства шрифтов, приложение сначала проверяет, поддерживает ли веб-браузер API Local Font Access.
Он также проверяет наличие старой экспериментальной версии 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
);
Если API Local Font Access недоступен, то селектор семейства шрифтов станет серым. Вместо списка шрифтов пользователю будет показан текст-заполнитель:
if (isLocalFontsApiEnabled === false) {
showPlaceholder("no-local-fonts-api");
return;
}
В противном случае используется API Local Font Access для получения списка всех шрифтов из операционной системы. Обратите внимание на блок 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 изменилась и функция на короткое время сломалась , пользователи сразу это заметили. Ярослав быстро изменил код на защитный шаблон, который вы можете видеть в фрагменте выше, который работает с обновленным Chrome, а также с другими производными Chromium, которые, возможно, не перешли на последнюю версию. Возьмите Boxy SVG на пробу и обязательно проверьте свои локально установленные шрифты. Вы можете обнаружить некоторые давно забытые классические, такие как Zapf Dingbats или Webdings .