4 pontos por GN⁺ 2026-03-30 | 1 comentários | Compartilhar no WhatsApp
  • Pretext é uma biblioteca pura em JavaScript/TypeScript para calcular a altura e a distribuição de linhas de texto multilinha sem acesso ao DOM, com suporte tanto para navegador quanto para ambiente de servidor
  • Como não usa APIs de medição do DOM como getBoundingClientRect, elimina o custo de reflow de layout e garante precisão com uma lógica própria de medição baseada no motor de fontes
  • Por meio das APIs prepare() / layout(), pré-processa o texto e realiza cálculo rápido de altura com operações puramente aritméticas usando dados de largura em cache
  • Suporta emoji, texto com direções mistas (bidi) e vários idiomas, além de fornecer os mesmos resultados em Canvas, SVG, WebGL e renderização no servidor
  • É um motor de texto de alto desempenho que pode ser usado para implementar layouts de UI precisos, como scroll virtualizado, validação de overflow de texto e posicionamento de texto flutuante

Visão geral

  • Pretext é uma biblioteca pura em JavaScript/TypeScript para medição e layout de texto multilinha, com suporte a DOM, Canvas, SVG e renderização no servidor
  • Não usa APIs de medição do DOM (getBoundingClientRect, offsetHeight etc.), portanto elimina o custo de reflow de layout
  • Oferece desempenho preciso e rápido por meio de uma lógica própria de medição baseada no motor de fontes do navegador
  • Suporta todos os idiomas, emoji e texto com direções mistas (bidi), além de lidar com diferenças entre navegadores

Instalação e demo

Principais recursos

  • O Pretext oferece duas formas principais de uso
  • 1. Medição da altura de parágrafos sem acesso ao DOM

    • prepare() pré-processa o texto, faz normalização de espaços, separação de segmentos, aplicação de regras de glue e medição baseada em canvas, e retorna um handle opaco (opaque handle)
    • layout() usa dados de largura em cache para calcular altura e número de linhas com operações puramente aritméticas
    • Para o mesmo texto e configuração, não é necessário chamar prepare() repetidamente; em redimensionamentos, basta executar layout() novamente
    • Com a opção { whiteSpace: 'pre-wrap' }, preserva espaços, tabs (\t) e quebras de linha (\n)
    • Resultado de benchmark: prepare() cerca de 19ms (com base em 500 textos), layout() cerca de 0.09ms
    • O valor de altura retornado pode ser usado em recursos de UI como:
      • Cálculo preciso de altura em virtualização e oclusão
      • Sistemas de layout baseados em JS (ex.: masonry, estruturas semelhantes a flexbox)
      • Validação de overflow de texto baseada em IA
      • Manutenção da posição de scroll ao carregar texto
  • 2. Composição manual do layout de parágrafos

    • prepareWithSegments() gera dados por segmento
    • layoutWithLines() retorna, em largura fixa, o texto e as informações de largura de cada linha
    • walkLineRanges() percorre a largura e o intervalo de cursor de cada linha sem criar strings de texto
      • Ex.: permite ajuste de layout com busca binária para testar várias larguras e encontrar a quantidade de linhas e altura adequadas
    • layoutNextLine() faz o layout sequencialmente, uma linha por vez, quando cada linha tem largura diferente
      • Ex.: posicionamento de texto flutuante para fazer o texto contornar imagens
    • Essa abordagem também pode ser aplicada da mesma forma em Canvas, SVG, WebGL e renderização no servidor

