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. Descubre 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 Boxy SVG edita el SVG del ícono de Project Fugu.

API de Local Fonts Access en Boxy SVG

Una función que Jarosław mencionó 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 de forma local, incluidos los detalles de nivel superior, como nombres, estilos y familias, así como los 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. Se mostrará un texto de marcador de posición al usuario en lugar de la lista de fuentes:

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

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

De lo contrario, se usa 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 controlar 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 Almacenamiento de Chrome DevTools 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 aislarse del resto de la página, Boxy SVG usa el Shadow DOM en este y otros elementos personalizados.

Panel de elementos de las Herramientas para desarrolladores de Chrome que muestra el selector de fuentes que se inspecciona: un elemento personalizado llamado &quot;bx-fontfamiliypicker&quot;.

Conclusiones

La función de fuentes locales ha sido muy popular, y los usuarios disfrutan de 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 puedes ver en el fragmento anterior, que funciona con la versión actualizada de Chrome y también con otras derivadas 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.