Saiba como a API Local Fonts Access permite acessar as fontes instaladas localmente pelo usuário e receber detalhes de baixo nível sobre elas.
Fontes seguras da 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 incluiu sites definidos em
Comic Sans MS. Confira como um
stack de fontes seguras da Web clássico (com a substituição final de qualquer
fonte sans-serif
)
pode ficar:
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. Na Internet,
infelizmente, não podemos ter coisas legais. O problema com a função local()
é que ela pode ser
usada indevidamente para 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.
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.)
A API Local Fonts Access
O início deste artigo pode ter deixado você de mau humor. Não podemos ter coisas legais? Não se preocupe. Acreditamos que podemos, e talvez não seja o fim. Mas primeiro, vamos responder a uma pergunta que você pode estar fazendo a si mesmo.
Por que precisamos da API Local Fonts Access quando há fontes da Web?
Ferramentas de design e gráficos de qualidade profissional sempre foram difíceis de oferecer na Web. Um obstáculo tem sido a incapacidade de acessar e usar a variedade completa de fontes criadas e sugeridas 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 os 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 ter licença para entrega pela 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
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 sensível a impressões digitais. 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. O retorno exato da lista de fontes instaladas fornecida por uma 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 auxiliados pela manutenção dessa 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 poderosos 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 listada no
registro de permissões, seja concedida.
Transparência
A informação sobre se 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 os recarregamentos de página. Ele pode ser revogado na página
informações do site.
Feedback
A equipe do Chrome quer saber sobre sua experiência com a API Local Fonts 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? Envie um problema de especificação no repositório do GitHub correspondente ou adicione sua opinião 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 digite Blink>Storage>FontAccess
na caixa Components.
O Glitch é ótimo para compartilhar reprosagens rápidas e fáceis.
Mostrar suporte para a API
Você planeja usar a API Local Fonts 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.
Links úteis
- Explicação
- Especificação do rascunho
- Bug do Chromium para enumeração de fontes
- Bug do Chromium para acesso à tabela de fontes
- Entrada do ChromeStatus
- Repositório do GitHub
- Análise da TAG
- Posição dos padrões do Mozilla
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.