RecursosPreçosDocumentaçãoBlog
Obtenha mote
← Blog
Engenharia·2 de julho de 2026·5 min de leitura

Por que deixamos a renderização dos caracteres a cargo da plataforma em vez de desenhá-los diretamente

Você já percebeu que o hangul parece normal, mas somente os caracteres chineses parecem um pouco estranhos? Quando decidimos não desenhar os caracteres diretamente e deixar essa tarefa a cargo do mecanismo de texto da plataforma, nosso trabalho deixa de ser \"o que desenhar\" e passa a ser \"qual fonte pedir\".

m
mote
Comparação da mesma sequência de caracteres renderizada com os subconjuntos KR e JP da Noto Sans CJK. O hangul é igual, mas as formas de caracteres chineses como os que significam osso e reto são diferentes.
Comparação da mesma sequência de caracteres renderizada com os subconjuntos KR e JP da Noto Sans CJK. O hangul é igual, mas as formas de caracteres chineses como os que significam osso e reto são diferentes.

Muitas coisas precisam acontecer até que o texto apareça na tela. É preciso transformar caracteres em formas, dividir as linhas e desenhá-los em pixels. Também é necessário lidar com tudo, desde a composição do hangul até caracteres chineses, emojis, escrita árabe e fallback de fontes.

Nós não recriamos esse processo. Deixamos essa tarefa a cargo do Pango e do HarfBuzz no Linux, do DirectWrite no Windows e do TextKit 2 no macOS.

Isso porque é uma área que os sistemas operacionais vêm aprimorando há muito tempo. Se nós mesmos a implementássemos, havia uma grande possibilidade de a qualidade piorar justamente nas partes mais difíceis, como caracteres CJK e acessibilidade. Também entendemos que o diferencial do produto não estava no renderizador em si, mas na tipografia e em uma experiência de uso que parecesse nativa da plataforma.

Em vez disso, nós decidimos quando criar

O layout de texto consome tempo e memória para ser criado. Documentos grandes têm dezenas de milhares de linhas, então não era possível criar todas elas.

Por isso, criamos apenas as linhas visíveis na tela e as que ficam ao redor delas. As linhas que se afastam da tela são descartadas imediatamente. Mesmo depois de rolar por muito tempo um documento de 1 MB, apenas cerca de quarenta a sessenta layouts permanecem ativos.

Estimamos previamente a altura das linhas que ainda não foram criadas. Isso é necessário porque a barra de rolagem precisa conhecer o comprimento total do documento.

Naturalmente, às vezes a estimativa está errada. Se a altura recém-calculada for diferente da esperada, o conteúdo da tela pode se deslocar. Nesse caso, corrigimos a posição da rolagem pela diferença no mesmo quadro. Aos olhos do usuário, deve parecer que nada aconteceu.

As linhas em que o hangul está sendo composto são renderizadas sem formatação. Isso ocorre porque, se atributos como negrito ou oculto forem aplicados durante a digitação, a quantidade de caracteres reconhecida pelo método de entrada poderá ficar diferente da quantidade exibida na tela.

O primeiro problema que encontramos foi a aparência dos caracteres chineses

Quando deixamos algo a cargo da plataforma, também herdamos seus valores padrão.

A fonte padrão da interface do Ubuntu não contém hangul. Por isso, a fonte escolhida pelo sistema como substituta era a Noto Sans CJK JP. O hangul era exibido corretamente, mas alguns caracteres chineses apareciam com formas japonesas. Isso acontece porque há caracteres que, embora tenham o mesmo código Unicode, possuem formas diferentes na Coreia e no Japão.

A solução foi simples. Colocamos a Noto Sans CJK KR antes das demais na lista de fontes da interface.

Mas havia algo ainda mais importante. Se nós não definirmos o critério, a plataforma responderá com base nos próprios valores padrão, e não no idioma do usuário.

Criamos formas adequadas para cada idioma por meio da ordem das fontes

A pilha de fontes procura os caracteres começando pela primeira fonte. Somente os caracteres que não existem nela passam para a fonte seguinte.

