Cómo la app de edición de imágenes vectoriales Boxy SVG usa la API de Acceso a fuentes locales para permitir que los usuarios elijan sus fuentes locales favoritas

La API de Local Font Access proporciona un mecanismo para acceder a los datos de fuentes instalados localmente del usuario, incluidos los detalles de nivel superior, como nombres, estilos y familias, así como los bytes sin procesar de los archivos de fuentes subyacentes. Obtén información sobre cómo la app de edición de SVG Boxy SVG usa esta API.

Introducción

(Este artículo también está disponible en forma de video).

Boxy SVG es un editor de gráficos vectoriales. Su caso de uso principal es editar dibujos en formato de archivo SVG para crear ilustraciones, logotipos, íconos y otros elementos de diseño gráfico. Fue desarrollado por el desarrollador Jarosław Foksa y se lanzó inicialmente el 15 de marzo de 2013. Jarosław administra un blog de Boxy SVG en el que anuncia las nuevas funciones que agrega a la app. El desarrollador es un gran defensor del Project Fugu de Chromium y hasta tiene una etiqueta de Fugu en el rastreador de ideas de la app.

La app de SVG en Boxy editando el ícono SVG de Project Fugu.

API de Local Font Access en Boxy SVG

Una de las funciones que Jarosław publicó en su blog fue la API de Local Font Access. La API de Local Font Access permite a los usuarios acceder a sus fuentes instaladas localmente, incluidos detalles de nivel superior como nombres, estilos y familias, así como también bytes sin procesar de los archivos de fuentes subyacentes. En la siguiente captura de pantalla, puedes ver cómo le otorgué a la app acceso a las fuentes instaladas de forma local en mi MacBook y elegí la fuente Marker Felt para mi texto.

La app de Boxy SVG edita el SVG del ícono de Project Fugu y agrega el texto "Project Fugu rocks" establecido en la fuente Marker Felt, que se muestra seleccionada en el selector de fuentes.

El código subyacente es bastante sencillo. Cuando el usuario abre el selector de familia de fuentes por primera vez, la aplicación primero verifica si el navegador web admite la API de Local Font Access.

También busca la versión experimental anterior de la API y la usa si está presente. A partir de 2023, puedes ignorar de forma segura la API anterior, ya que solo estuvo disponible durante un período breve a través de marcas experimentales de Chrome, pero es posible que algunos derivados de Chromium aún la usen.

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

Si la API de Local Font Access no está disponible, el selector de familia de fuentes se volverá gris. El usuario verá un texto de marcador de posición en lugar de la lista de fuentes:

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

Selector de fuentes que muestra el mensaje &quot;Tu navegador no admite la API de Local Font Access&quot;.

De lo contrario, se utiliza la API de Local Font Access para recuperar la lista de todas las fuentes del sistema operativo. Observa el bloque try…catch, que se necesita para manejar los errores de permisos de forma correcta.

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

Una vez que se recupera la lista de fuentes locales, se crea un fontsIndex simplificado y normalizado a partir de ella:

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

Luego, el índice de fuentes normalizado se almacena en la base de datos de IndexedDB para que se pueda consultar fácilmente, compartir entre instancias de apps y conservar entre sesiones. Boxy SVG usa Dexie.js para administrar la base de datos:

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

Sección de almacenamiento de las Herramientas para desarrolladores de Chrome que muestra la tabla IndexedDB con la caché de fuentes.

Una vez que se propaga la base de datos, el widget del selector de fuentes puede consultarla y mostrar los resultados en la pantalla:

Selector de fuentes propagado con fuentes

Vale la pena mencionar que Boxy SVG renderiza la lista en un elemento personalizado llamado <bx-fontfamilypicker> y aplica diseño a cada elemento de la lista de fuentes para que se muestre en la familia de fuentes en particular. Para aislar el mapa del resto de la página, el SVG de Boxy usa el Shadow DOM en este y otros elementos personalizados.

Panel Elements de Chrome DevTools que muestra el selector de fuentes que se inspecciona: un elemento personalizado llamado &quot;bx-fontfamiliypicker&quot;.

Conclusiones

La función de fuentes locales fue muy popular: los usuarios disfrutaron el acceso a sus fuentes locales para sus diseños y creaciones. Cuando cambió la forma de la API y la función dejó de funcionar brevemente, los usuarios lo notaron de inmediato. Jarosław cambió rápidamente el código al patrón defensivo que se ve en el fragmento anterior, que funciona con Chrome actualizado y otros derivados de Chromium que pueden no haber cambiado a la versión más reciente. Prueba Boxy SVG y asegúrate de revisar las fuentes instaladas de forma local. Es posible que descubras algunos clásicos olvidados, como Zapf Dingbats o Webdings.