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 sobre elas

.

Fontes seguras da Web

Se você vem desenvolvendo a Web por tempo suficiente, pode se lembrar das chamadas fontes seguras da Web. Essas fontes estão disponíveis em quase todas as instâncias dos sistemas operacionais mais usados (ou seja, Windows, macOS, as distribuições mais comuns do Linux, Android e iOS). No início dos anos 2000, a Microsoft até liderou uma iniciativa chamada fontes principais TrueType para a Web, que forneciam essas fontes sem custo financeiro para download com o objetivo de que "sempre que você visitar um site da Web que as especifique, verá as páginas exatamente como o designer do site pretendia". Sim, isso inclui sites definidos na Comic Sans MS. Confira uma pilha de fontes clássicas seguras para a Web (com o substituto final de qualquer fonte sans-serif) que tenha esta aparência:

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

Fontes da Web

Os tempos em que fontes seguras da Web eram importantes já passaram muito tempo. Atualmente, temos fontes da Web, algumas delas até fontes variáveis que podem ser ajustadas ainda mais mudando os valores dos vários eixos expostos. Você pode usar fontes da Web declarando um bloco @font-face no início do CSS, que especifica os arquivos de fontes para download:

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

Depois disso, você pode usar a fonte da Web personalizada especificando a font-family normalmente:

body {
  font-family: 'FlamboyantSansSerif';
}

Fontes locais como vetor de impressão digital

A maioria das fontes da Web vem 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 fontes personalizadas sejam carregadas localmente. Se o usuário tiver o FlamboyantSansSerif instalado no sistema operacional, a cópia local será usada em vez de ela ser transferida por download:

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

Essa abordagem fornece um bom mecanismo substituto que potencialmente economiza largura de banda. Infelizmente, na Internet não podemos ter coisas boas. O problema com a função local() é que ela pode ser usada indevidamente por técnicas de impressão digital do navegador. Acontece que a lista de fontes que um usuário instalou pode ser bem identificada. Muitas empresas têm as próprias 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 alguém trabalha testando a existência de um grande número de fontes corporativas conhecidas, como a Google Sans. O invasor tentaria renderizar o texto definido nessas fontes em uma tela e medir os glifos. Se os glifos corresponderem ao formato conhecido da fonte corporativa, o invasor terá um hit. Se os glifos não corresponderem, o invasor vai saber que uma fonte de substituição padrão foi usada, já que a fonte corporativa não foi instalada. Para ver todos os detalhes sobre esse e outros ataques de impressão digital de navegador, leia o documento de pesquisa da Laperdix et al. (links em inglês)

as fontes da empresa separadas, mesmo que apenas a lista de fontes instaladas possa ser identificada. A situação com esse vetor de ataque se tornou tão ruim que, recentemente, a equipe do WebKit decidiu"incluir apenas fontes da Web [na lista de fontes disponíveis] 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 começo deste artigo pode ter trazido você para um estado de espírito negativo. Será que não podemos ter algo bom? Não se preocupe. Acreditamos que é possível e que talvez tudo não seja desesperado. Mas antes vou responder a uma pergunta que você pode estar se perguntando.

Por que precisamos da API Local Font Access quando há fontes da Web?

Historicamente, as ferramentas gráficas e de design de qualidade profissional têm sido difíceis de fornecer na Web. Um obstáculo foi a incapacidade de acessar e usar toda a variedade de fontes criadas e sugeridas por profissionais 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 vetoriais e às tabelas de fontes usadas pelos rasterizadores para renderizar os contornos de glifo. Da mesma forma, não há como acessar os dados binários de uma fonte da Web.

  • As ferramentas de design precisam de acesso aos bytes de fonte para fazer a própria implementação de layout OpenType e permitir que as ferramentas de design se conectem em níveis mais baixos, para ações como a execução de filtros vetoriais ou transformações nas formas de glifo.
  • Os desenvolvedores podem ter pilhas de fontes legadas para seus 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 fornecem.
  • Algumas fontes podem não ser licenciadas para envio na Web. Por exemplo, a Linotype tem uma licença para algumas fontes que incluem apenas uso em computadores.

A API Local Font 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.
  • A capacidade de solicitar acesso ao contêiner SFNT de baixo nível (orientado a bytes) para cada resultado de enumeração que inclui os dados completos da fonte

Suporte ao navegador

Compatibilidade com navegadores

  • 103
  • 103
  • x
  • x

Origem

Como usar a API Local Font Access

Detecção de recursos

Para verificar se a API Local Font Access é compatível, use:

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

Como enumerar fontes locais

Para ver uma lista das fontes instaladas localmente, é necessário chamar window.queryLocalFonts(). Na primeira vez, isso acionará uma solicitação de permissão, que o usuário pode aprovar ou negar. Se o usuário aprovar as fontes locais para serem consultadas, o navegador retornará uma matriz com dados de fontes que você pode repetir. 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ê tem interesse apenas em um subconjunto de fontes, também é possível filtrá-las com base nos nomes do PostScript adicionando um parâmetro postscriptNames.

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

Como acessar dados SFNT

O acesso SFNT completo está disponível por meio do método blob() do objeto FontData. SFNT é um formato de arquivo de fontes 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 com alta possibilidade de impressão digital. No entanto, os navegadores podem retornar qualquer coisa que quiserem. Por exemplo, navegadores focados em anonimato podem optar por fornecer apenas um conjunto de fontes padrão integradas no navegador. Da mesma forma, os navegadores não precisam fornecer os dados da tabela exatamente como eles aparecem no disco.

Sempre que possível, a API Local Font Access foi projetada para expor apenas as informações necessárias para permitir os casos de uso mencionados. As APIs do sistema podem produzir uma lista de fontes instaladas não em uma ordem aleatória ou ordenada, mas na ordem de instalação das fontes. 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 mantidos com 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 da 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

Se um site tiver recebido acesso às fontes locais do usuário, será possível conferir na folha de informações do site.

Persistência da permissão

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

Feedback

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

Fale sobre o design da API

Há algo na API que não funciona como você esperava? Ou há métodos ou propriedades ausentes que você precisa para implementar sua ideia? Tem alguma dúvida ou comentário sobre o modelo de segurança? Registre um problema de especificação no repositório do GitHub correspondente ou adicione suas ideias a um problema existente.

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 insira Blink>Storage>FontAccess na caixa Componentes. O Glitch funciona muito bem para compartilhar repetições rápidas e fáceis.

Mostrar suporte à API

Você planeja usar a API Local Font Access? Seu suporte público ajuda a equipe do Chrome a priorizar recursos e mostra a outros fornecedores de navegador como é importante oferecer suporte a eles.

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

Agradecimentos

A especificação da API Local Font 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).