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 znasz 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 w. firma Microsoft była nawet inicjatorem inicjatywy TrueType Core Fonts for the Web, która umożliwiała bezpłatne pobieranie tych czcionek. Jej celem było to, aby zawsze, gdy użytkownik odwiedza witrynę, która je określa, widział strony dokładnie tak, jak zamierzył to projektant witryny. Tak, dotyczy to witryn z ustawionymi ustawieniami Comic Sans MS. Oto klasyczny zestaw czcionek internetowych (z ostatecznym substytutem dowolnej sans-serif czcionki) może wyglądać tak:

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. Aby używać czcionek internetowych, na początku pliku CSS zadeklaruj blok @font-face, który określa pliki czcionek do pobrania:

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

Następnie możesz użyć 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 niestety nie ma rzeczy idealnych. Problem z funkcją local() polega na tym, że można jej używać do tworzenia odcisków palców przeglądarki. Okazuje się, że lista czcionek zainstalowanych przez użytkownika może być dość charakterystyczna. 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 firmowych, 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?

W przeszłości trudno było znaleźć w internecie narzędzia do tworzenia grafik i projektów o profesjonalnej jakości. 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 programistycznego do kształtów wektorowych i tablic czcionek używanych przez rastrowanie do renderowania kontur glifu. Nie ma też możliwości uzyskania dostępu do danych binarnych czcionki internetowej.

  • Narzędzia do projektowania potrzebują dostępu do bajtów czcionek, aby móc stosować własne implementacje układu OpenType i umożliwiać narzędziom do projektowania łączenie się z poziomami niższego poziomu w celu wykonywania takich działań jak stosowanie filtrów wektorowych czy przekształcanie kształtów glifu.
  • Deweloperzy mogą mieć starsze zestawy czcionek w aplikacjach, które przenoszą do sieci. 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 mieć licencji na dostarczanie przez Internet. Na przykład Linotype ma licencję na niektóre czcionki, która obejmuje tylko użytkowanie na komputerze.

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

  • Interfejs API do wyliczania czcionek, który umożliwia użytkownikom przyznawanie dostępu do pełnego zestawu dostępnych czcionek systemowych.
  • W ramach każdego wyniku zliczania możliwość żądania dostępu do kontenera SFNT niskiego poziomu (orientowanego na bajty), który zawiera pełne dane czcionki.

Obsługa przeglądarek

Obsługa przeglądarek

  • Chrome: 103.
  • Edge: 103.
  • Firefox: nieobsługiwane.
  • 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 zapytanie dotyczące czcionek lokalnych, przeglądarka zwróci tablicę z danymi czcionek, które możesz przetwarzać w pętli. Każda czcionka jest reprezentowana przez 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 czcionki, który może zawierać inne czcionki, takie jak PostScript, TrueType, OpenType, czcionki w formacie 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. Sprawdź też kod źródłowy. Demonstracja przedstawia element niestandardowy o nazwie <font-select>, który implementuje lokalny selektor czcionek.

Kwestie związane z prywatnością

Zezwolenie "local-fonts" umożliwia dostęp do powierzchni, która może być łatwo zidentyfikowana. 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 tak, jak są one widoczne na dysku.

W miarę możliwości interfejs Local Font Access API udostępnia tylko informacje 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 zgodnie z 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 wdrżał interfejs API Local Font Access, korzystając z podstawowych zasad określonych w artykule Kontrolowanie dostępu do zaawansowanych funkcji platformy internetowej, w tym kontroli użytkownika, przejrzystości i ergonomiki.

Kontrola użytkownika

Dostęp do czcionek użytkownika jest całkowicie pod jego kontrolą i nie będzie dozwolony, chyba że użytkownik przyzna uprawnienia "local-fonts" wymienione 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 przechowywane między wczytaniami stron. 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.

Poinformuj nas o projektowaniu interfejsu API

Czy coś w interfejsie API nie działa zgodnie z oczekiwaniami? A może brakuje metod lub właściwości, których potrzebujesz do wdrożenia swojego pomysłu? Masz pytania lub uwagi dotyczące modelu bezpieczeństwa? Zgłoś problem ze specyfikacją w odpowiednim repozytorium GitHub lub dodaj swoje uwagi do istniejącego problemu.

Zgłaszanie problemów z implementacją

Czy znalazłeś/znalazłaś błąd w implementacji 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? Twoja publiczna pomoc pomaga zespołowi Chrome ustalać priorytety funkcji i pokazuje innym dostawcom przeglądarek, jak ważne jest wspieranie tych funkcji.

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 Brett Jordan na Unsplash.