Erweiterte Typografie mit lokalen Schriftarten verwenden

Hier erfahren Sie, wie Sie mit der Local Font Access API auf die lokal installierten Schriftarten des Nutzers zugreifen und detaillierte Informationen dazu abrufen können.

Veröffentlicht am 24. August 2020

Websichere Schriftarten

Wenn Sie schon länger Webentwickler sind, erinnern Sie sich vielleicht noch an die sogenannten websicheren Schriftarten. Diese Schriftarten sind auf fast allen Instanzen der am häufigsten verwendeten Betriebssysteme (Windows, macOS, die gängigsten Linux-Distributionen, Android und iOS) verfügbar. Anfang der 2000er Jahre startete Microsoft sogar eine Initiative namens TrueType Core Fonts for the Web, die diese Schriftarten kostenlos zum Download anbot, damit „Websites, die diese Schriftarten verwenden, genau so angezeigt werden, wie es der Webdesigner vorgesehen hat“. Ja, das gilt auch für Websites, auf denen Comic Sans MS verwendet wird. Ein klassischer websicherer Schriftarten-Stack (mit der ultimativen Fallback-Schriftart sans-serif) könnte so aussehen:

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

Webschriftarten

Die Zeiten, in denen websichere Schriftarten wirklich wichtig waren, sind längst vorbei. Heute haben wir Webfonts, von denen einige sogar variable Fonts sind, die wir durch Ändern der Werte für die verschiedenen verfügbaren Achsen weiter anpassen können. Webfonts können verwendet werden, indem am Anfang des CSS ein @font-face-Block deklariert wird, in dem die herunterzuladenden Schriftartdateien angegeben werden:

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

Anschließend können Sie die benutzerdefinierte Web-Schriftart wie gewohnt mit font-family verwenden:

body {
  font-family: 'FlamboyantSansSerif';
}

Lokale Schriftarten als Fingerprint-Vektor

Die meisten Webfonts stammen aus dem Web. Interessant ist jedoch, dass die Property src in der @font-face-Deklaration neben der Funktion url()> auch die Funktion local() akzeptiert. Dadurch können benutzerdefinierte Schriftarten lokal geladen werden. Wenn der Nutzer FlamboyantSansSerif auf seinem Betriebssystem installiert hat, wird die lokale Kopie verwendet, anstatt die Schriftart herunterzuladen:

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

Dieser Ansatz bietet einen guten Fallback-Mechanismus, der möglicherweise Bandbreite spart. Im Internet gibt es leider nicht nur Gutes. Das Problem mit der local()-Funktion besteht darin, dass sie für Browser-Fingerprinting missbraucht werden kann. Es stellt sich heraus, dass die Liste der Schriftarten, die ein Nutzer installiert hat, ziemlich identifizierend sein kann. Viele Unternehmen haben eigene Unternehmensschriftarten, die auf den Laptops der Mitarbeiter installiert sind. Google hat beispielsweise eine Unternehmensschriftart namens Google Sans.

Die macOS-App „Schriftsammlung“ mit einer Vorschau der Schriftart „Google Sans“
Die Schriftart Google Sans, die auf dem Laptop eines Google-Mitarbeiters installiert ist.

Ein Angreifer kann versuchen, herauszufinden, für welches Unternehmen jemand arbeitet, indem er die Existenz einer großen Anzahl bekannter Unternehmensschriftarten wie Google Sans testet. Der Angreifer würde versuchen, Text, der in diesen Schriftarten festgelegt ist, auf einem Canvas zu rendern und die Glyphen zu messen. Wenn die Glyphen mit der bekannten Form der Unternehmensschrift übereinstimmen, hat der Angreifer einen Treffer erzielt. Wenn die Glyphen nicht übereinstimmen, weiß der Angreifer, dass eine Standardschriftart verwendet wurde, da die Unternehmensschriftart nicht installiert war. Ausführliche Informationen zu dieser und anderen Browser-Fingerprinting-Angriffen finden Sie im Übersichtsartikel von Laperdix et al.

