Utiliser une typographie avancée avec des polices locales

Découvrez comment l'API Local Font Access vous permet d'accéder aux polices installées localement par l'utilisateur et d'obtenir des détails de bas niveau les concernant.

Polices Web sécurisées

Si vous développez des sites Web depuis suffisamment longtemps, vous vous souvenez peut-être des polices Web sécurisées. Ces polices sont connues pour être disponibles sur presque toutes les instances des systèmes d'exploitation les plus utilisés (à savoir Windows, macOS, les distributions Linux les plus courantes, Android et iOS). Au début des années 2000, Microsoft a même lancé une initiative appelée TrueType core fonts for the Web (polices TrueType principales pour le Web). L'objectif était de proposer ces polices sans frais au téléchargement avec l'objectif suivant : chaque fois que vous consultez un site Web qui les spécifie, vous verrez des pages exactement comme le concepteur le souhaitait. Oui, cela incluait les sites définis dans Comic Sans MS. Voici une pile de polices Web classique sécurisée (avec la solution de remplacement ultime de n'importe quelle police sans-serif) pourrait ressembler à ceci:

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

Polices Web

L'époque où les polices Web sécurisées étaient vraiment importantes est révolue. Aujourd'hui, nous proposons des polices Web. Certaines sont même des polices variables que nous pouvons affiner davantage en modifiant les valeurs des différents axes exposés. Vous pouvez utiliser des polices Web en déclarant un bloc @font-face au début du code CSS, qui spécifie le ou les fichiers de polices à télécharger:

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

Vous pouvez ensuite utiliser la police Web personnalisée en spécifiant font-family, comme d'habitude:

body {
  font-family: 'FlamboyantSansSerif';
}

Polices locales en tant que vecteur d'empreinte

La plupart des polices Web proviennent du Web. Il est toutefois intéressant de noter que la propriété src de la déclaration @font-face accepte également une fonction local(), en plus de la fonction url(). Cela permet de charger (surprise !) des polices personnalisées localement. Si l'utilisateur a installé FlamboyantSansSerif sur son système d'exploitation, la copie locale est utilisée au lieu d'être téléchargée:

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

Cette approche fournit un mécanisme de secours intéressant qui peut potentiellement économiser de la bande passante. Sur Internet, malheureusement, il n'y a pas de belles choses. Le problème avec la fonction local() est qu'elle peut être utilisée de manière abusive pour la création d'empreintes dans le navigateur. Il s'avère que la liste des polices installées par un utilisateur peut être assez révélatrice. De nombreuses entreprises disposent de leurs propres polices d'entreprise installées sur les ordinateurs portables de leurs employés. Par exemple, Google utilise une police d'entreprise appelée Google Sans.

L'application Font Book de macOS affichant un aperçu de la police Google Sans.
Police Google Sans installée sur l'ordinateur portable d'un employé de Google.

Un pirate informatique peut essayer de déterminer l'entreprise pour laquelle quelqu'un travaille en testant l'existence d'un grand nombre de polices d'entreprise connues, telles que Google Sans. Le pirate tente alors d'afficher le texte défini dans ces polices sur un canevas et de mesurer les glyphes. Si les glyphes correspondent à la forme connue de la police d'entreprise, le pirate informatique est victime d'un piratage. Si les glyphes ne correspondent pas, le pirate informatique sait qu'une police de remplacement par défaut a été utilisée, car la police d'entreprise n'a pas été installée. Pour en savoir plus sur cette attaque et d'autres attaques par empreinte digitale dans les navigateurs, lisez l'enquête publiée par Laperdix et al.

Les polices d’entreprise les plus à l’écart, même la liste des polices installées peut permettre d’identifier. La situation avec ce vecteur d'attaque est devenue si mauvaise que récemment l'équipe WebKit a décidé de "inclure uniquement [dans la liste des polices disponibles] les polices Web et les polices fournies avec le système d'exploitation, mais pas les polices installées localement par l'utilisateur". (Et voici un article sur l'octroi de l'accès aux polices locales.)

API Local Font Access

Le début de cet article peut vous avoir mis dans un état d'esprit négatif. Ne peut-on vraiment pas avoir de belles choses ? Ne vous inquiétez pas. Nous pensons que c'est possible, et peut-être que tout n'est pas désespéré. Mais d’abord, laissez-moi répondre à une question que vous vous posez peut-être.

Pourquoi avons-nous besoin de l'API Local Font Access alors qu'il existe des polices Web ?

Les outils de conception et de graphisme de qualité professionnelle ont toujours été difficiles à proposer sur le Web. L'un des obstacles est l'impossibilité d'accéder et d'utiliser l'ensemble des polices d'écriture conçues par des professionnels et dotées d'indices, que les concepteurs ont installées localement. Les polices Web sont compatibles avec certains cas d'utilisation de publication, mais n'autorisent pas l'accès programmatique aux formes de glyphes vectorielles et aux tables de polices utilisées par les rastériseurs pour afficher les contours des glyphes. De même, il n'existe aucun moyen d'accéder aux données binaires d'une police Web.

  • Les outils de conception ont besoin d'accéder aux octets de police pour effectuer leur propre implémentation de mise en page OpenType et permettre aux outils de conception de s'associer à des niveaux inférieurs, pour des actions telles que l'exécution de filtres vectoriels ou de transformations sur les formes de glyphes.
  • Les développeurs peuvent avoir d'anciennes piles de polices pour leurs applications qu'ils importent sur le Web. Pour utiliser ces piles, elles nécessitent généralement un accès direct aux données de police, ce que les polices Web ne fournissent pas.
  • Il est possible qu'aucune licence ne soit disponible pour certaines polices pour la diffusion sur le Web. Par exemple, Linotype dispose d'une licence pour certaines polices qui n'inclut que l'utilisation sur ordinateur.

