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 de forma local 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. Aprende cómo la app de edición de SVG de Boxy SVG usa esta API.

Introducción

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

Boxy SVG es un editor de gráficos vectoriales. Su principal caso de uso es la edición de dibujos en formato de archivo SVG para la creación de ilustraciones, logotipos, íconos y otros elementos de diseño gráfico. Fue desarrollado por la empresa desarrolladora polaca Jarosław Foksa y se lanzó inicialmente el 15 de marzo de 2013. Jarosław publica un blog de SVG de Boxy en el que anuncia las nuevas funciones que agrega a la app. El desarrollador es un firme partidario del Proyecto Fugu de Chromium e incluso tiene una etiqueta 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 Font Access en SVG de Boxy

Una característica que Jarosław envió en su blog fue la API de Local Font Access. La API de Local Font Access permite a los usuarios acceder a las fuentes instaladas localmente, incluidos los detalles de nivel superior como nombres, estilos y familias, además de los bytes sin procesar de los archivos de fuentes subyacentes. En la siguiente captura de pantalla, puedes ver cómo he otorgado acceso a la app a las fuentes instaladas localmente en mi MacBook y elegí la fuente Marker Felt para mi texto.

La app de Boxy SVG edita el ícono SVG 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 comprueba si el navegador web es compatible con la API de Local Font Access.

También comprueba la antigua versión experimental de la API y la utiliza si está presente. A partir de 2023, puedes ignorar de forma segura la API anterior, ya que solo estaba disponible por un período breve mediante funciones 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 la familia de fuentes se pondrá de color gris. Se mostrará al usuario un texto de marcador de posición 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, la API de Local Font Access se usa 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 una fontsIndex simplificada y normalizada 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 normalizadas se almacena en la base de datos IndexedDB para que se pueda consultar, compartir entre instancias de app y conservar entre sesiones fácilmente. 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 Storage de las Herramientas para desarrolladores de Chrome que muestra la tabla IndexedDB con la caché de fuentes.

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

Selector de fuentes con fuentes propagadas.

Vale la pena mencionar que Boxy SVG renderiza la lista en un elemento personalizado llamado <bx-fontfamilypicker> y aplica un estilo a cada elemento de la lista de fuentes para que se muestre en la familia de fuentes específica. Para aislarlo del resto de la página, Boxy SVG utiliza el Shadow DOM en este y otros elementos personalizados.

El panel Elements de las Herramientas para desarrolladores de Chrome muestra el selector de fuentes que se está inspeccionando: un elemento personalizado llamado &quot;bx-fontfamiliypicker&quot;.

Conclusiones

La función de fuentes locales es muy popular, y los usuarios disfrutan del acceso a las fuentes locales para sus diseños y creaciones. Cuando la forma de la API cambió y la función dejaba de funcionar brevemente, los usuarios lo notaron inmediatamente. Jarosław se apresuró a cambiar 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 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 localmente. Es posible que descubras algunos clásicos olvidados por mucho tiempo, como Zapf Dingbats o Webdings.