Comment l'application de retouche d'images vectorielles Boxy SVG utilise l'API Local Font Access pour permettre aux utilisateurs de choisir leurs polices locales préférées

L'API Local Font Access fournit un mécanisme permettant d'accéder aux données sur les polices installées localement par l'utilisateur, y compris des détails de niveau supérieur tels que les noms, les styles et les familles, ainsi qu'aux octets bruts des fichiers de police sous-jacents. Découvrez comment l'application d'édition SVG Boxy SVG utilise cette API.

Introduction

(Cet article est également disponible sous forme de vidéo.)

Boxy SVG est un éditeur de graphiques vectoriels. Son principal cas d'utilisation est la modification de dessins au format de fichier SVG, pour créer des illustrations, des logos, des icônes et d'autres éléments de conception graphique. Il a été développé par le développeur polonais Jarosław Foksa et est initialement sorti le 15 mars 2013. Jarosław gère un blog Boxy SVG dans lequel il annonce les nouvelles fonctionnalités qu'il ajoute à l'application. Ce développeur est un fervent partisan du projet Fugu de Chromium et dispose même d'une balise Fugu sur le suivi des idées de l'application.

Application Boxy SVG en train de modifier l'icône SVG du projet Fugu.

API Local Font Access dans le SVG Boxy

L'API Local Fonts Access est une fonctionnalité ajoutée dont Jarosław a parlé dans son article de blog. L'API Local Font Access permet aux utilisateurs d'accéder aux polices installées localement, y compris aux informations de niveau supérieur telles que les noms, les styles et les familles, ainsi qu'aux octets bruts des fichiers de polices sous-jacents. La capture d'écran suivante montre comment j'ai accordé à l'application l'accès aux polices installées localement sur mon MacBook et choisi la police Marker Felt pour mon texte.

Application Boxy SVG modifiant l'icône SVG Project Fugu en ajoutant le texte "Project Fugu rocks" (Project Fugu est génial) dans la police Marker Felt, qui est sélectionnée dans le sélecteur de police.

Le code sous-jacent est assez simple. Lorsque l'utilisateur ouvre le sélecteur de famille de polices pour la première fois, l'application vérifie d'abord si le navigateur Web prend en charge l'API Local Font Access.

Il recherche également l'ancienne version expérimentale de l'API et l'utilise si elle est présente. À partir de 2023, vous pouvez ignorer l'ancienne API en toute sécurité, car elle n'était disponible que pendant une courte période via des flags Chrome expérimentaux. Toutefois, certains dérivés de Chromium peuvent toujours l'utiliser.

let isLocalFontsApiEnabled = (
  // Local Font Access API, Chrome >= 102
  window.queryLocalFonts !== undefined ||
  // Experimental Local Font Access API, Chrome < 102
  navigator.fonts?.query !== undefined
);

Si l'API Local Font Access n'est pas disponible, l'outil de sélection de famille de polices devient gris. Un texte d'espace réservé s'affichera pour l'utilisateur à la place de la liste des polices:

if (isLocalFontsApiEnabled === false) {
  showPlaceholder("no-local-fonts-api");
  return;
}

Sélecteur de police affichant le message &quot;Votre navigateur n&#39;est pas compatible avec l&#39;API Local Font Access&quot;.

Sinon, l'API Local Font Access est utilisée pour récupérer la liste de toutes les polices du système d'exploitation. Notez le bloc try…catch, qui est nécessaire pour gérer correctement les erreurs d'autorisation.

let localFonts;

if (isLocalFontsApiEnabled === true) {
  try {
    // Local Font Access API, Chrome >= 102
    if (window.queryLocalFonts) {
      localFonts = await window.queryLocalFonts();
    }
    // Experimental Local Font Access API, Chrome < 102
    else if (navigator.fonts?.query) {
      localFonts = await navigator.fonts.query({
        persistentAccess: true,
      });
    }
  } catch (error) {
    showError(error.message, error.name);
  }
}

Une fois la liste des polices locales récupérée, une fontsIndex simplifiée et normalisée est créée:

let fontsIndex = [];

for (let localFont of localFonts) {
  let face = "400";

  // Determine the face name
  {
    let subfamily = localFont.style.toLowerCase();
    subfamily = subfamily.replaceAll(" ", "");
    subfamily = subfamily.replaceAll("-", "");
    subfamily = subfamily.replaceAll("_", "");

    if (subfamily.includes("thin")) {
      face = "100";
    } else if (subfamily.includes("extralight")) {
      face = "200";
    } else if (subfamily.includes("light")) {
      face = "300";
    } else if (subfamily.includes("medium")) {
      face = "500";
    } else if (subfamily.includes("semibold")) {
      face = "600";
    } else if (subfamily.includes("extrabold")) {
      face = "800";
    } else if (subfamily.includes("ultrabold")) {
      face = "900";
    } else if (subfamily.includes("bold")) {
      face = "700";
    }

    if (subfamily.includes("italic")) {
      face += "i";
    }
  }

  let descriptor = fontsIndex.find((descriptor) => {
    return descriptor.family === localFont.family);
  });

  if (descriptor) {
    if (descriptor.faces.includes(face) === false) {
      descriptor.faces.push(face);
    }
  } else {
    let descriptor = {
      family: localFont.family,
      faces: [face],
    };

    fontsIndex.push(descriptor);
  }
}

for (let descriptor of fontsIndex) {
  descriptor.faces.sort();
}

L'index des polices normalisées est ensuite stocké dans la base de données IndexedDB afin qu'il puisse être facilement interrogé, partagé entre les instances d'application et conservé entre les sessions. Boxy SVG utilise Dexie.js pour gérer la base de données:

let database = new Dexie("LocalFontsManager");
database.version(1).stores({cache: "family"}).
await database.cache.clear();
await database.cache.bulkPut(fontsIndex);

Section &quot;Stockage&quot; des outils pour les développeurs Chrome affichant la table IndexedDB avec le cache des polices.

Une fois la base de données renseignée, le widget de sélecteur de police peut l'interroger et afficher les résultats à l'écran:

Sélecteur de police avec des polices insérées.

Il est important de noter que Boxy SVG affiche la liste dans un élément personnalisé nommé <bx-fontfamilypicker> et stylise chaque élément de la liste de polices afin qu'il s'affiche dans la famille de polices concernée. Pour isoler cet élément du reste de la page, Boxy SVG utilise le Shadow DOM dans cet élément et d'autres.

Panneau &quot;Éléments&quot; des outils pour les développeurs Chrome affichant l&#39;outil de sélection de police en cours d&#39;inspection: un élément personnalisé nommé &quot;bx-fontfamiliypicker&quot;.

Conclusions

La fonctionnalité Polices locales a été très populaire, car les utilisateurs apprécient de pouvoir accéder à leurs polices locales pour leurs conceptions et leurs créations. Lorsque la forme de l'API a changé et que la fonctionnalité a cessé de fonctionner brièvement, les utilisateurs en ont immédiatement noté l'existence. Jarosław a rapidement remplacé le code par le modèle défensif que vous pouvez voir dans l'extrait ci-dessus. Il fonctionne avec la version à jour de Chrome, ainsi que d'autres dérivés de Chromium qui n'ont peut-être pas encore migré vers la dernière version. Essayez Boxy SVG et n'oubliez pas de vérifier les polices installées localement. Vous pourriez découvrir des classiques oubliés depuis longtemps comme Zapf Dingbats ou Webdings.