El diccionario Catalog de PDF tiene exactamente una clave de navegación obligatoria: /Pages. Esa clave debe apuntar a un objeto indirecto de tipo /Pages, que a su vez contiene el array /Kids y el /Count total de páginas. Si se elimina ese puntero, ningún lector conforme puede localizar ni una sola página en el fichero. La norma ISO 32000-1 §7.7.2 es inequívoca al respecto: el Catalog deberá tener una entrada /Pages, y el objeto referenciado deberá tener el tipo /Pages. Los ficheros que incumplen este requisito no son meramente no conformes, sino que están estructuralmente rotos de una manera que la mayoría de los analizadores gestiona mal.
Qué dice realmente la especificación
Un PDF conforme mínimo tiene al menos tres objetos. El objeto 1 es el Catalog, el objeto 2 es la raíz de Pages y a partir del objeto 3 vienen los diccionarios Page individuales. El Catalog apunta a la raíz de Pages; la raíz de Pages lista sus hijos en /Kids; cada Page lleva una referencia inversa /Parent. Toda la cadena es bidireccional por diseño, de modo que un analizador puede comenzar desde cualquier extremo y llegar a cualquier página en tiempo O(log n) para árboles equilibrados.
% Minimal conforming structure (ISO 32000-1 §7.7.2)
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R 4 0 R] /Count 2 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 5 0 R /Resources << >> >>
endobj
4 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 6 0 R /Resources << >> >>
endobj
El árbol de páginas puede estar anidado. Un documento de miles de páginas suele agrupar las páginas en objetos nodo intermedios que también tienen el tipo /Pages, cada uno con su propio /Kids y un /Count que refleja el subárbol por debajo de él. El /Count del nodo raíz siempre es igual al número total de páginas. Ese recuento es lo que los visores muestran en el campo de número de página antes de haber analizado ni una sola página, porque leer un entero del objeto 2 es mucho más económico que recorrer todo el árbol.
Aspecto de un fichero sin Pages
Los ficheros que carecen del diccionario Pages suelen provenir de generadores PDF que escriben los objetos de página directamente sin ensamblarlos en un árbol, o de corrupción que elimina el nodo raíz dejando intactos los objetos Page hoja. El Catalog en ese tipo de fichero o bien carece completamente de la clave /Pages, o bien contiene una referencia a un objeto que ya no existe en la tabla de referencias cruzadas.
% Non-conforming: Catalog with no /Pages reference
1 0 obj
<< /Type /Catalog >>
endobj
% Page objects exist but are unreachable from the Catalog
5 0 obj
<< /Type /Page /MediaBox [0 0 612 792] /Contents 6 0 R /Resources << >> >>
endobj
15 0 obj
<< /Type /Page /MediaBox [0 0 612 792] /Contents 16 0 R /Resources << >> >>
endobj
25 0 obj
<< /Type /Page /MediaBox [0 0 612 792] /Contents 26 0 R /Resources << >> >>
endobj
Un analizador que siga la especificación leerá el Catalog, intentará resolver /Pages, no encontrará nada (o una referencia muerta), y generará un error o informará de cero páginas. Lo que no debe hacer es continuar como si el fichero tuviera cero páginas y tener éxito silenciosamente; eso produce una salida en blanco que parece correcta para las herramientas automatizadas y es errónea para todo ser humano que la abra.
Por qué los analizadores se bloquean
La mayoría de los analizadores PDF asignan su tabla de páginas interna en el momento de la carga, basándose en el valor /Count del nodo raíz de Pages. Cuando ese nodo está ausente, el analizador o bien lee cero, no asigna nada y luego desreferencia un puntero nulo la primera vez que algún código solicita la página 1, o bien lee basura y asigna un búfer completamente incorrecto. Ninguno de los dos resultados es elegante. La violación de acceso en 0x008E5D78 que aparece en los registros de fallos al procesar ese tipo de fichero es exactamente esto: una desreferencia de puntero nulo dentro de la ruta de acceso a páginas, provocada por la ausencia de la estructura que el analizador asumía que siempre estaría ahí.
La suposición de diseño subyacente es razonable. La gran mayoría de los PDF existentes tienen un diccionario Pages. Los analizadores que omiten la comprobación de existencia para ahorrar unas instrucciones no están siendo descuidados; están optimizando para el caso común. Los ficheros que penalizan esa optimización son suficientemente raros como para que el código de producción nunca llegue a encontrarse con uno hasta que lo hace, momento en que el bloqueo es tanto reproducible como desconcertante si el ingeniero no ha leído el §7.7.2.
Recuperación sin árbol de Pages
Si un analizador debe gestionar estos ficheros en lugar de rechazarlos, la recuperación sigue un camino predecible: escanear todos los objetos indirectos de la tabla de referencias cruzadas, recopilar los que tienen /Type /Page y ordenarlos por número de objeto. El orden de número de objeto no garantiza coincidir con el orden de lectura según la especificación, pero en la práctica los generadores que omiten el árbol de Pages tienden a emitir páginas secuencialmente, por lo que el orden de número de objeto es correcto más a menudo que no.
La comprobación en sí es barata. Antes de recorrer el puntero /Pages del Catalog, hay que confirmar que el puntero existe, que resuelve a un objeto real y que el /Type del objeto resuelto es igual a /Pages. Si falla cualquiera de esas tres condiciones, se pasa al escaneo lineal. El escaneo es más lento que el recorrido del árbol para documentos grandes, porque lee cada cabecera de objeto en lugar de seguir una ruta equilibrada, pero funciona, y para un fichero que ya está malformado, la corrección tiene más peso que la velocidad.
Un caso límite que el escaneo lineal no resuelve automáticamente: el orden de las páginas. Sin un array /Kids que defina la secuencia, el orden «correcto» queda indefinido por la especificación. El orden de número de objeto es el predeterminado pragmático; si el fichero es suficientemente importante como para procesarlo con cuidado, vale la pena el trabajo adicional de comprobar si los objetos Page llevan un /StructParents explícito o referencias de anotación que impliquen una secuencia de lectura.
Implicaciones para los generadores de PDF
Para quien escriba un generador de PDF en lugar de un analizador, la lección es concreta: emitir siempre la raíz de Pages antes de cerrar el fichero. El Catalog sin entrada /Pages no es un PDF válido bajo ninguna revisión de la especificación. Los generadores que construyen objetos de página al vuelo y ensamblan el árbol en la finalización (el enfoque que utilizan la mayoría de los escritores en streaming) están bien siempre que la finalización se ejecute realmente. El modo de fallo habitual es una excepción o un retorno anticipado que aborta la escritura antes de que el trailer esté completo, dejando un fichero que se abre en algunos visores (que tienen heurísticas de recuperación) y falla en otros (que no las tienen).
PDF/A y PDF/UA imponen restricciones adicionales al árbol de páginas más allá de lo que exige la especificación base, pero ninguno de los dos relaja el requisito de /Pages. Un validador que compruebe la conformidad con ISO 19005 o ISO 14289 detectará un diccionario Pages ausente como una violación de la especificación base antes de llegar siquiera a las reglas específicas del perfil.