Używanie zaawansowanej typografii z czcionkami lokalnymi

Dowiedz się, jak interfejs Local Font Access API umożliwia dostęp do czcionek zainstalowanych lokalnie użytkownika i uzyskiwanie szczegółowych informacji na ich temat

Czcionki bezpieczne dla internetu

Jeśli zajmujesz się tworzeniem stron internetowych wystarczająco długo, pamiętasz o tzw. czcionkach bezpiecznych do przeglądania internetu. Te czcionki są dostępne w niemal wszystkich instancjach najczęściej używanych systemów operacyjnych (Windows, macOS, najpopularniejsze dystrybucje Linuksa, Android i iOS). Na początku XXI wieku firma Microsoft wyprzedziła nawet inicjatywę o nazwie TrueType Core Fonts for the Web, która udostępniała te czcionki do bezpłatnego pobrania, z celem, że „za każdym razem, gdy wejdziesz na stronę, która je zawiera, zobaczysz strony dokładnie tak, jak powinny”. Tak, obejmuje to witryny umieszczone w Comic Sans MS. Oto klasyczny stos czcionek internetowych (z ostatecznym rozwiązaniem zastępczym dowolnej czcionki sans-serif) może wyglądać tak:

body {
  font-family: Helvetica, Arial, sans-serif;
}

Czcionki internetowe

Czasy, w których bezpieczne czcionki internetowe były tak istotne, dawno minęły. Obecnie mamy czcionki internetowe. Niektóre z nich to nawet czcionki zmienne, które możemy dostosować, zmieniając wartości w różnych odsłoniętych osiach. Aby używać czcionek internetowych, zadeklaruj na początku kodu CSS blok @font-face określający pliki czcionek do pobrania:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: url('flamboyant.woff2');
}

Po tym czasie możesz korzystać z niestandardowej czcionki internetowej, określając w zwykły sposób font-family:

body {
  font-family: 'FlamboyantSansSerif';
}

Czcionki lokalne jako wektory odcisku palca

Większość czcionek internetowych pochodzi z internetu. Ciekawe jest jednak to, że właściwość src w deklaracji @font-face, oprócz funkcji url(), akceptuje również funkcję local(). Dzięki temu czcionki niestandardowe mogą być ładowane (niespodzianka) lokalnie. Jeśli w systemie operacyjnym użytkownika będzie zainstalowany plik FlamboyantSansSerif, zostanie użyta jego kopia lokalna, a nie pobrana:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}

Takie podejście zapewnia dobry mechanizm zastępczego, który potencjalnie pomaga zaoszczędzić przepustowość. Niestety w internecie nie można mieć ładnych rzeczy. Problem z funkcją local() polega na tym, że może ona być wykorzystywana do generowania odcisków cyfrowych w przeglądarce. To dość złożona lista czcionek zainstalowanych przez użytkownika. Wiele firm instaluje własne czcionki na laptopach pracowników. Na przykład Google ma firmową czcionkę o nazwie Google Sans.

Aplikacja Font Book na urządzeniu z systemem macOS, która wyświetla podgląd czcionki Google Sans.
Czcionka Google Sans zainstalowana na laptopie pracownika Google.

Osoba przeprowadzająca atak może spróbować określić, dla jakiej firmy pracuje. W tym celu testuje istnienie dużej liczby znanych czcionek firmowych, takich jak Google Sans. Osoba atakująca spróbuje renderować tekst ustawiony tymi czcionkami na płótnie i zmierzyć glify. Jeśli glify pasują do znanego kształtu czcionki korporacyjnej, atakujący uzyskuje trafienie. Jeśli glify nie są zgodne, osoba przeprowadzająca atak wie, że została użyta domyślna czcionka zastępcza, ponieważ czcionka firmowa nie została zainstalowana. Szczegółowe informacje na temat tego i innych ataków z wykorzystaniem odcisków cyfrowych w przeglądarkach znajdziesz w publikacji ankiety opracowanej przez Laperdix i innych.

Czcionki firmy są rozróżniane, a nawet można zidentyfikować je tylko na liście zainstalowanych czcionek. Sytuacja z tym wektorem ataku stała się tak zła, że zespół WebKit postanowił „uwzględniać tylko [na liście dostępnych czcionek] czcionki internetowe i czcionki, które są zainstalowane w systemie operacyjnym, a nie te zainstalowane lokalnie. Oto artykuł o przyznawaniu dostępu do lokalnych czcionek.

Interfejs Local Font Access API