Resumo da API

  • API para medição básica

    • prepare(text, font, options?): analisa e mede o texto, retornando um handle para passar a layout()
    • layout(prepared, maxWidth, lineHeight): calcula a altura do texto e o número de linhas de acordo com a largura e a altura de linha fornecidas
  • API para layout manual

    • prepareWithSegments(text, font, options?): retorna dados por segmento
    • layoutWithLines(prepared, maxWidth, lineHeight): inclui texto, largura e informações de cursor de cada linha
    • walkLineRanges(prepared, maxWidth, onLine): passa para o callback a largura e o intervalo de cursor de cada linha
    • layoutNextLine(prepared, start, maxWidth): executa o layout em forma de iterador por linha
    • Inclui definições de tipo LayoutLine, LayoutLineRange, LayoutCursor
  • Outros utilitários

    • clearCache(): limpa o cache interno
    • setLocale(locale?): define a localidade e limpa o cache (sem afetar o estado existente)

Limitações e observações

  • O Pretext não é um motor completo de renderização de fontes
  • Propriedades CSS alvo padrão
    • white-space: normal
    • word-break: normal
    • overflow-wrap: break-word
    • line-break: auto
  • Ao usar { whiteSpace: 'pre-wrap' }, preserva espaços, tabs e quebras de linha, aplicando tab-size: 8
  • No macOS, a fonte system-ui não é adequada para a precisão do layout(), portanto recomenda-se usar um nome de fonte explícito
  • Por causa de overflow-wrap: break-word, em larguras muito estreitas pode haver quebra dentro das palavras, mas a divisão ocorre apenas com base em unidades de caractere (grapheme)

Desenvolvimento

  • Consulte DEVELOPMENT.md para ambiente e comandos de desenvolvimento

Contribuição e contexto

  • Dá continuidade às ideias do projeto text-layout de Sebastian Markbage
  • A estrutura evoluiu herdando e desenvolvendo o design de shaping baseado em canvas measureText, tratamento bidi do pdf.js e quebra de linha em streaming