L'API Local Font Access tente de résoudre ces problèmes. Il se compose de deux parties :

  • Une API d'énumération de polices, qui permet aux utilisateurs d'accorder l'accès à l'ensemble des polices système disponibles.
  • À partir de chaque résultat d'énumération, la possibilité de demander l'accès au conteneur SFNT de bas niveau (orienté octet), incluant l'ensemble des données de police.

Prise en charge des navigateurs

Navigateurs pris en charge

  • 103
  • 103
  • x
  • x

Source

Utiliser l'API Local Font Access

Détection de fonctionnalités

Pour vérifier si l'API Local Font Access est compatible, utilisez:

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

Énumérer les polices locales

Pour obtenir la liste des polices installées localement, vous devez appeler window.queryLocalFonts(). La première fois, cela déclenche une invite d'autorisation, que l'utilisateur peut approuver ou refuser. Si l'utilisateur approuve l'interrogation des polices locales, le navigateur renvoie un tableau contenant les données de police que vous pouvez lire en boucle. Chaque police est représentée sous la forme d'un objet FontData avec les propriétés family (par exemple, "Comic Sans MS"), fullName (par exemple, "Comic Sans MS"), postscriptName (par exemple, "ComicSansMS") et style (par exemple, "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);
}

Si vous n'êtes intéressé que par un sous-ensemble de polices, vous pouvez également les filtrer en fonction des noms PostScript en ajoutant un paramètre postscriptNames.

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

Accéder aux données SFNT

L'accès complet à SFNT est disponible via la méthode blob() de l'objet FontData. SFNT est un format de fichier de police qui peut contenir d'autres polices, telles que PostScript, TrueType, OpenType, Web Open Font Format (WOFF), etc.

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

Démonstration

Vous pouvez voir l'API Local Font Access en action dans la démonstration ci-dessous. Veillez également à consulter le code source. La démonstration présente un élément personnalisé appelé <font-select> qui implémente un sélecteur de police local.

Considérations relatives à la confidentialité

L'autorisation "local-fonts" semble fournir une surface pouvant faire l'objet d'une empreinte digitale. Toutefois, les navigateurs sont libres de renvoyer tout ce qu'ils souhaitent. Par exemple, les navigateurs axés sur l'anonymat peuvent choisir de ne fournir qu'un ensemble de polices par défaut intégrées au navigateur. De même, les navigateurs ne sont pas tenus de fournir les données de table exactement telles qu'elles apparaissent sur le disque.

Dans la mesure du possible, l'API Local Font Access est conçue pour n'afficher que les informations exactes nécessaires pour permettre les cas d'utilisation mentionnés. Les API système peuvent générer une liste des polices installées non pas dans un ordre aléatoire ni trié, mais dans l'ordre de leur installation. Le fait de renvoyer exactement la liste des polices installées fournie par une telle API système peut exposer des données supplémentaires pouvant être utilisées pour l'empreinte numérique. Le maintien de cet ordre ne facilite pas les cas d'utilisation que nous souhaitons activer. Par conséquent, cette API nécessite que les données renvoyées soient triées avant d'être renvoyées.

Sécurité et autorisations

L'équipe Chrome a conçu et mis en œuvre l'API Local Font Access en suivant les principes fondamentaux définis dans l'article Contrôler l'accès aux fonctionnalités puissantes de la plate-forme Web, y compris le contrôle utilisateur, la transparence et l'ergonomie.

Contrôle des utilisateurs

L'accès aux polices d'un utilisateur est entièrement sous son contrôle et ne sera autorisé que si l'autorisation "local-fonts", répertoriée dans le registre des autorisations, est accordée.

Transparence

L'autorisation d'accès aux polices locales de l'utilisateur s'affiche dans la feuille d'informations du site.

Persistance des autorisations

L'autorisation "local-fonts" sera conservée entre chaque actualisation de la page. Vous pouvez la révoquer via la feuille d'informations sur le site.

Commentaires

L'équipe Chrome souhaite connaître votre avis sur votre expérience avec l'API Local Font Access.

Décrivez-nous la conception de l'API.

Y a-t-il quelque chose dans l'API qui ne fonctionne pas comme prévu ? Ou manque-t-il des méthodes ou des propriétés dont vous avez besoin pour mettre en œuvre votre idée ? Vous avez une question ou un commentaire sur le modèle de sécurité ? Signalez un problème de spécification dans le dépôt GitHub correspondant ou ajoutez vos commentaires à un problème existant.

Signaler un problème d'implémentation

Avez-vous détecté un bug dans l'implémentation de Chrome ? Ou l'implémentation est-elle différente des spécifications ? Signalez un bug à l'adresse new.crbug.com. Veillez à inclure autant de détails que possible, ainsi que des instructions simples pour reproduire le bug, puis saisissez Blink>Storage>FontAccess dans la zone Composants. Glitch est idéal pour partager des reproductions simples et rapides.

Afficher la compatibilité avec l'API

Comptez-vous utiliser l'API Local Font Access ? Votre assistance publique aide l'équipe Chrome à prioriser les fonctionnalités et montre aux autres fournisseurs de navigateurs à quel point il est essentiel de les prendre en charge.

Envoyez un tweet à @ChromiumDev avec le hashtag #LocalFontAccess, et indiquez-nous où et comment vous l'utilisez.

Remerciements

La spécification de l'API Local Font Access a été modifiée par Emil A. Eklund, Alex Russell, Joshua Bell et Olivier Yiptong. Cet article a été lu par Joe Medley, Dominik Röttsches et Olivier Yiptong. Image principale de Brett Jordan sur Unsplash.