Interfejs API Local Font Access zapewnia mechanizm dostępu do danych czcionek zainstalowanych lokalnie przez użytkownika, w tym szczegółów na wyższym poziomie, takich jak nazwy, style i rodziny, a także surowych bajtów z podstawowych plików czcionek. Dowiedz się, jak z tego interfejsu API korzysta aplikacja Boxy SVG do edycji SVG.
Wprowadzenie
(Ten artykuł jest też dostępny w formie filmu).
Boxy SVG to edytor grafiki wektorowej. Głównym zastosowaniem jest edytowanie rysunków w formacie pliku SVG do tworzenia ilustracji, logo, ikon i innych elementów projektu graficznego. Został on stworzony przez polskiego dewelopera, Jarosława Foksę, a podwyższono go do premiery 15 marca 2013 roku. Prowadzi bloga Boxy SVG, w którym ogłasza nowe funkcje dodawane do aplikacji. Jest też gorącym zwolennikiem projektu Fugu w Chromium i ma nawet tag Fugu w treściach z kategorii pomysłów w aplikacji.
Interfejs API dostępu do czcionek lokalnych w Boxy SVG
Jednym z dodanych przez Jarosława postów na blogu był ten o interfejsie Local Fonts Access API. Interfejs API Local Font Access umożliwia użytkownikom dostęp do lokalnie zainstalowanych czcionek, w tym szczegółów wyższego poziomu, takich jak nazwy, style i rodziny, a także nieprzetworzonych bajtów podstawowych plików czcionek. Na poniższym zrzucie ekranu widać, jak przyznałem aplikacji dostęp do czcionek zainstalowanych lokalnie na moim MacBooku i wybrałem czcionkę Marker Felt dla tekstu.
Kod źródłowy jest dość prosty. Gdy użytkownik po raz pierwszy otworzy selektor rodziny czcionek, aplikacja najpierw sprawdza, czy przeglądarka obsługuje interfejs Local Font Access API.
Sprawdza też, czy jest dostępna starsza wersja eksperymentalna interfejsu API, i jeśli tak, to z niej korzysta. Od 2023 r. możesz bezpiecznie zignorować stary interfejs API, ponieważ był on dostępny tylko przez krótki czas za pomocą eksperymentalnych flag Chrome, ale niektóre pochodne Chromium mogą go nadal używać.
let isLocalFontsApiEnabled = (
// Local Font Access API, Chrome >= 102
window.queryLocalFonts !== undefined ||
// Experimental Local Font Access API, Chrome < 102
navigator.fonts?.query !== undefined
);
Jeśli interfejs API Local Font Access jest niedostępny, selektor rodziny czcionek stanie się szary. Zamiast listy czcionek użytkownik zobaczy tekst zastępczy:
if (isLocalFontsApiEnabled === false) {
showPlaceholder("no-local-fonts-api");
return;
}
W przeciwnym razie do pobrania listy wszystkich czcionek z systemu operacyjnego używany jest interfejs Local Font Access API. Zwróć uwagę na blok try…catch
, który jest potrzebny do prawidłowego obsługiwania błędów uprawnień.
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);
}
}
Po pobraniu listy lokalnych czcionek tworzona jest z niej uproszczona i unormowana 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();
}
Normalizowany indeks czcionek jest następnie przechowywany w bazie danych IndexedDB, aby można było łatwo wysyłać zapytania o dane, udostępniać je między instancjami aplikacji i przechowywać między sesjami. Boxy SVG do zarządzania bazą danych używa Dexie.js:
let database = new Dexie("LocalFontsManager");
database.version(1).stores({cache: "family"}).
await database.cache.clear();
await database.cache.bulkPut(fontsIndex);
Gdy baza danych zostanie wypełniona, widżet selektora czcionek może wysłać zapytanie do bazy i wyświetlić wyniki na ekranie:
Warto wspomnieć, że Boxy SVG renderuje listę w elemencie niestandardowym o nazwie <bx-fontfamilypicker>
i stylizuje każdy element listy czcionek, aby wyświetlał się w określonej rodzinie czcionek. Aby odizolować go od reszty strony, Boxy SVG używa w tym i innych elementach niestandardowych modelu Shadow DOM.
Podsumowanie
Funkcja czcionek lokalnych cieszy się dużą popularnością, ponieważ użytkownicy chętnie korzystają z dostępu do czcionek lokalnych w swoich projektach i utworach. Użytkownicy od razu informowali o tym, że zmienił się kształt interfejsu API i funkcja nie działała na krótko. Jarosław szybko zmienił kod na wzór obronny, który widać w powyższym fragmencie kodu. Działa on z aktualną wersją Chrome, a także z innymi pochodnymi Chromium, które nie zostały jeszcze przełączone na najnowszą wersję. Przetestuj szybko zainstalowane czcionki Boxy i zapoznaj się z lokalnie zainstalowanymi czcionkami. Możesz odkryć dawno zapomniane klasyki, takie jak Zapf Dingbats czy Webdings.