Ir al contenido
getnextpdf.com

Por qué el texto de un PDF no es realmente texto

Cuando lees un PDF, ves palabras. El archivo no contiene palabras. Contiene instrucciones para pintar formas en unas coordenadas, y resulta que las formas parecen letras. La brecha entre lo que un PDF dibuja y lo que significa es la razón por la que una página puede verse impecable y aun así copiarse como disparates. Esta página trata sobre esa brecha y sobre el pequeño mapa aparte que la cierra.

Todo lo que haces con un PDF como texto —seleccionarlo, copiarlo, buscarlo, indexarlo para su recuperación, leerlo en voz alta con un lector de pantalla— depende de recuperar caracteres que el archivo nunca almacenó directamente. Si esa recuperación falla, el fallo es invisible. La página sigue representándose. Nadie lo nota hasta que alguien copia un párrafo en un correo y obtiene □□□□, o busca en un contrato de 400 páginas una cláusula que está claramente ahí y no encuentra nada.

Esa es una clase de error cara precisamente porque sobrevive a toda revisión visual. No hay nada que ver. El documento parece autoritativo y es, para fines de máquina, mudo.

  • Un PDF dibuja glifos —formas visuales elegidas por códigos numéricos dentro de una fuente—, no caracteres Unicode.
  • La misma forma puede significar caracteres distintos, y el mismo carácter puede dibujarse con glifos distintos. La apariencia y el significado están desacoplados por diseño.
  • Para recuperar el texto, un lector ejecuta la ruta de representación a la inversa: de código a carácter. La CMap /ToUnicode de la fuente es la tabla de consulta para ese paso inverso (Spec: ISO 32000-2, §9.10).
  • Sin /ToUnicode, o con uno incorrecto, el copiar y pegar sale ilegible y la búsqueda falla, con la página aún en perfecta nitidez de píxeles.
  • Este es el problema del significado. Que el glifo se vea bien es el problema aparte de la apariencia que se trata en Las fuentes: la parte difícil. NextPDF emite un /ToUnicode correcto para que el viaje de ida y vuelta sea fiel.

Empecemos por cómo el texto llega a la página siquiera. Un flujo de contenido no dice «escribe la palabra file». Selecciona una fuente y luego entrega una cadena de códigos a un operador de representación de texto (Spec: ISO 32000-2, §9). Cada código es un índice —una posición en un programa de fuente— y la fuente convierte ese índice en un contorno de glifo y lo pinta. El código 70 no es el carácter F. Es «la posición 70 en esta fuente», y resulta que la posición 70 contiene una curva con forma de F. Elige otra fuente y la posición 70 podría ser un copo de nieve.

Así que un código solo significa algo en relación con la codificación de su fuente. Para una fuente simple, esa codificación es aproximadamente un byte por glifo. Para las escrituras que necesitan miles de glifos —CJK, o cualquier documento Unicode completo— eso no es espacio suficiente, y el PDF recurre a una fuente compuesta (Type 0) (Spec: ISO 32000-2, §9.7). Una fuente compuesta lee códigos de varios bytes a través de una CMap, los convierte en CID (identificadores de carácter) y asigna los CID a glifos. Es una indirección elegante que permite que una sola fuente direccione un conjunto enorme de glifos. También es un lugar más donde el rastro desde «lo que se muestra» hasta «lo que significa» puede enfriarse.

Ahora ejecútalo al revés. Para extraer texto, un lector toma los códigos que encuentra en el flujo de contenido y tiene que recuperar caracteres (Spec: ISO 32000-2, §9.10). La codificación que dibujó el glifo iba de carácter a glifo; la extracción necesita de código de glifo a carácter, y no se garantiza que esa dirección sea invertible. Una fuente subconjunto puede haber renumerado sus glifos. Los CID de una fuente compuesta pueden ser privados. La forma de la página no lleva ningún significado Unicode inherente.

La solución es enviar el mapa inverso junto con la fuente. Ese mapa es la CMap /ToUnicode: para cada código que usa el documento, registra el valor (o los valores) Unicode que ese código representa. Con él, la extracción es una consulta limpia. Sin él, un lector se queda adivinando a partir de codificaciones de fuente y heurísticas, y adivinar es exactamente donde fi se convierte en un signo de interrogación.

  1. Your charactersThe Unicode text you set, for example the word 'file'.
  2. Encoding → codesThe font's encoding turns characters into numeric codes; a composite font routes them through a CMap to CIDs.
  3. Codes → glyphsEach code selects a glyph outline, which is painted to the page. This is the part you see.
  4. Extraction reverses itA reader reads the codes back and looks each one up in /ToUnicode to recover Unicode.
  5. Text againWith a correct /ToUnicode, copy, search, indexing, and screen readers all get the original characters back.
Las dos direcciones del texto en PDF. La representación va de carácter → glifo y es lo que ves. La extracción va de código → carácter y es lo que puedes copiar, buscar y leer en voz alta, y solo funciona cuando el mapa /ToUnicode cierra el bucle.

NextPDF escribe el mapa inverso por norma. Cuando incrusta una fuente compuesta emite el descendiente CIDFontType2, el padre Type0 y una CMap /ToUnicode para que los códigos se resuelvan de vuelta a Unicode: el trabajo de codificación descrito en Las fuentes: la parte difícil. Lo que vale la pena separar aquí es por qué: el flujo /ToUnicode no tiene que ver con hacer que la página se vea bien. Los glifos ya se ven bien sin él. Es el único artefacto que mantiene el texto extraíble.

