Como o app de edição de imagens vetoriais Boxy SVG usa a API Local Font Access para permitir que os usuários escolham as fontes locais favoritas

A API Local Font Access oferece um mecanismo para acessar os dados de fonte do usuário instalados localmente, incluindo detalhes de nível superior, como nomes, estilos e famílias, além dos bytes brutos dos arquivos de fonte subjacentes. Saiba como o app de edição Boxy SVG usa essa API.

Introdução

Este artigo também está disponível na forma de vídeo.

O Boxy SVG é um editor de gráficos vetoriais. O principal caso de uso dele é editar desenhos no formato de arquivo SVG para criar ilustrações, logotipos, ícones e outros elementos de design gráfico. Desenvolvido pelo desenvolvedor polonês Jarosław Foksa, ele foi lançado inicialmente em 15 de março de 2013. Jarosław tem um blog Boxy SVG no qual anuncia os novos recursos que adicionou ao app. O desenvolvedor é um forte defensor do Projeto Fugu do Chromium e até tem uma tag Fugu no rastreador de ideias do app.

O app Boxy SVG editando o ícone do Project Fugu SVG.

API Local Font Access no Boxy SVG

Uma adição de recurso que Jarosław postou no blog foi a API Local Font Access. A API Local Font Access permite que os usuários acessem as fontes instaladas localmente, incluindo detalhes de nível superior, como nomes, estilos e famílias, além dos bytes brutos dos arquivos de fonte subjacentes. Na captura de tela a seguir, você pode ver como concedi ao app acesso às fontes instaladas localmente no meu MacBook e escolhi a fonte Marker Felt para o texto.

O app Boxy SVG editando o ícone do Project Fugu SVG adicionando o texto "Project Fugu rocks" definido no campo de fonte Marker Felt, mostrado selecionado no seletor de fontes.

O código subjacente é bastante simples. Quando o usuário abre o seletor de família de fontes pela primeira vez, o aplicativo primeiro verifica se o navegador da Web é compatível com a API Local Font Access.

Ele também verifica a antiga versão experimental da API e a usa, se ela estiver presente. A partir de 2023, você pode ignorar a API antiga com segurança, já que ela estava disponível apenas por um curto período usando flags experimentais do Chrome, mas alguns derivados do Chromium ainda podem usá-la.

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

Se a API Local Font Access não estiver disponível, o seletor de família de fontes ficará cinza. Um texto marcador de posição será exibido para o usuário em vez da lista de fontes:

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

Seletor de fonte mostrando a mensagem &quot;Seu navegador não é compatível com a API Local Font Access&quot;.

Caso contrário, a API Local Font Access será usada para recuperar a lista de todas as fontes do sistema operacional. Observe o bloco try…catch, que é necessário para processar erros de permissão corretamente.

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

Depois que a lista de fontes locais é recuperada, uma fontsIndex simplificada e normalizada é criada a partir dela:

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

O índice de fontes normalizadas é armazenado no banco de dados IndexedDB para que possa ser facilmente consultado, compartilhado entre instâncias de apps e preservado entre sessões. O Boxy SVG usa o Dexie.js para gerenciar o banco de dados:

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

Seção &quot;Armazenamento&quot; do Chrome DevTools mostrando a tabela IndexedDB com o cache de fontes.

Depois que o banco de dados for preenchido, o widget do seletor de fontes poderá consultá-lo e exibir os resultados na tela:

Seletor de fonte preenchido com fontes.

Vale mencionar que o Boxy SVG renderiza a lista em um elemento personalizado chamado <bx-fontfamilypicker> e define o estilo de cada item da lista de fontes para que ele seja exibido na família de fontes específica. Para isolar do restante da página, o Boxy SVG usa o Shadow DOM nesse e em outros elementos personalizados.

Painel Elements do Chrome DevTools mostrando o seletor de fontes sendo inspecionado: um elemento personalizado chamado &quot;bx-fontfamiliypicker&quot;.

Conclusões

O recurso de fontes locais ficou bastante conhecido, e os usuários estão aproveitando as fontes locais para designs e criações. Quando o formato da API mudou e o recurso quebrou brevemente, os usuários observaram imediatamente. O Jarosław foi rápido para mudar o código para o padrão defensivo que você pode ver no snippet acima, que funciona com o Chrome atualizado e também outros derivados do Chromium que podem não ter mudado para a versão mais recente. Teste o Boxy SVG e confira as fontes instaladas localmente. Talvez você encontre alguns clássicos, como Zapf Dingbats ou Webdings.