Usar tipografia avançada com fontes locais

Saiba como a API Local Font Access permite acessar as fontes instaladas localmente do usuário e conferir detalhes de baixo n��vel sobre elas

Thomas Steiner
Thomas Steiner

Fontes seguras para a Web

Se você trabalha com desenvolvimento da Web há tempo suficiente, talvez se lembre das chamadas fontes seguras para Web. Essas fontes estão disponíveis em quase todas as instâncias dos sistemas operacionais mais usados, como Windows, macOS, as distribuições mais comuns do Linux, Android e iOS. No início dos anos 2000, a Microsoft liderou uma iniciativa chamada TrueType core fonts for the Web, que oferecia o download sem custo financeiro dessas fontes com o objetivo de "sempre que você visitar um site que as especifica, as páginas vão aparecer exatamente como o designer do site pretendia". Sim, isso inclui sites definidos no Comic Sans MS. Esta é uma pilha clássica de fontes seguras para a Web (com o substituto final de qualquer fonte sans-serif) parecida com esta:

body {
  font-family: Helvetica, Arial, sans-serif;
}

Fontes da Web

Os dias em que as fontes seguras para a Web eram importantes já se foram. Hoje, temos fontes da Web, algumas das quais são até mesmo fontes variáveis que podem ser ajustadas ainda mais mudando os valores dos vários eixos expostos. É possível usar fontes da Web declarando um bloco @font-face no início do CSS, que especifica os arquivos de fonte a serem transferidos por download:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: url('flamboyant.woff2');
}

Depois disso, é possível usar a fonte da Web personalizada especificando o font-family, como de costume:

body {
  font-family: 'FlamboyantSansSerif';
}

Fontes locais como vetor de impressão digital

A maioria das fontes da Web vem, bem, da Web. No entanto, um fato interessante é que a propriedade src na declaração @font-face, além da função url(), também aceita uma função local(). Isso permite que as fontes personalizadas sejam carregadas (surpresa!) localmente. Se o usuário tiver o FlamboyantSansSerif instalado no sistema operacional, a cópia local será usada em vez de ser transferida por download:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}

Essa abordagem oferece um bom mecanismo alternativo que pode economizar largura de banda. Infelizmente, não podemos ter coisas boas na Internet. O problema com a função local() é que ela pode ser usada para técnicas de impressão digital do navegador. A lista de fontes instaladas por um usuário pode ser bastante identificável. Muitas empresas têm fontes corporativas instaladas nos laptops dos funcionários. Por exemplo, o Google tem uma fonte corporativa chamada Google Sans.

O app Font Book do macOS mostrando uma prévia da fonte Google Sans.
A fonte Google Sans instalada no laptop de um funcionário do Google.

Um invasor pode tentar determinar em qual empresa uma pessoa trabalha testando a existência de um grande número de fontes corporativas conhecidas, como Google Sans. O invasor tentaria renderizar o texto definido nessas fontes em uma tela e medir os glifos. Se os glifos corresponderem à forma conhecida da fonte corporativa, o invasor terá uma correspondência. Se os glifos não corresponderem, o invasor saberá que uma fonte de substituição padrão foi usada, já que a corporativa não foi instalada. Para detalhes completos sobre esse e outros ataques de impressão digital do navegador, leia o artigo da pesquisa de Laperdix et al.

Além das fontes da empresa, até mesmo a lista de fontes instaladas pode ser identificada. A situação com esse vetor de ataque ficou tão ruim que, recentemente, a equipe do WebKit decidiu "incluir apenas [na lista de fontes disponíveis] fontes da Web e fontes que vêm com o sistema operacional, mas não as fontes instaladas localmente pelo usuário". (E aqui estou, com um artigo sobre como conceder acesso a fontes locais.)

API Local Font Access

