Używanie zaawansowanej typografii z czcionkami lokalnymi

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

Bezpieczne czcionki internetowe

Jeśli zajmujesz się już od jakiegoś czasu tworzeniem stron internetowych, być może pamiętasz tak zwane bezpieczne czcionki internetowe. Te czcionki są dostępne w prawie wszystkich instancjach najczęściej używanych systemów operacyjnych (Windows, macOS, najczęściej używanych dystrybucjach Linuksa, Android i iOS). Na początku XXI wieku firma Microsoft zapoczątkowała nawet inicjatywę o nazwie Podstawowe czcionki TrueType dla witryn internetowych, która udostępnia te czcionki do bezpłatnego pobrania. W ten sposób „za każdym razem, gdy odwiedzisz taką witrynę, zobaczysz strony dokładnie w taki sposób, w jaki zamierzył je projektant. Tak, dotyczyło to witryn utworzonych w Comic Sans MS. Tak wygląda klasyczny stos czcionek z funkcją bezpiecznego w przeglądarce (z ostatecznym wyborem czcionki sans-serif):

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

czcionki internetowe,

Czasy, w których znaczenie miały czcionki bezpieczne w internecie, dawno minęły. Obecnie mamy czcionki internetowe, z których niektóre są nawet czcionkami zmiennymi, które możemy jeszcze bardziej dostosować, zmieniając wartości różnych udostępnionych osi. Możesz używać czcionek internetowych, deklarując blok @font-face na początku pliku CSS, który określa pliki czcionek do pobrania:

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

Następnie możesz używać niestandardowej czcionki internetowej, podając font-family:

body {
  font-family: 'FlamboyantSansSerif';
}

Lokalne czcionki jako wektor odcisku palca

Większość czcionek internetowych pochodzi z internetu. Ciekawostką jest to, że w deklaracji funkcji @font-face występująca w niej właściwość src oprócz funkcji url() obsługuje też funkcję local(). Dzięki temu czcionki niestandardowe są wczytywane (niespodzianka!) lokalnie. Jeśli użytkownik ma zainstalowaną w systemie operacyjnym czcionkę FlamboyantSansSerif, zostanie użyta jej lokalna kopia, a nie pobrana:

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

To podejście zapewnia przydatny mechanizm zastępczy, który może oszczędzać przepustowość. W internecie nie możemy niestety mieć nic przyjemnych. Problem z funkcją local() polega na tym, że można jej używać do tworzenia odcisków palców przeglądarki. Lista czcionek zainstalowanych przez użytkownika może być dość identyfikowalna. Wiele firm ma własne czcionki firmowe zainstalowane na laptopach pracowników. Na przykład firma Google ma font firmowy o nazwie Google Sans.

Aplikacja Font Book w systemie macOS, która wyświetla podgląd czcionki Google Sans.
Czcionka Google Sans zainstalowana na laptopie pracownika Google.

Atakujący może próbować określić, w jakiej firmie pracuje dana osoba, testując obecność dużej liczby znanych czcionek korporacyjnych, takich jak Google Sans. Osoba atakująca może spróbować renderować tekst zapisany w tych czcionkach na płótnie i zmierzyć glif. Jeśli glify pasują do znanego kształtu czcionki firmowej, atakujący trafia w dziesiątkę. Jeśli znaki nie pasują, atakujący wie, że użyto domyślnego czcionki zastępczej, ponieważ czcionka firmowa nie była zainstalowana. Szczegółowe informacje o tym i innych atakach polegających na tworzeniu odcisków palców przeglądarki znajdziesz w raporcie zespołu Laperdix et al.

Oprócz czcionek firmy, identyfikatorem może być nawet sama lista zainstalowanych czcionek. Sytuacja z tym wektorem ataku stała się tak zła, że zespół WebKit postanowił uwzględniać [na liście dostępnych czcionek] tylko czcionki internetowe i czcionki dołączone do systemu operacyjnego, a nie czcionki instalowane lokalnie przez użytkownika. (A tutaj jestem, z artykułem o przyznawaniu dostępu do czcionek lokalnych).

Interfejs Local Fonts Access API

Początek tego artykułu może wywołać u Ciebie negatywne emocje. Czy naprawdę nie możemy mieć ładnych rzeczy? Nie martw się. Myślę, że możemy to zrobić i nie wszystko jest stracone. Najpierw jednak odpowiem na pytanie, które może Cię nurtować.

Dlaczego potrzebujemy interfejsu Local Font Access API, skoro istnieją czcionki internetowe?

Profesjonalne narzędzia do projektowania i tworzenia grafiki w przeszłości były trudne do udostępnienia w sieci. Jednym z problemów było to, że projektanci nie mogli korzystać z pełnej gamy profesjonalnie skonstruowanych i zaimplementowanych czcionek zainstalowanych lokalnie. Czcionki internetowe umożliwiają niektóre przypadki użycia dotyczące publikowania, ale nie umożliwiają dostępu programowego do kształtów wektorowych znaków i tablic czcionek używanych przez rastrowanie do renderowania kontur znaków. Nie ma również możliwości dostępu do danych binarnych czcionki internetowej.

  • Narzędzia do projektowania potrzebują dostępu do bajtów czcionek, aby mieć własną implementację układu OpenType. Umożliwiają też przyczepianie narzędzi projektowych na niższych poziomach na przykład w celu wykonywania filtrów wektorowych lub przekształcania kształtów glifów.
  • Deweloperzy mogą mieć starsze stosy czcionek do swoich aplikacji, które udostępniają w internecie. Aby korzystać z tych pakietów, zwykle trzeba mieć bezpośredni dostęp do danych czcionek, czego czcionki internetowe nie zapewniają.
  • Niektóre czcionki mogą nie być objęte licencją na przesyłanie przez internet. Na przykład Linotype ma licencję na niektóre czcionki tylko do użytkowania na komputerze.

