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