Abran un PDF que haya pasado algunos años en producción y a menudo encontrarán más contabilidad interna que contenido: miles de pequeños objetos de diccionario, cada uno precedido por su propio encabezado obj, más una tabla de referencias cruzadas que cuesta 20 bytes ASCII por objeto. En un caso de soporte que analizamos, un archivo de pólizas de 58 MB dedicaba menos de la mitad de sus bytes al contenido real de las páginas; el resto era sobrecarga estructural y revisiones obsoletas. HotPDF, una biblioteca PDF VCL nativa para Delphi y C++Builder, expone los dos mecanismos de formato de archivo que atacan ambas mitades del problema: object streams para almacenamiento compacto y actualizaciones incrementales para modificaciones de sólo anexado que no destruyen lo anterior.
Cómo los object streams y xref streams remodelan el archivo
Hasta PDF 1.4, cada objeto indirecto se ubica sin comprimir en el cuerpo, y la tabla de referencias cruzadas del final es una estructura de texto de ancho fijo: exactamente 20 bytes por entrada, sin compresión permitida. Por lo tanto, un documento con 200,000 objetos carga alrededor de 4 MB sólo en datos xref antes de dibujar un solo glifo. PDF 1.5 introdujo dos reemplazos, definidos en ISO 32000-1 §7.5.7 y §7.5.8: object streams (/Type /ObjStm), que reúnen objetos que no son streams en un único contenedor comprimido con Flate, y cross-reference streams, que almacenan la propia tabla de búsqueda como binario comprimido con campos de ancho variable.
El ahorro es mayor exactamente donde los generadores ingenuos más perjudican: documentos cargados de formularios con miles de diccionarios de campos, árboles de marcadores profundos y elementos de estructura de PDF etiquetado. Esos objetos son individualmente diminutos y muy repetitivos, lo que los convierte en entrada ideal para Flate cuando se empaquetan juntos. Los streams de contenido de página ya eran comprimibles antes de PDF 1.5, de modo que los object streams no reducen demasiado los archivos dominados por imágenes; reducen drásticamente los archivos dominados por estructura.
En HotPDF las dos funciones se activan con un par de propiedades, y la dependencia de orden importa:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'catalog-2026.pdf';
Pdf.UseXRefStream := True; // binary xref, prerequisite for ObjStm
Pdf.UseObjectStreams := True; // pack objects into /Type /ObjStm
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Compressed structure demo');
Pdf.EndDoc; // emits XRefStm + ObjStm containers
finally
Pdf.Free;
end;
end;
UseObjectStreams requiere que UseXRefStream sea True, porque un objeto comprimido se direcciona mediante una entrada xref de tipo 2 (número de object stream más índice), y las entradas de tipo 2 simplemente no pueden expresarse en una tabla de texto clásica de 20 bytes. Configurar sólo UseObjectStreams no aporta nada; configurar ambas antes de BeginDoc es la configuración funcional.
El límite de compatibilidad en PDF 1.4
Ambas propiedades tienen False como valor predeterminado por una razón que afecta a equipos con integraciones heredadas. Un lector limitado a la semántica de PDF 1.4 no informa "compressed objects unsupported" cuando encuentra un xref stream; normalmente informa una tabla de referencias cruzadas dañada o rechaza el archivo por completo, porque el diseño de palabras clave del trailer que espera no está allí. Si su salida alimenta una pasarela de fax antigua, una impresora de hardware con intérprete embebido o un parser posterior escrito para PDF 1.4, dejen ambos indicadores desactivados para ese canal y acepten el archivo más grande. Para canales de archivo y entrega web, donde todos los visores masivos manejan PDF 1.5 desde hace dos décadas, activarlos equivale casi a compresión gratuita.
Vale la pena advertir a su equipo de soporte sobre un efecto operativo: una vez que los diccionarios se empaquetan en object streams, comparar byte a byte dos archivos generados deja de tener sentido, porque un cambio de un solo campo puede volver a comprimir todo un contenedor. Las investigaciones de soporte deben comparar documentos lógicamente, por contenido de objeto, no con una diferencia binaria.
Por qué existen las actualizaciones incrementales: offsets, firmas y pistas de auditoría
Una firma digital en PDF cubre un /ByteRange explícito: dos tramos del archivo físico, medidos en offsets absolutos de bytes, sobre los que se calculó el resumen CMS. Reescriban el archivo, incluso con contenido visualmente idéntico, y todos los offsets se desplazan; el resumen ya no coincide y la firma aparece como rota. Ésta es la razón concreta por la que ISO 32000-1 §7.5.6 define las actualizaciones incrementales: los objetos cambiados y agregados se anexan después del %%EOF existente, seguidos por una nueva sección de referencias cruzadas cuya entrada /Prev apunta a la anterior. Los bytes originales nunca se tocan, por lo que una revisión firmada previamente sigue siendo verificable, y Acrobat puede mostrar cada revisión firmada por separado en el panel de firmas.
HotPDF lo envuelve como un punto de entrada dedicado:
Pdf.BeginIncrementalUpdate('contract-signed.pdf');
Pdf.AddPage;
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Addendum recorded 2026-06-11');
Pdf.SaveIncrementalUpdate('contract-updated.pdf'); // appends the delta only
Dos detalles son fáciles de equivocar aquí. Primero, BeginIncrementalUpdate debe recibir el nombre del archivo original; la sección xref anexada almacena offsets que sólo son válidos en relación con esos bytes originales exactos. Segundo, el guardado es de sólo anexado por definición, así que la salida siempre es más grande que la entrada. Eso no es un defecto que haya que optimizar; es la propiedad que mantiene intactas las revisiones firmadas anteriores.
Editar documentos cargados: LoadFromFile, no BeginDoc
Otra trampa alcanza a los desarrolladores que aprendieron HotPDF a través de la API de generación. BeginDoc inicia un documento nuevo; es la llamada incorrecta cuando el objetivo es modificar uno existente. La cirugía de documentos pasa por la ruta de documento cargado:
PageCount := Pdf.LoadFromFile('base.pdf');
Pdf.InsertPagesFromDocument(OtherDoc, '1-3', 5); // pages 1-3 after page 5
Pdf.MovePage(2, 5);
Pdf.SaveLoadedDocument('modified.pdf');
El síntoma de mezclar los dos modelos es un archivo que contiene sólo el contenido nuevo y nada del original, porque BeginDoc creó tranquilamente un documento nuevo junto al que ustedes creían estar editando. Al revisar código, traten LoadFromFile + SaveLoadedDocument como un vocabulario emparejado y BeginDoc + EndDoc como otro; un procedimiento que usa ambos para el mismo documento casi siempre es un bug.
Contener el crecimiento: cuándo compactar un archivo anexado
El guardado de sólo anexado tiene un costo de largo plazo. Un trabajo nocturno que estampa una línea de estado sobre el mismo PDF produce 365 revisiones al año, y cada revisión arrastra consigo una nueva sección xref. Una vez que el historial de revisiones cumplió su propósito, y siempre que ninguna firma deba sobrevivir, el archivo puede compactarse reserializándolo por la ruta de documento cargado:
Pdf.LoadFromFile('stamped.pdf');
Pdf.SaveLoadedDocument('compacted.pdf');
Ese re-guardado es una reescritura completa, lo que significa que descarta deliberadamente las revisiones anteriores e invalidará cualquier firma del archivo, así que colóquenlo detrás de la misma verificación de política que usan antes de cualquier operación destructiva. Una regla de producción razonable que hemos visto funcionar: compactar cuando el conteo de revisiones cruza un umbral o cuando la sobrecarga anexada supera un porcentaje del archivo base, y nunca compactar archivos cuyo panel de firmas no esté vacío.
Comprobar el resultado antes de enviarlo
La verificación de este par de funciones es agradablemente mecánica. Abran la salida en Adobe Acrobat y comprueben tres cosas: que las propiedades del documento informen PDF 1.5 o posterior cuando los object streams están activados; que el panel de firmas siga validando cada revisión firmada previamente después de una actualización incremental; y que el conteo de páginas y los marcadores hayan sobrevivido a un ciclo de cargar-modificar-guardar. Para canales de archivo, pasen también el archivo por veraPDF, ya que las estructuras xref comprimidas son exactamente el tipo de detalle que un parser estricto examina con más cuidado que un visor tolerante. Si además procesan entradas muy grandes, las técnicas de inspección de nuestro recorrido por la Direct File API para flujos de trabajo con PDF grandes combinan bien con el guardado incremental, y la mecánica de firmas mencionada arriba se cubre a fondo en el artículo sobre firmas digitales y PAdES en HotPDF.
FAQ
¿Activar object streams rompe lectores PDF antiguos?
Los lectores que sólo implementan PDF 1.4 no pueden analizar xref streams y suelen informar que el archivo está dañado. Mantengan UseXRefStream y UseObjectStreams en su valor predeterminado False para canales que alimentan intérpretes heredados, y actívenlos para canales modernos de visualización y archivo.
¿Una actualización incremental mantiene válida mi firma digital?
Sí, ése es su propósito: los objetos nuevos se anexan después de los bytes firmados, por lo que el /ByteRange firmado sigue produciendo el resumen correcto. Una reescritura completa, incluida la compactación por cargar y volver a guardar, rompe todas las firmas existentes aunque el contenido visible no cambie.
¿Por qué mi archivo sigue creciendo después de guardados repetidos?
El guardado incremental anexa un delta en cada guardado y nunca recupera espacio. Compacten ocasionalmente con LoadFromFile más SaveLoadedDocument una vez que el historial de revisiones y las firmas ya no necesiten preservarse.
Adónde seguir
Ambas funciones forman parte del conjunto estándar de características de HotPDF Component, junto con las API de generación, formularios, cifrado y firma mostradas en otros artículos de este blog. La página del producto enlaza la documentación completa de la API si quieren mapear las llamadas anteriores sobre su propia canalización de documentos.