Aproveitando esse comportamento, podemos exibir o alfabeto latino em uma fonte serifada e o hangul em uma fonte sem serifa. Uma única linha de configuração permite combinar fontes adequadas a cada idioma.

Quando a fonte de leitura fica em branco, usamos a fonte do corpo do texto sem alterações. Se uma fonte diferente for especificada, ela será aplicada somente no modo de leitura. No modo de código-fonte, usamos a fonte de código.

O tamanho dos caracteres acompanha as configurações de acessibilidade do sistema operacional. Assim, quem aumentou o tamanho do texto no sistema não precisa configurá-lo novamente em cada aplicativo.

A aba Aparência do cartão de configurações — o campo de fontes contém a pilha completa, separada por vírgulas, e o campo da fonte de leitura está vazio, exibindo em texto esmaecido "Igual ao corpo do texto".
A aba Aparência do cartão de configurações — o campo de fontes contém a pilha completa, separada por vírgulas, e o campo da fonte de leitura está vazio, exibindo em texto esmaecido "Igual ao corpo do texto".

O leitor de tela também aproveita a mesma base. Como os recursos de acessibilidade já estão conectados à camada de texto da plataforma, basta fornecermos corretamente o conteúdo da linha solicitada.

Se faltar uma tradução, a compilação falha

As frases da interface são escritas em inglês e usadas como chaves na tabela de traduções. Quando não há tradução, a frase em inglês aparece na tela sem alterações.

O problema é que isso não gera um erro. Mesmo quando falta uma tradução, o inglês simplesmente aparece misturado ao restante do conteúdo.

Por isso, criamos um script de verificação. Ele reúne as frases realmente usadas no código e as compara com as tabelas de tradução de 12 idiomas. Se uma frase for adicionada a apenas um idioma ou se alguma tradução for esquecida, a compilação falha.

Cada tabela de traduções tem 304 chaves, e 282 frases são realmente usadas no código. Os idiomas disponíveis são English, 한국어, 日本語, 简体中文, 繁體中文, Deutsch, Français, Español, Português (Brasil), Русский, Italiano e Polski.

A aba Geral das configurações — a primeira linha é o menu suspenso de idioma e, como 한국어 está selecionado, os nomes de todos os itens abaixo também estão em coreano.
A aba Geral das configurações — a primeira linha é o menu suspenso de idioma e, como 한국어 está selecionado, os nomes de todos os itens abaixo também estão em coreano.

Quando o idioma é alterado, abrimos uma nova janela com o mesmo documento e fechamos a janela existente. Se traduzíssemos um por um as centenas de widgets que já estão abertos, haveria uma grande possibilidade de deixarmos algum de fora. Concluímos que é mais seguro reabrir a janela uma única vez no momento da troca de idioma do que manter um fluxo complexo que quase nunca é usado.

Quando deixamos a tarefa a cargo da plataforma, o resultado também se parece com ela

Mesmo com o mesmo documento, os pixels não são exatamente iguais em todos os sistemas operacionais. Isso acontece porque cada um suaviza os caracteres de uma maneira diferente e usa fontes padrão diferentes.

Não tentamos forçar resultados idênticos. Em vez disso, mantemos os mesmos critérios do documento, como a proporção do tamanho dos caracteres, a altura da linha e as margens.

Os resultados do fallback também podem variar de acordo com as fontes instaladas. Por isso, em vez de prometer um resultado específico, mostramos de maneira transparente a ordem em que as fontes são solicitadas. O usuário também pode alterá-la diretamente.

Não desenhar diretamente não significa que a responsabilidade desaparece. É preciso definir com ainda mais precisão o que será delegado e em que ordem cada recurso será solicitado.

← AnterioresPor que colocamos interfaces específicas de cada plataforma sobre um único núcleo em RustMais recentes →Por que a digitação não fica lenta mesmo quando o documento cresce
Acompanhe por RSS.
Deixe só a escrita.
Português (Brasil)
© 2026 mote