El lugar más famoso donde esto aflora es la ligadura fi. Muchas fuentes dibujan f e i como un solo glifo combinado, porque el punto de la i choca con el gancho de la f. En la página es una sola forma, dibujada por un solo código. La cuestión es qué ocurre cuando la copias.

% 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'
endbfchar

Esa única entrada es la diferencia entre que una búsqueda de «file» coincida y que una búsqueda de «file» pase por alto en silencio cada aparición representada con la ligadura. Asigna el código de ligadura a la secuencia de dos caracteres f + i, y la palabra se puede buscar y copiar. Déjalo sin asignar, y la página seguirá mostrando file a la perfección mientras el texto, en silencio, no dice nada que una máquina pueda leer.

Por esto «fi» puede extraerse como uno o como dos caracteres, y por esto la diferencia no es aleatoria: es lo que se le dijo al mapa /ToUnicode que dijera. Un mapa correcto descompone la ligadura de vuelta en sus caracteres. NextPDF emite exactamente ese tipo de entrada, para que una ligadura en la página sean dos letras corrientes en el portapapeles.

La trampa, con nombre: «el texto se representa, así que el texto está bien». La representación y la extracción son mecanismos distintos que leen datos distintos. La representación sigue la ruta de código a glifo y es lo que comprueban tus ojos. La extracción sigue la ruta de código a carácter a través de /ToUnicode y es lo que comprueba todo consumidor de máquina. Un documento puede bordar lo primero y fallar lo segundo por completo, porque por construcción no hay nada visual que lo revele.

Una segunda trampa, más sutil: suponer que un glifo «obviamente» sabe qué carácter es. No lo sabe. Un glifo es una forma con un índice. La correspondencia de vuelta a un carácter es información adicional que el productor tiene que aportar. Si el productor nunca la escribió, ningún lector puede recuperarla con fidelidad: solo puede adivinar.

Faithful text extraction from documents NextPDF writes — edition availability
EditionAvailability
CoreNextPDF emite una CMap /ToUnicode correcta para las fuentes que incrusta, incluidos los casos de ligaduras y de fuentes compuestas, de modo que los documentos que produce son buscables y copiables.
ProNot in this edition
EnterpriseNot in this edition

Un mapa /ToUnicode solo puede llevar la información que la fuente de origen realmente expone. Donde un glifo no tiene un carácter determinable —una marca puramente decorativa, un símbolo de uso privado sin asignación Unicode— ningún mapa puede inventar un significado que nunca estuvo ahí. NextPDF emite la correspondencia veraz que puede derivar; no fabrica caracteres para rellenar un hueco.

Esta página trata sobre los documentos que NextPDF escribe. No es una herramienta de reparación para PDF entrantes arbitrarios cuyo /ToUnicode ya está ausente o es incorrecto; recuperar texto de esos es un problema heurístico aparte. Y la extracción fiel es necesaria para la accesibilidad, pero no es toda ella: el orden de lectura, las etiquetas y el texto alternativo se tratan en Qué hace accesible a un PDF.

¿Por qué el mismo PDF se copia bien en un lector y mal en otro? Distintos lectores recurren a alternativas distintas cuando falta /ToUnicode. Algunos adivinan a partir de la codificación incorporada de la fuente y aciertan con texto latino común; otros no. Un /ToUnicode correcto elimina la lotería: todo lector conforme obtiene los mismos caracteres.

¿Es lo mismo que la fuente no esté incrustada? No. La incrustación tiene que ver con la apariencia: si el glifo se pinta sin la fuente instalada. /ToUnicode tiene que ver con el significado: si el código se corresponde de vuelta con un carácter. Una fuente puede estar perfectamente incrustada y aun así no tener un /ToUnicode utilizable, que es el fallo de página bonita pero no buscable.

¿La extracción necesita alguna vez más de un carácter por código? Sí: el caso de la ligadura es exactamente eso: un código se corresponde con dos caracteres. Una entrada /ToUnicode puede asignar un solo código a una secuencia corta, que es como las ligaduras, y algunas formas compuestas, vuelven como sus letras constituyentes.

  • Las fuentes: la parte difícil: la pieza complementaria del lado de la apariencia: incrustación, subconjuntos y cómo se construye la codificación. Esta página es el lado del significado de la misma moneda.
  • Qué hace accesible a un PDF: el texto fiel es un ingrediente; el orden de lectura, las etiquetas y el texto alternativo son el resto.
  • Qué es realmente un PDF: el modelo de objetos dentro del cual viven las fuentes, las codificaciones y los flujos /ToUnicode.
  • Glifo: una forma visual en una fuente (un contorno). Lo que un PDF pinta en realidad. Un glifo tiene un índice, pero ningún significado de carácter inherente.
  • Carácter: una unidad de lenguaje escrito, identificada por un punto de código Unicode. Lo que quieres decir, y lo que la extracción intenta recuperar.
  • Código: el valor numérico en un flujo de contenido que selecciona un glifo de la fuente actual. No es un carácter Unicode; solo tiene significado en relación con la fuente.
  • CID: un identificador de carácter que usa una fuente compuesta para direccionar un glifo dentro de un conjunto grande; un paso intermedio entre el código y el glifo.
  • Fuente compuesta (Type 0): una fuente que lee códigos de varios bytes a través de una CMap para llegar a los CID y a los glifos, usada cuando una sola fuente debe direccionar miles de glifos.
  • /ToUnicode: el flujo de CMap que asigna los códigos que usa un documento de vuelta a valores Unicode; lo que hace que el texto de PDF sea buscable, copiable y accesible.
  • Ligadura: un solo glifo que dibuja dos o más letras como una sola forma (por ejemplo fi); su entrada /ToUnicode la descompone de vuelta en sus caracteres.