Interfejs API Local Font Access to próba rozwiązania tych problemów. Składa się z 2 części:

  • Interfejs API enumeracji czcionek, który umożliwia użytkownikom przyznawanie dostępu do pełnego zestawu dostępnych czcionek systemowych.
  • Z każdego wyniku wyliczania możliwość żądania niskopoziomowego (z uwzględnieniem bajtów) dostępu do kontenera SFNT zawierającego pełne dane czcionki.

Obsługa przeglądarek

Obsługa przeglądarek

  • Chrome: 103.
  • Edge: 103.
  • Firefox: funkcja nieobsługiwana.
  • Safari: nieobsługiwane.

Źródło

Jak korzystać z interfejsu Local Font Access API

Wykrywanie cech

Aby sprawdzić, czy interfejs API Local Font Access jest obsługiwany:

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

Wyliczanie czcionek lokalnych

Aby uzyskać listę zainstalowanych lokalnie czcionek, musisz wywołać funkcję window.queryLocalFonts(). Po raz pierwszy wyświetli się prośba o przyznanie uprawnień, którą użytkownik może zaakceptować lub odrzucić. Jeśli użytkownik zatwierdzi czcionki lokalne, których ma dotyczyć zapytanie, 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 je też filtrować na podstawie nazw PostScript, 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, takie jak 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);
}

Prezentacja

Interfejs Local Font Access API możesz zobaczyć w działaniu w poniżej przedstawionym demo. Koniecznie zapoznaj się też z kodem źródłowym. Wersja demonstracyjna pokazuje element niestandardowy o nazwie <font-select>, który implementuje lokalny selektor czcionek.

Kwestie związane z prywatnością

Wygląda na to, że uprawnienie "local-fonts" umożliwia korzystanie z platformy umożliwiającej czytanie odcisku palca. Przeglądarki mogą jednak zwracać dowolne treści. Na przykład przeglądarki skoncentrowane na zapewnieniu anonimowości mogą udostępniać tylko zestaw domyślnych czcionek wbudowanych w przeglądarkę. Podobnie przeglądarki nie muszą udostępniać danych tabeli dokładnie w postaci, w której znajdują się na dysku.

W miarę możliwości interfejs Local Font Access API udostępnia tylko te informacje, które są niezbędne do obsługi wymienionych przypadków użycia. Interfejsy API systemu mogą wyświetlać listę zainstalowanych czcionek nie w kolejności losowej ani posortowanej, ale według kolejności instalacji. Zwracanie dokładnie takiej samej listy zainstalowanych czcionek, jaką podaje systemowy interfejs API, może ujawniać dodatkowe dane, które mogą być używane do tworzenia odcisków palców. Zachowanie tej kolejności nie pomaga w przypadku przypadków użycia, które chcemy umożliwić. W związku z tym ten interfejs API wymaga, aby zwracane dane były przed zwróceniem posortowane.

Zabezpieczenia i uprawnienia

Zespół Chrome zaprojektował i wdrożył interfejs Local Font Access API zgodnie z podstawowymi zasadami określonymi w artykule Kontrola dostępu do zaawansowanych funkcji platformy internetowej, takimi jak kontrola użytkowników, przejrzystość i ergonomia.

Kontrola użytkownika

Dostęp do czcionek użytkownika jest w pełni pod jego kontrolą i nie będzie dozwolony, dopóki nie zostanie przyznane uprawnienie "local-fonts" określone w rejestrze uprawnień.

Przejrzystość

Informacje o tym, czy witrynie przyznano dostęp do czcionek lokalnych użytkownika, będą widoczne w arkuszu informacji o witrynie.

Trwałość uprawnień

Uprawnienie "local-fonts" będzie zachowane między wczytaniami strony. Możesz cofnąć tę zgodę na karcie Informacje o witrynie.

Prześlij opinię

Zespół Chrome chce poznać Twoje wrażenia z korzystania z interfejsu API dostępu do czcionek lokalnych.

Prześlij informacje o projektowaniu interfejsu API

Czy coś w interfejsie API nie działa zgodnie z oczekiwaniami? A może brakuje Ci metod lub właściwości, których potrzebujesz, by wdrożyć swój pomysł? Masz pytania lub uwagi dotyczące modelu bezpieczeństwa? Zgłoś problem ze specyfikacją w odpowiednim repozytorium GitHub lub podziel się uwagami na temat istniejącego problemu.

Zgłoś problem z implementacją

Czy wystąpił błąd związany z implementacją 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, proste instrukcje odtwarzania błędu i wpisz Blink>Storage>FontAccess w polu Składniki. Glitch to świetne narzędzie do szybkiego i łatwego udostępniania informacji o powtarzających się problemach.

Pokaż informacje o pomocy dotyczącej interfejsu API

Zamierzasz używać interfejsu Local Font Access API? Twoje publiczne wsparcie pomaga zespołowi Chrome ustalać priorytety dla funkcji i pokazuje innym dostawcom przeglądarek, jak ważne jest, aby wspierać te funkcje.

Wyślij tweeta do @ChromiumDev, używając hashtaga #LocalFontAccess, i powiedz nam, gdzie i jak z niego korzystasz.

Podziękowania

Specyfikacja interfejsu API Local Font Access została zmodyfikowana przez Emil A. Eklund, Alex Russell, Joshua Bell i Olivier Yiptong. Ten artykuł został sprawdzony przez Joe Medley, Dominik Röttsches i Olivier Yiptong. Baner powitalny autorstwa Bretta Jordana na Unsplash.