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 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.

Aplikacja Boxy SVG edytuje ikonę projektu Fugu w formacie SVG.

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.

Aplikacja Boxy SVG edytuje ikonę SVG projektu Fugu i dodaje tekst „Project Fugu Rocks” w polu znacznika czcionki Felt, który jest zaznaczony w selektorze czcionek.

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;
}

Wybieranie czcionki – komunikat „Twoja przeglądarka nie obsługuje interfejsu API Local Font Access”

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);

Sekcja Pamięć w Narzędziach deweloperskich w Chrome, która pokazuje tabelę IndexedDB z pamięcią podręczną czcionek.

Gdy baza danych zostanie wypełniona, widżet selektora czcionek może wysłać zapytanie do bazy i wyświetlić wyniki na ekranie:

Selektor czcionek z czcionkami.

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.

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ą, 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.