Por que o texto em um PDF não é realmente texto
Visão geral
Seção intitulada “Visão geral”Quando você lê um PDF, você vê palavras. O arquivo não contém palavras. Ele contém instruções para pintar formas em coordenadas, e as formas por acaso se parecem com letras. A lacuna entre o que um PDF desenha e o que ele significa é a razão pela qual uma página pode parecer impecável e ainda assim copiar como algo sem sentido. Esta página trata dessa lacuna e do pequeno mapa, separado, que a fecha.
Por que isso importa
Seção intitulada “Por que isso importa”Tudo o que você faz com um PDF enquanto texto — selecioná-lo, copiá-lo,
pesquisá-lo, indexá-lo para recuperação, lê-lo em voz alta com um leitor de tela
— depende de recuperar caracteres que o arquivo nunca armazenou diretamente. Se
essa recuperação falha, a falha é invisível. A página ainda renderiza. Ninguém
percebe até que alguém copie um parágrafo para um e-mail e obtenha □□□□, ou
pesquise em um contrato de 400 páginas por uma cláusula que está claramente lá e
não encontre nada.
Essa é uma classe de bug cara precisamente porque ela sobrevive a toda revisão visual. Não há nada para ver. O documento parece confiável e é, para fins de máquina, mudo.
A versão resumida
Seção intitulada “A versão resumida”- Um PDF desenha glifos — formas visuais escolhidas por códigos numéricos em uma fonte — não caracteres Unicode.
- A mesma forma pode significar caracteres diferentes, e o mesmo caractere pode ser desenhado por glifos diferentes. Aparência e significado são desacoplados por design.
- Para obter o texto de volta, um leitor executa o caminho de exibição ao
contrário: código para caractere. O CMap
/ToUnicodeda fonte é a tabela de consulta para esse passo reverso (Spec: ISO 32000-2, §9.10ISO 32000-2 §9.10). - Sem
/ToUnicode, ou com um errado, o resultado é copiar-colar ilegível e busca falha — com a página ainda perfeita pixel a pixel. - Esse é o problema do significado. Se o glifo parece certo é o problema
separado da aparência, tratado em
Fonts: the hard part. O NextPDF emite um
/ToUnicodecorreto para que o ciclo de ida e volta seja fiel.
Como o NextPDF aborda isso
Seção intitulada “Como o NextPDF aborda isso”Comece por como o texto chega à página, em primeiro lugar. Um content stream não
diz “escreva a palavra file.” Ele seleciona uma fonte e, então, entrega uma
sequência de códigos a um operador de exibição de texto
(Spec: ISO 32000-2, §9ISO 32000-2 §9). Cada código é um índice — uma
posição em um programa de fonte — e a fonte transforma esse índice em um contorno
de glifo e o pinta. O código 70 não é o caractere F. Ele é “a posição 70
nesta fonte”, e a posição 70 por acaso contém uma curva em forma de F.
Escolha uma fonte diferente e a posição 70 poderia ser um floco de neve.
Então, um código só significa algo em relação à codificação da sua fonte. Para uma fonte simples, essa codificação é mais ou menos um byte por glifo. Para os sistemas de escrita que precisam de milhares de glifos — CJK, ou qualquer documento em Unicode completo — isso não é espaço suficiente, e o PDF recorre a uma fonte composta (Type 0) (Spec: ISO 32000-2, §9.7ISO 32000-2 §9.7). Uma fonte composta lê códigos multibyte por meio de um CMap, transforma-os em CIDs (identificadores de caractere) e mapeia CIDs para glifos. É uma indireção elegante que permite que uma fonte enderece um conjunto enorme de glifos. É também mais um lugar onde o rastro de “o que é exibido” para “o que isso significa” pode se perder.
Agora execute-o ao contrário. Para extrair texto, um leitor pega os códigos que encontra no content stream e precisa recuperar caracteres (Spec: ISO 32000-2, §9.10ISO 32000-2 §9.10). A codificação que desenhou o glifo foi de caractere para glifo; a extração precisa de código de glifo para caractere, e essa direção não tem garantia de ser invertível. Uma fonte com subset pode ter renumerado seus glifos. Os CIDs de uma fonte composta podem ser privados. A forma na página não carrega significado Unicode inerente.
A correção é fornecer o mapa reverso junto com a fonte. Esse mapa é o CMap
/ToUnicode: para cada código que o documento usa, ele registra o valor (ou
valores) Unicode que aquele código representa. Com ele, a extração é uma consulta
limpa. Sem ele, um leitor fica entregue a adivinhar a partir de codificações de
fonte e heurísticas — e adivinhar é exatamente onde fi vira um ponto de
interrogação.
- Your charactersThe Unicode text you set, for example the word 'file'.
- Encoding → codesThe font's encoding turns characters into numeric codes; a composite font routes them through a CMap to CIDs.
- Codes → glyphsEach code selects a glyph outline, which is painted to the page. This is the part you see.
- Extraction reverses itA reader reads the codes back and looks each one up in /ToUnicode to recover Unicode.
- Text againWith a correct /ToUnicode, copy, search, indexing, and screen readers all get the original characters back.
O NextPDF escreve o mapa reverso como rotina. Quando ele incorpora uma fonte
composta, emite o descendente CIDFontType2, o pai Type0 e um CMap
/ToUnicode, para que os códigos resolvam de volta para o Unicode — o trabalho
de codificação descrito em
Fonts: the hard part. O ponto que vale
separar aqui é o porquê: o stream /ToUnicode não tem a ver com fazer a página
parecer correta. Os glifos já parecem corretos sem ele. Ele é o único artefato
que mantém o texto extraível.
Exemplo prático
Seção intitulada “Exemplo prático”O lugar mais famoso em que isso aparece é a ligadura fi. Muitas fontes
desenham f e i como um único glifo combinado, porque o pingo do i colide
com o gancho do f. Na página é uma forma só, desenhada por um único código. A
questão é o que acontece quando você a copia.
% A /ToUnicode entry that maps the single ligature code to TWO characters,% so selecting the 'fi' glyph copies out as 'f' then 'i' — not one mystery box.1 beginbfchar<0085> <00660069> % code 0x85 -> U+0066 'f' U+0069 'i'endbfcharEssa única entrada é a diferença entre uma busca por “file” que encontra
correspondência e uma busca por “file” que silenciosamente deixa de encontrar
toda ocorrência renderizada com a ligadura. Mapeie o código da ligadura para a
sequência de dois caracteres f + i, e a palavra fica pesquisável e copiável.
Deixe-o sem mapeamento, e a página ainda exibe file perfeitamente enquanto o
texto silenciosamente não diz nada que uma máquina consiga ler.
É por isso que “fi” pode extrair como um caractere ou dois, e por que a diferença
não é aleatória — é o que quer que o mapa /ToUnicode tenha sido instruído a
dizer. Um mapa correto decompõe a ligadura de volta em seus caracteres. O NextPDF
emite exatamente esse tipo de entrada, para que uma ligadura na página sejam duas
letras comuns na área de transferência.
Equívoco comum
Seção intitulada “Equívoco comum”A armadilha, nomeada: “o texto renderiza, então o texto está bom.” A
renderização e a extração são mecanismos diferentes lendo dados diferentes. A
renderização segue o caminho de código para glifo e é o que seus olhos conferem.
A extração segue o caminho de código para caractere por meio de /ToUnicode e é
o que todo consumidor de máquina confere. Um documento pode mandar bem no
primeiro e falhar completamente no segundo, porque, por construção, não há nada
visual que o revele.
Uma segunda armadilha, mais sutil: presumir que um glifo “obviamente” sabe qual caractere ele é. Ele não sabe. Um glifo é uma forma com um índice. O mapeamento de volta para um caractere é informação adicional que o produtor tem de fornecer. Se o produtor nunca a escreveu, nenhum leitor consegue recuperá-la fielmente — ele só pode adivinhar.
Limites e fronteiras
Seção intitulada “Limites e fronteiras”| Edition | Availability |
|---|---|
| Core | O NextPDF emite um CMap /ToUnicode correto para as fontes que incorpora, incluindo os casos de ligadura e de fonte composta, de modo que os documentos que ele produz são pesquisáveis e copiáveis. |
| Pro | Not in this edition |
| Enterprise | Not in this edition |
Um mapa /ToUnicode só pode carregar a informação que a fonte de origem
efetivamente expõe. Onde um glifo não tem caractere determinável — uma marca
puramente decorativa, um símbolo de uso privado sem atribuição Unicode — nenhum
mapa consegue inventar um significado que nunca esteve lá. O NextPDF emite o
mapeamento verídico que consegue derivar; ele não fabrica caracteres para
preencher uma lacuna.
Esta página trata dos documentos que o NextPDF escreve. Ela não é uma
ferramenta de reparo para PDFs de entrada arbitrários cujo /ToUnicode já está
ausente ou errado; recuperar texto desses é um problema separado e heurístico. E
a extração fiel é necessária para a acessibilidade, mas não é o todo dela — a
ordem de leitura, as tags e o texto alternativo são tratados em
What makes a PDF accessible.
Mini-FAQ
Seção intitulada “Mini-FAQ”Por que o mesmo PDF copia bem em um leitor e mal em outro?
Leitores diferentes recorrem a fallbacks diferentes quando /ToUnicode está
ausente. Alguns adivinham a partir da codificação interna da fonte e dão sorte em
texto latino comum; outros não. Um /ToUnicode correto remove a loteria — todo
leitor em conformidade obtém os mesmos caracteres.
Isso é a mesma coisa que a fonte não estar incorporada?
Não. A incorporação tem a ver com aparência — se o glifo é pintado sem a fonte
instalada. /ToUnicode tem a ver com significado — se o código mapeia de volta
para um caractere. Uma fonte pode estar perfeitamente incorporada e ainda assim
não ter um /ToUnicode utilizável, que é a falha do tipo bonito-mas-não-
pesquisável.
A extração alguma vez precisa de mais de um caractere por código?
Sim — o caso da ligadura é exatamente esse: um código mapeia para dois
caracteres. Uma entrada /ToUnicode pode mapear um único código para uma curta
sequência, que é como ligaduras, e algumas formas compostas, voltam como suas
letras constituintes.
Documentos relacionados
Seção intitulada “Documentos relacionados”- Fonts: the hard part — a peça complementar sobre o lado da aparência: incorporação, subsetting e como a codificação é construída. Esta página é o lado do significado da mesma moeda.
- What makes a PDF accessible — o texto fiel é um ingrediente; a ordem de leitura, as tags e o texto alternativo são o resto.
- What a PDF actually is — o modelo de
objetos dentro do qual as fontes, as codificações e os streams
/ToUnicodevivem.
Glossário
Seção intitulada “Glossário”- Glifo — uma forma visual em uma fonte (um contorno). O que um PDF de fato pinta. Um glifo tem um índice, mas nenhum significado de caractere inerente.
- Caractere — uma unidade de linguagem escrita, identificada por um code point Unicode. O que você quer dizer, e o que a extração tenta recuperar.
- Código — o valor numérico em um content stream que seleciona um glifo na fonte atual. Não é um caractere Unicode; só tem significado em relação à fonte.
- CID — um identificador de caractere usado por uma fonte composta para endereçar um glifo dentro de um conjunto grande; um passo intermediário entre código e glifo.
- Fonte composta (Type 0) — uma fonte que lê códigos multibyte por meio de um CMap para alcançar CIDs e glifos, usada quando uma fonte precisa endereçar milhares de glifos.
/ToUnicode— o stream de CMap que mapeia os códigos que um documento usa de volta para valores Unicode; o que torna o texto em PDF pesquisável, copiável e acessível.- Ligadura — um único glifo que desenha duas ou mais letras como uma só forma
(por exemplo
fi); sua entrada/ToUnicodea decompõe de volta em seus caracteres.