Początek tego artykułu mógł wprawić Cię w zły nastrój. Czy naprawdę możemy nie mieć fajnych rzeczy? Często nie. Uważamy, że możemy. Może nie wszystko jest beznadziejne. Najpierw jednak odpowiem na pytanie, które zadajesz sobie pytanie.

Dlaczego potrzebny jest interfejs Local Font Access API, skoro istnieją czcionki internetowe?

Dotychczas trudno było udostępniać w internecie profesjonalne narzędzia do projektowania i grafiki. Jednym z problemów jest brak dostępu do pełnej gamy profesjonalnie skonstruowanych i opartych na podpowiedziach czcionek, które projektanci zainstalowali lokalnie. Czcionki internetowe umożliwiają korzystanie z niektórych przypadków użycia w publikacji, ale nie umożliwiają programowego dostępu do wektorowych kształtów glifów ani tabel czcionek używanych przez rasteryzatory do renderowania konturów glifów. Analogicznie nie ma sposobu na uzyskanie dostępu do danych binarnego czcionki internetowej.

  • Narzędzia do projektowania potrzebują dostępu do bajtów czcionek, aby wykonać własną implementację układu OpenType i umożliwić narzędziom projektowym łapanie go na niższych poziomach w celu wykonywania takich działań jak filtry wektorowe lub przekształcenia kształtów glifów.
  • Deweloperzy mogą mieć w swoich aplikacjach starsze stosy czcionek, które udostępniają w internecie. Aby korzystać z tych stosów, zazwyczaj wymagają bezpośredniego dostępu do danych czcionek, czego nie zapewniają czcionki internetowe.
  • Niektóre czcionki mogą nie być licencjonowane na potrzeby przesyłania w internecie. Na przykład Linotype ma licencję na niektóre czcionki, która obejmuje tylko użytkowanie na komputerze.

Interfejs Local Font Access API jest próbą sprostania tym wyzwaniom. Składa się z 2 części:

  • interfejs Font enumeration API, który umożliwia użytkownikom przyznawanie dostępu do pełnego zestawu dostępnych czcionek systemowych.
  • Z każdego wyniku wyliczenia można poprosić o niski poziom (zorientowany na bajty) dostęp do kontenera SFNT z pełnymi danymi czcionek.

Obsługiwane przeglądarki

Obsługa przeglądarek

  • 103
  • 103
  • x
  • x

Źródło

Jak korzystać z interfejsu Local Font Access API

Wykrywanie funkcji

Aby sprawdzić, czy interfejs Local Font Access API jest obsługiwany, użyj polecenia:

if ('queryLocalFonts' in window) {
  // The Local Font Access API is supported
}

Wyliczanie czcionek lokalnych

Aby uzyskać listę lokalnie zainstalowanych czcionek, musisz wywołać metodę window.queryLocalFonts(). Za pierwszym razem pojawi się prośba o przyznanie uprawnień, którą użytkownik może zatwierdzić lub odrzucić. Jeśli użytkownik zatwierdzi ich lokalne czcionki do wyszukania, przeglądarka zwróci tablicę z danymi czcionek, które można zapętlić. Każda czcionka jest reprezentowana jako obiekt FontData z właściwościami family (np. "Comic Sans MS"), fullName (np. "Comic Sans MS"), postscriptName (np. "ComicSansMS") i style (np. "Regular").

// Query for all available fonts and log metadata.
try {
  const availableFonts = await window.queryLocalFonts();
  for (const fontData of availableFonts) {
    console.log(fontData.postscriptName);
    console.log(fontData.fullName);
    console.log(fontData.family);
    console.log(fontData.style);
  }
} catch (err) {
  console.error(err.name, err.message);
}

Jeśli interesuje Cię tylko podzbiór czcionek, możesz też filtrować je na podstawie nazw PostScriptu, dodając parametr postscriptNames.

const availableFonts = await window.queryLocalFonts({
  postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});

Dostęp do danych SFNT

Pełny dostęp do SFNT jest dostępny za pomocą metody blob() obiektu FontData. SFNT to format pliku czcionek, który może zawierać inne czcionki, np. PostScript, TrueType, OpenType, Web Open Font Format (WOFF) i inne.

try {
  const availableFonts = await window.queryLocalFonts({
    postscriptNames: ['ComicSansMS'],
  });
  for (const fontData of availableFonts) {
    // `blob()` returns a Blob containing valid and complete
    // SFNT-wrapped font data.
    const sfnt = await fontData.blob();
    // Slice out only the bytes we need: the first 4 bytes are the SFNT
    // version info.
    // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
    const sfntVersion = await sfnt.slice(0, 4).text();

    let outlineFormat = 'UNKNOWN';
    switch (sfntVersion) {
      case '\x00\x01\x00\x00':
      case 'true':
      case 'typ1':
        outlineFormat = 'truetype';
        break;
      case 'OTTO':
        outlineFormat = 'cff';
        break;
    }
    console.log('Outline format:', outlineFormat);
  }
} catch (err) {
  console.error(err.name, err.message);
}

