Jak aplikacja Boxy SVG do edycji obrazów wektorowych korzysta z interfejsu Local Font Access API, aby umożliwić użytkownikom wybór ulubionych czcionek lokalnych

Interfejs Local Font Access API umożliwia dostęp do lokalnie zainstalowanych przez użytkownika danych dotyczących czcionek. Dotyczy to m.in. szczegółowych informacji wyższego poziomu, takich jak nazwy, style i rodziny, a także nieprzetworzonych bajtów plików czcionek. Dowiedz się, jak aplikacja do edycji SVG Boxy SVG wykorzystuje ten interfejs API.

Wstęp

(Ten artykuł jest również dostępny w formie filmu).

Boxy SVG to edytor grafiki wektorowej. Jej głównym zastosowaniem jest edytowanie rysunków w formacie pliku SVG, co pozwala tworzyć ilustracje, logo, ikony i inne elementy projektów graficznych. Jest on opracowany przez polskiego dewelopera Jarosława Foksę i opublikowany 15 marca 2013 roku. Jarosław prowadzi bloga w formacie Boxy SVG, na którym przedstawia nowe funkcje, które dodaje do aplikacji. Deweloper zdecydowanie wspiera projekt Fugu w Chromium, a w narzędziu do śledzenia pomysłów aplikacji umieścił nawet tag Fugu.

Aplikacja Boxy SVG edytująca ikonę SVG Project Fugu.

Interfejs Local Font Access API w formacie SVG Boxy

Do nowych funkcji, o których pisze Jarosław, dołączył interfejs Local Font Access API. Interfejs Local Font Access API umożliwia użytkownikom dostęp do lokalnie zainstalowanych czcionek, w tym do informacji wyższego poziomu, takich jak nazwy, style i rodziny, a także do nieprzetworzonych bajtów plików czcionek. Na poniższym zrzucie ekranu możesz zobaczyć, jak przyznałem aplikacji dostęp do lokalnie zainstalowanych czcionek na moim MacBooku i wybrałem czcionkę znacznika Fil w tekście.

Aplikacja Boxy SVG edytuje ikonę SVG Project Fugu i dodaje tekst „Project Fugu rocks” (skały projektu Project Fugu) w zaznaczonym selektorze czcionek.

Podstawowy kod jest dość przejrzysty. Gdy użytkownik po raz pierwszy otworzy selektor rodziny czcionek, aplikacja najpierw sprawdzi, czy przeglądarka obsługuje interfejs Local Font Access API.

Sprawdza też starą, eksperymentalną wersję interfejsu API i używa jej, jeśli jest dostępna. Od 2023 roku 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 jego pochodne mogą być nadal używane.

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 Local Font Access API jest niedostępny, selektor rodziny czcionek zmieni kolor na szary. Zamiast listy czcionek użytkownik zobaczy tekst zastępczy:

if (isLocalFontsApiEnabled === false) {
  showPlaceholder("no-local-fonts-api");
  return;
}

Selektor czcionek z komunikatem „Twoja przeglądarka nie obsługuje interfejsu Local Font Access API”.

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 niezbędny do prawidłowej obsługi 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 czcionek lokalnych tworzony jest na jej podstawie uproszczony i znormalizowany 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();
}

Znormalizowany indeks czcionek jest następnie przechowywany w bazie danych IndexedDB, aby można go było łatwo wysyłać zapytania, udostępniać między instancjami aplikacji i zachowywać między sesjami. Boxy SVG zarządza bazą danych za pomocą Dexie.js:

let database = new Dexie("LocalFontsManager");
database.version(1).stores({cache: "family"}).
await database.cache.clear();
await database.cache.bulkPut(fontsIndex);

Sekcja Pamięć w Narzędziach deweloperskich w Chrome zawierająca tabelę IndexedDB z pamięcią podręczną czcionek.

Po zapełnieniu bazy danych widżet selektora czcionek może wysyłać do niej zapytania i wyświetlać wyniki na ekranie:

Selektor czcionek wypełniony czcionkami.

Warto wspomnieć, że Boxy SVG renderuje listę w elemencie niestandardowym o nazwie <bx-fontfamilypicker> i stylizuje każdy element listy czcionek, tak aby był wyświetlany w określonej rodzinie czcionek. Aby odizolować od reszty strony, obiekt Boxy SVG używa Shadow DOM w tym i innych elementach niestandardowych.

Panel elementów Narzędzi deweloperskich w Chrome przedstawiający sprawdzany selektor czcionek: element niestandardowy o nazwie „bx-fontfamiliypicker”.

Podsumowanie

Funkcja czcionek lokalnych cieszy się dużą popularnością wśród użytkowników, którzy chcą korzystać z lokalnych czcionek w swoich projektach i pracach. Gdy kształt interfejsu API zmienił się i funkcja na krótko się zepsuła, użytkownicy natychmiast to zauważyli. Jarosław szybko zmienił kod na wzór defensywny widoczny we fragmencie powyżej, który działa z najnowszą wersją Chrome, a także z innymi urządzeniami pochodnymi Chromium, które mogły nie zostać zaktualizowane do najnowszej wersji. Wypróbuj funkcję Boxy SVG i zapoznaj się z zainstalowanymi lokalnie czcionkami. Być może odkryjesz dawno zapomniane klasyki, takie jak Zapf Dingbats czy Webdings.