1 comentários

 
GN⁺ 2026-03-30
Comentários do Hacker News
  • Este projeto é realmente impressionante
    Resolve o problema de calcular com eficiência a altura de texto com quebra de linha sem realmente renderizar o texto na página web
    Faz cache da largura e altura de segmentos divididos por palavra e implementa diretamente o algoritmo de quebra de linha do navegador
    É um trabalho muito difícil por causa do tratamento de hífen, emoji, chinês e outros caracteres, além das diferenças de renderização entre navegadores (incluindo Safari)
    Usa o dataset corpora e a página de teste de accuracy para testar em comparação com navegadores reais

    • Eu também já fiz algo parecido. É uma versão simples de 2 KB chamada uWrap.js, implementada sem IA
      No meu código, com texto ASCII, leva 80 ms; o pretext leva 2200 ms
      Ainda não testei a precisão, mas pretendo fazer isso hoje à noite
      Já existem PRs de melhoria de desempenho abertas na issue #18
    • Motores de layout de texto são notoriamente difíceis
      Eu também sofri no passado tentando renderizar texto multilinha em canvas
      Este projeto é muito mais útil porque está ligado diretamente ao DOM
      Exemplo: demo do Scrawl
    • Pela descrição, na prática parece ser um método de renderizar os segmentos em canvas para medir
      Pode ser mais lento que APIs nativas e não há garantia de que use a mesma lógica da renderização não-canvas do navegador
    • Na prática, isso não implementa diretamente o algoritmo de renderização de texto do navegador
      É uma abordagem de renderizar no canvas e medir depois, fornecendo basicamente uma API para análise de layout de texto
    • Tive trabalho com cálculo de altura de texto ao criar legendas dinâmicas para vídeos no Remotion, e essa biblioteca parece que vai ajudar bastante
  • Isto é realmente uma funcionalidade esperada há muito tempo
    Faz tempo que era difícil implementar direito coisas como acordeões responsivos
    O padrão de evolução da web sempre foi ① surgem requisitos complexos → ② hacks em JS/CSS → ③ padronização
    Desta vez, parece um estágio 2 bem feito, não um hack
    Pelo RESEARCH.md, até as diferenças de medição de emoji entre navegadores foram estudadas em detalhe
    A manutenção deve ser difícil, mas isso parece um grande ponto de virada para a evolução da web

    • Acordeões responsivos agora já são possíveis com CSS, mas esse tipo de API ainda fazia falta
      Desta vez, é interessante que a IA foi usada ativamente no processo de desenvolvimento. Parece que a maior parte foi implementada com o agente do Cursor
  • Segundo o autor da biblioteca, ele fez Claude Code e Codex aprenderem dados de ground truth dos navegadores e repetiu medições ao longo de várias semanas
    Veja o tweet relacionado
    Parece que Autoresearch também foi usado em parte

  • Gostei especialmente do exemplo de reflow baseado em shape
    Queria aplicar isso no Ensō (enso.sonnet.io), mas me contive para manter a simplicidade
    O exemplo de acordeão também pode ser implementado com interpolate-size do CSS
    Veja o artigo do Josh Comeau
    O exemplo de bolhas de texto pode ser feito de forma parecida com text-wrap: balance | pretty

    • Mas balance ou pretty não resolvem completamente
      Muitas vezes você não quer simplesmente deixar o comprimento das linhas uniforme
      Issue relacionada do CSSWG: #191
    • text-wrap ajuda a ajustar o número de palavras por linha, mas o problema da margem direita continua
  • O pretext não usa canvas.measureText diretamente; você passa o texto e os atributos para uma API JS, e ele calcula o layout automaticamente
    Antes, era preciso usar measureText diretamente ou portar o harfbuzz para o navegador
    Parece mais o resultado de combinar bem elementos já existentes do que um avanço técnico radical
    Ainda assim, fico curioso sobre a diferença em relação a Skia-wasm / Canvaskit

    • A diferença é a simplicidade. O pretext não é wasm
    • O Skia é um enorme motor de renderização. Ele fornece uma API de renderização independente de dispositivo como o Flutter
      O diferencial do pretext é ter implementado renderização de glifos em TypeScript puro com ajuda de IA
      Parece a diferença entre implementar diretamente o ffmpeg em C e apenas chamá-lo a partir de Dart
      Esse tipo de tentativa mostra novas possibilidades para FOSS client-side
    • Se o autor estiver certo, isso vai trazer uma grande mudança para frameworks GUI web e editores de texto rico
  • No ano passado criei um sistema de composição tipográfica para folhetos impressos em HTML, e calculei repetidamente os limites das caixas com a Selection API para lidar com quebra de linha e evitar linhas órfãs
    Ainda funciona bem hoje, mas existe um hack de off-by-one cujo motivo eu não entendo
    A funcionalidade iterativa de geração de linhas do pretext é realmente muito bem-vinda

  • No ambiente Fedora + Firefox, todas as demos parecem quebradas
    Ex.: captura de tela

    • Isso mostra que projetos desse tipo acabam sendo uma sequência interminável de perseguir edge cases
  • Esse tipo de recurso na verdade deveria ser oferecido como API padrão do navegador
    Fico curioso sobre como fazer uma solicitação de funcionalidade ao W3C, e se algo como votação da comunidade seria possível

    • Já existe a proposta de Font Metrics API
      Mas os fornecedores de navegadores não estão priorizando isso
      No momento, o Chrome está mais focado em APIs relacionadas a IA
  • O engine Sciter já tem a funcionalidade Graphics.Text
    É um elemento de renderização de texto baseado em canvas ao qual se pode aplicar estilos CSS diretamente

  • A busca de texto no navegador (Ctrl+F) não funciona direito em listas com rolagem virtual
    Para resolver esse tipo de problema, talvez seja necessária uma nova API de “Search”, e não JS

    • Havia a Virtual Scroller API, mas quase não houve progresso
      Projetos relacionados: Display Locking, documentação da MDN
    • A busca nativa só percorre nós que existem no DOM
      Como os itens fora da tela de listas virtualizadas não estão no DOM, eles não podem ser encontrados
      Para resolver isso, seria necessário um novo contrato do navegador que integrasse seleção, foco, posição de rolagem e navegação entre correspondências
      Mas é improvável que os sites adotem isso de forma consistente