Pokaz

Działanie interfejsu Local Font Access API jest możliwe w prezentacji poniżej. Zapoznaj się też z kodem źródłowym. W wersji demonstracyjnej widoczny jest element niestandardowy o nazwie <font-select>, który implementuje lokalny selektor czcionek.

Kwestie dotyczące prywatności

Uprawnienie "local-fonts" wydaje się zapewniać powierzchnię, na której można łatwo odcisnąć palec. Przeglądarki mogą jednak zwracać dowolne wyniki. Na przykład przeglądarki nastawione na anonimizację mogą udostępniać wbudowany w przeglądarkę tylko zestaw domyślnych czcionek. Przeglądarki nie muszą też podawać danych tabeli dokładnie w takiej postaci, w jakiej wyświetlają się na dysku.

Gdy tylko jest to możliwe, interfejs Local Font Access API ma udostępniać tylko te informacje, które są niezbędne do wspomnianych przypadków użycia. Interfejsy API systemu mogą tworzyć listę zainstalowanych czcionek, a nie w kolejności losowej ani posortowanej, lecz w kolejności instalacji czcionek. Zwracanie dokładnej listy zainstalowanych czcionek podanej przez taki systemowy interfejs API może ujawnić dodatkowe dane, które mogą być używane do odcisków cyfrowych, a przypadki użycia, które chcemy włączyć, nie są obsługiwane przez zachowanie tej kolejności. Z tego powodu interfejs API wymaga posortowania zwróconych danych przed ich zwróceniem.

Zabezpieczenia i uprawnienia

Zespół Chrome zaprojektował i wdrożył interfejs Local Font Access API zgodnie z podstawowymi zasadami określonymi w artykule Kontrolowanie dostępu do zaawansowanych funkcji platformy internetowej, w tym dotyczących kontroli użytkownika, przejrzystości i ergonomii.

Kontrola użytkowników

Użytkownik ma pełną kontrolę nad dostępem do czcionek użytkownika. Taki dostęp jest możliwy tylko wtedy, gdy zostanie przyznane uprawnienie "local-fonts", które jest wymienione w rejestrze uprawnień.

Przejrzystość

To, czy witryna otrzymała dostęp do czcionek lokalnych użytkownika, będzie widoczna w arkuszu informacji o witrynie.

Trwałość uprawnień

Uprawnienie "local-fonts" zostanie zachowane po każdym ponownym załadowaniu strony. Możesz go odwołać, korzystając z arkusza informacji o witrynie.

Prześlij opinię

Zespół Chrome chce poznać Twoją opinię na temat korzystania z interfejsu Local Font Access API.

Opowiedz nam o projekcie interfejsu API

Czy interfejs API nie działa zgodnie z oczekiwaniami? A może brakuje metod lub właściwości, które potrzebujesz do realizacji swojego pomysłu? Masz pytanie lub komentarz na temat modelu bezpieczeństwa? Zgłoś problem ze specyfikacją w odpowiednim repozytorium GitHub lub dodaj swoje uwagi na temat istniejącego problemu.

Zgłoś problem z implementacją

Czy wystąpił błąd związany z implementacją przeglądarki Chrome? A może implementacja różni się od specyfikacji? Zgłoś błąd na stronie new.crbug.com. Podaj jak najwięcej szczegółów i proste instrukcje odtwarzania, a następnie wpisz Blink>Storage>FontAccess w polu Komponenty. Usterki to świetny sposób na udostępnianie szybkich i łatwych replik.

Pokaż obsługę interfejsu API

Czy zamierzasz korzystać z interfejsu Local Font Access API? Twoja publiczna pomoc pomaga zespołowi Chrome priorytetowo traktować funkcje i pokazuje innym dostawcom przeglądarek, jak ważne jest ich wsparcie.

Wyślij tweeta na adres @ChromiumDev, używając hashtagu #LocalFontAccess, i daj nam znać, gdzie i jak używasz tego elementu.

Podziękowania

Specyfikację interfejsu Local Font Access API wprowadził Emil A. Eklund, Alex Russell, Joshua Bell i Olivier Yiptong. Ten artykuł napisali Joe Medley, Dominik Röttsches i Olivier Yiptong. Baner powitalny od Bretta Jordan w serwisie Unsplash.