Abgesehen von Unternehmensschriftarten kann schon die Liste der installierten Schriftarten identifizierbar sein. Die Situation mit diesem Angriffsvektor hat sich so verschlechtert, dass das WebKit-Team kürzlich beschlossen hat, „nur Webfonts und Schriftarten, die mit dem Betriebssystem geliefert werden, in die Liste der verfügbaren Schriftarten aufzunehmen, nicht aber lokal vom Nutzer installierte Schriftarten“. (Und hier bin ich mit einem Artikel zum Gewähren des Zugriffs auf lokal gespeicherte Schriftarten.)

Die Local Font Access API

Der Anfang dieses Artikels hat Sie vielleicht in eine negative Stimmung versetzt. Können wir wirklich nichts Schönes haben? Kein Grund zur Sorge. Wir glauben, dass wir das schaffen können, und vielleicht ist noch nicht alles verloren. Zuerst möchte ich aber eine Frage beantworten, die Sie sich vielleicht stellen.

Warum benötigen wir die Local Font Access API, wenn es Webfonts gibt?

Professionelle Design- und Grafiktools waren im Web bisher schwer zu realisieren. Ein Problem war bisher, dass nicht auf die gesamte Vielfalt professionell erstellter und gehinteter Schriftarten zugegriffen werden konnte, die Designer lokal installiert haben. Webfonts ermöglichen einige Anwendungsfälle für die Veröffentlichung, aber keinen programmatischen Zugriff auf die Vektorglyphenformen und Schriftarttabellen, die von Rasterizern zum Rendern der Glyphenumrisse verwendet werden. Es gibt auch keine Möglichkeit, auf die Binärdaten einer Web-Schriftart zuzugreifen.

  • Designtools benötigen Zugriff auf Schriftart-Bytes, um ihre eigene OpenType-Layoutimplementierung durchzuführen und Designtools auf niedrigeren Ebenen einzubinden, z. B. für Aktionen wie das Anwenden von Vektorfiltern oder Transformationen auf die Glyphenformen.
  • Entwickler haben möglicherweise ältere Schrift-Stacks für ihre Anwendungen, die sie ins Web bringen. Für die Verwendung dieser Stacks ist in der Regel direkter Zugriff auf Schriftartdaten erforderlich, was bei Webfonts nicht möglich ist.
  • Einige Schriftarten sind möglicherweise nicht für die Bereitstellung über das Web lizenziert. Linotype hat beispielsweise eine Lizenz für einige Schriftarten, die nur die Desktop-Nutzung umfasst.

Die Local Font Access API soll diese Herausforderungen lösen. Sie besteht aus zwei Teilen:

  • Eine API zur Schriftartaufzählung, mit der Nutzer Zugriff auf alle verfügbaren Systemschriftarten gewähren können.
  • Aus jedem Enumerierungsergebnis kann der Zugriff auf den SFNT-Container auf niedriger Ebene (byteorientiert) angefordert werden, der die vollständigen Schriftartdaten enthält.

Unterstützte Browser

Browser Support

  • Chrome: 103.
  • Edge: 103.
  • Firefox: not supported.
  • Safari: not supported.

Source

Local Font Access API verwenden

Funktionserkennung

So prüfen Sie, ob die Local Font Access API unterstützt wird:

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

Lokale Schriftarten auflisten

Wenn Sie eine Liste der lokal installierten Schriftarten abrufen möchten, müssen Sie window.queryLocalFonts() aufrufen. Beim ersten Mal wird eine Berechtigungsaufforderung ausgelöst, die der Nutzer genehmigen oder ablehnen kann. Wenn der Nutzer die Abfrage seiner lokalen Schriftarten genehmigt, gibt der Browser ein Array mit Schriftartdaten zurück, das Sie durchlaufen können. Jede Schriftart wird als FontData-Objekt mit den Attributen family (z. B. "Comic Sans MS"), fullName (z. B. "Comic Sans MS"), postscriptName (z. B. "ComicSansMS") und style (z. B. "Regular") dargestellt.

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

Wenn Sie nur an einer Teilmenge von Schriftarten interessiert sind, können Sie sie auch anhand der PostScript-Namen filtern, indem Sie einen postscriptNames-Parameter hinzufügen.

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

Auf SFNT-Daten zugreifen

Der vollständige SFNT-Zugriff ist über die Methode blob() des Objekts FontData verfügbar. SFNT ist ein Schriftartdateiformat, das andere Schriftarten wie PostScript, TrueType, OpenType, Web Open Font Format (WOFF) und andere enthalten kann.

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