O início deste artigo pode ter deixado você de mau humor. Podemos realmente não ter coisas boas? Não se preocupe. Acreditamos que sim, e talvez tudo não seja sem esperança. Mas primeiro, deixe-me responder uma pergunta que você pode estar se fazendo.

Por que a API Local Font Access é necessária quando há fontes da Web?

Ferramentas de design e gráficos de qualidade profissional sempre foram difíceis de oferecer na Web. Um problema é a incapacidade de acessar e usar toda a variedade de fontes sugeridas e construídas profissionalmente que os designers instalaram localmente. As fontes da Web permitem alguns casos de uso de publicação, mas não permitem o acesso programático às formas de glifo vetorial e às tabelas de fontes usadas por rastreadores para renderizar os contornos de glifo. Da mesma forma, não há como acessar dados binários de uma fonte da Web.

  • As ferramentas de design precisam ter acesso aos bytes da fonte para fazer a própria implementação de layout OpenType e permitir que as ferramentas de design sejam conectadas em níveis mais baixos, para ações como executar filtros de vetor ou transformações nas formas de glifos.
  • Os desenvolvedores podem ter pilhas de fontes legados para os aplicativos que estão trazendo para a Web. Para usar essas pilhas, elas geralmente exigem acesso direto aos dados da fonte, algo que as fontes da Web não oferecem.
  • Algumas fontes podem não estar licenciadas para exibição na Web. Por exemplo, a Linotype tem uma licença para algumas fontes que inclui apenas o uso em computadores.

A API Local Fonts Access é uma tentativa de resolver esses desafios. Ele consiste em duas partes:

  • Uma API de enumeração de fontes, que permite que os usuários concedam acesso ao conjunto completo de fontes do sistema disponíveis.
  • Em cada resultado de enumeração, a capacidade de solicitar acesso ao contêiner SFNT de baixo nível (orientado a bytes) que inclui todos os dados da fonte.

Suporte ao navegador

Compatibilidade com navegadores

  • Chrome: 103.
  • Edge: 103.
  • Firefox: não é compatível.
  • Safari: não é compatível.

Origem

Como usar a API Local Fonts Access

Detecção de recursos

Para verificar se a API Local Font Access tem suporte, use:

if ('queryLocalFonts' in window) {
  // The Local Font Access API is supported
}

Enumerar fontes locais

Para conferir uma lista das fontes instaladas localmente, chame window.queryLocalFonts(). Na primeira vez, isso vai acionar uma solicitação de permissão, que o usuário pode aprovar ou negar. Se o usuário aprovar a consulta das fontes locais, o navegador vai retornar uma matriz com dados de fontes que podem ser usados em um loop. Cada fonte é representada como um objeto FontData com as propriedades family (por exemplo, "Comic Sans MS"), fullName (por exemplo, "Comic Sans MS"), postscriptName (por exemplo, "ComicSansMS") e style (por exemplo, "Regular").

// Query for all available fonts and log metadata.
try {
  const availableFonts = await window.queryLocalFonts();
  for (const fontData of availableFonts) {
    console.log(fontData.postscriptName);
    console.log(fontData.fullName);
    console.log(fontData.family);
    console.log(fontData.style);
  }
} catch (err) {
  console.error(err.name, err.message);
}

Se você tiver interesse apenas em um subconjunto de fontes, também poderá filtrá-las com base nos nomes PostScript adicionando um parâmetro postscriptNames.

const availableFonts = await window.queryLocalFonts({
  postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});

Como acessar dados SFNT

O acesso total ao SFNT está disponível pelo método blob() do objeto FontData. O SFNT é um formato de arquivo de fonte que pode conter outras fontes, como PostScript, TrueType, OpenType, Web Open Font Format (WOFF) e outras.