Demo

Demo Sehen Sie sich auch den Quellcode an. In der Demo wird ein benutzerdefiniertes Element namens <font-select> gezeigt, das eine lokale Schriftartauswahl implementiert.

Datenschutz

Die Berechtigung "local-fonts" scheint eine Oberfläche zu bieten, die sich sehr gut für Fingerprinting eignet. Browser können jedoch beliebige Werte zurückgeben. Beispielsweise können Anonymitätsbrowser nur eine Reihe von Standard-Schriftarten bereitstellen, die im Browser integriert sind. Ebenso müssen Browser Tabellendaten nicht genau so bereitstellen, wie sie auf der Festplatte gespeichert sind.

Die Local Font Access API ist so konzipiert, dass sie nur die Informationen offenlegt, die für die genannten Anwendungsfälle erforderlich sind. System-APIs können eine Liste der installierten Schriftarten in der Reihenfolge der Schriftartinstallation zurückgeben, nicht in zufälliger oder sortierter Reihenfolge. Wenn genau die Liste der installierten Schriftarten zurückgegeben wird, die von einer solchen System-API bereitgestellt wird, können zusätzliche Daten offengelegt werden, die für das Fingerprinting verwendet werden können. Die Anwendungsfälle, die wir ermöglichen möchten, werden durch die Beibehaltung dieser Reihenfolge nicht unterstützt. Daher muss diese API die zurückgegebenen Daten sortieren, bevor sie zurückgegeben werden.

Sicherheit und Berechtigungen

Das Chrome-Team hat die Local Font Access API unter Berücksichtigung der in Controlling Access to Powerful Web Platform Features definierten Grundsätze entwickelt und implementiert, darunter Nutzerkontrolle, Transparenz und Ergonomie.

Nutzersteuerung

Der Zugriff auf die Schriftarten eines Nutzers liegt vollständig in seiner Hand und ist nur zulässig, wenn die Berechtigung "local-fonts", wie in der Berechtigungsregistrierung aufgeführt, erteilt wird.

Transparenz

Ob einer Website Zugriff auf die lokalen Schriftarten des Nutzers gewährt wurde, ist im Informationsblatt zur Website zu sehen.

Beibehalten von Berechtigungen

Die Berechtigung "local-fonts" bleibt auch nach dem Neuladen der Seite erhalten. Sie kann über das Tab Websiteinformationen widerrufen werden.

Feedback

Das Chrome-Team möchte gern wissen, wie Ihre Erfahrungen mit der Local Font Access API sind.

Informationen zum API-Design

Gibt es etwas an der API, das nicht wie erwartet funktioniert? Oder fehlen Methoden oder Eigenschaften, die Sie für die Umsetzung Ihrer Idee benötigen? Haben Sie Fragen oder Anmerkungen zum Sicherheitsmodell? Melden Sie ein Spezifikationsproblem im entsprechenden GitHub-Repository oder fügen Sie Ihre Gedanken zu einem bestehenden Problem hinzu.

Problem mit der Implementierung melden

Haben Sie einen Fehler in der Chrome-Implementierung gefunden? Oder weicht die Implementierung von der Spezifikation ab? Melden Sie einen Fehler unter new.crbug.com. Geben Sie so viele Details wie möglich an, fügen Sie eine einfache Anleitung zur Reproduktion hinzu und geben Sie Blink>Storage>FontAccess in das Feld Components (Komponenten) ein.

API-Support zeigen

Planen Sie, die Local Font Access API zu verwenden? Ihre öffentliche Unterstützung hilft dem Chrome-Team, Funktionen zu priorisieren, und zeigt anderen Browseranbietern, wie wichtig es ist, sie zu unterstützen.

Senden Sie einen Tweet an @ChromiumDev mit dem Hashtag #LocalFontAccess und teilen Sie uns mit, wo und wie Sie die Funktion verwenden.

Danksagungen

Die API-Spezifikation für den Zugriff auf lokal gespeicherte Schriftarten wurde von Emil A. bearbeitet. Eklund, Alex Russell, Joshua Bell und Olivier Yiptong. Dieser Artikel wurde von Joe Medley, Dominik Röttsches und Olivier Yiptong überprüft.