try {
  const availableFonts = await window.queryLocalFonts({
    postscriptNames: ['ComicSansMS'],
  });
  for (const fontData of availableFonts) {
    // `blob()` returns a Blob containing valid and complete
    // SFNT-wrapped font data.
    const sfnt = await fontData.blob();
    // Slice out only the bytes we need: the first 4 bytes are the SFNT
    // version info.
    // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
    const sfntVersion = await sfnt.slice(0, 4).text();

    let outlineFormat = 'UNKNOWN';
    switch (sfntVersion) {
      case '\x00\x01\x00\x00':
      case 'true':
      case 'typ1':
        outlineFormat = 'truetype';
        break;
      case 'OTTO':
        outlineFormat = 'cff';
        break;
    }
    console.log('Outline format:', outlineFormat);
  }
} catch (err) {
  console.error(err.name, err.message);
}

Demonstração

Confira a API Local Font Access em ação na demonstração abaixo. Confira também o código-fonte. A demonstração mostra um elemento personalizado chamado <font-select> que implementa um seletor de fontes local.

Considerações sobre privacidade

A permissão "local-fonts" parece fornecer uma superfície altamente identificável por impressão digital. No entanto, os navegadores são livres para retornar o que quiserem. Por exemplo, navegadores com foco em anonimato podem escolher fornecer apenas um conjunto de fontes padrão integradas ao navegador. Da mesma forma, os navegadores não precisam fornecer dados de tabela exatamente como aparecem no disco.

Sempre que possível, a API Local Fonts Access foi projetada para expor apenas as informações necessárias para ativar os casos de uso mencionados. As APIs do sistema podem produzir uma lista de fontes instaladas não em uma ordem aleatória ou classificada, mas na ordem de instalação da fonte. Retornar exatamente a lista de fontes instaladas fornecida por essa API do sistema pode expor outros dados que podem ser usados para impressão digital, e os casos de uso que queremos ativar não são assistidos por manter essa ordem. Como resultado, essa API exige que os dados retornados sejam classificados antes de serem retornados.

Segurança e permissões

A equipe do Chrome projetou e implementou a API Local Font Access usando os princípios básicos definidos em Como controlar o acesso a recursos avançados da plataforma Web, incluindo controle do usuário, transparência e ergonomia.

Controle do usuário

O acesso às fontes de um usuário está totalmente sob o controle dele e não será permitido, a menos que a permissão "local-fonts", conforme listado no registro de permissões, seja concedida.

Transparência

A informação de que um site recebeu acesso às fontes locais do usuário vai aparecer na ficha de informações do site.

Persistência de permissões

A permissão "local-fonts" será mantida entre as atualizações de página. Ele pode ser revogado na página informações do site.

Feedback

A equipe do Chrome quer saber sobre suas experiências com a API Local Font Access.

Conte sobre o design da API

Há algo na API que não funciona como esperado? Ou há métodos ou propriedades ausentes que você precisa para implementar sua ideia? Tem dúvidas ou comentários sobre o modelo de segurança? Registre um problema de especificação no repositório do GitHub (link em inglês) correspondente ou adicione sua opinião a um problema.

Informar um problema com a implementação

Você encontrou um bug na implementação do Chrome? Ou a implementação é diferente da especificação? Registre um bug em new.crbug.com. Inclua o máximo de detalhes possível, instruções simples para reprodução e digite Blink>Storage>FontAccess na caixa Components. O Glitch é ótimo para compartilhar reprosagens rápidas e fáceis.

Mostrar suporte à API

Você planeja usar a API Local Font Access? Seu apoio público ajuda a equipe do Chrome a priorizar recursos e mostra a outros fornecedores de navegadores a importância de oferecer suporte a eles.

Envie um tweet para @ChromiumDev usando a hashtag #LocalFontAccess e informe onde e como você está usando.

Agradecimentos

A especificação da API Local Fonts Access foi editada por Emil A. Eklund, Alex Russell, Joshua Bell e Olivier Yiptong. Este artigo foi revisado por Joe Medley, Dominik Röttsches e Olivier Yiptong. Imagem principal de Brett Jordan no Unsplash (links em inglês).