Fusionar o dividir un PDF de dos gigabytes de la forma obvia le cuesta dos cosas a la vez: tiempo de reloj y espacio de direcciones. La forma obvia es cargar cada entrada, hacer el trabajo, escribir la salida. La carga es donde se rompe. Un archivo de escaneo que pasa de 300 a 600 DPI duplica su resolución lineal y aproximadamente cuadruplica en disco, así que el mismo trabajo de ensamblaje que manejó archivos de 400 MB todo el año empieza a thrashing en el momento en que una entrada cruza un gigabyte, a menudo sin hacer nada más que contar páginas. La tarea nunca se hizo más difícil. Abrir, contar, escoger rangos, concatenar es todo. La carga de árbol completo simplemente dejó de ser un predeterminado sensato a ese tamaño. PDF Library for Delphi, la biblioteca PDF de losLab para Delphi y C++Builder, responde esto con su capa de Acceso Directo: una familia de funciones con prefijo DA respaldadas por un lector en flujo que recorre la tabla de referencia cruzada en su lugar en vez de construir todo el documento en memoria
A dónde va la memoria en una carga completa
Cargar un PDF "normalmente" significa analizar el xref, resolver cada objeto indirecto a un árbol en memoria, decodificar flujos de objeto, y cablear el árbol de páginas, fuentes y anotaciones a objetos que puede manipular. Para flujos de trabajo de edición ese es el trato correcto. Para trabajo de fusión, división e inspección es mayormente desperdicio. Un archivo de escaneo de 30.000 páginas podría contener millones de objetos indirectos, y un trabajo de división necesita leer unos pocos cientos de ellos: los nodos de página en el rango pedido, más lo que esos nodos referencian
La capa de Acceso Directo invierte el modelo. DAOpenFile y DAOpenFileReadOnly analizan el trailer y el xref, unos pocos kilobytes en la cola del archivo, y devuelven un identificador de archivo. Los objetos se obtienen de forma perezosa cuando una llamada los necesita. La consecuencia práctica es que abrir un archivo de múltiples gigabytes toma aproximadamente lo mismo que abrir uno pequeño, y la memoria rastrea lo que toca en lugar de lo que el archivo contiene
Sondear un archivo enorme sin cargarlo
El patrón de abajo viene del propio benchmark de archivos grandes de la biblioteca: abrir solo lectura, hacer preguntas, cerrar. Nunca existe un árbol de documento
var
Lib: TPDFlib;
Handle, Pages: Integer;
begin
Lib := TPDFlib.Create;
try
Handle := Lib.DAOpenFileReadOnly('archive-2025.pdf', '');
if Handle = 0 then
raise Exception.Create('Direct access open failed');
Pages := Lib.DAGetPageCount(Handle);
Writeln('pages : ', Pages);
Writeln('title : ', Lib.DAGetInformation(Handle, 'Title'));
Lib.DACloseFile(Handle);
finally
Lib.Free;
end;
end;
El modo solo lectura vale la pena preferirlo siempre que pueda: deja que la etapa de intake corra mientras otros procesos sostienen el archivo, y documenta la intención. Una etapa de sondeo que llame accidentalmente a una función mutante falla rápido en lugar de corromper el archivo
PageRef es un identificador de objeto, no un número de página
El error más común con la API DA es pasar un número de página donde una función espera un PageRef. Casi cada llamada DA por página toma un identificador de referencia al objeto de página en lugar de un número de página: DAExtractPageText, DARenderPageToFile, DARotatePage y DACapturePage todas esperan una ref. Obtiene uno traduciendo el número orientado al humano a través de DAFindPage:
PageRef := Lib.DAFindPage(Handle, 250); // page number -> object handle
if PageRef <> 0 then
begin
Text := Lib.DAExtractPageText(Handle, PageRef, 0);
Lib.DARenderPageToFile(Handle, PageRef, 5, 150, 'page250.png');
end;
Pasar el número crudo 250 en su lugar no lanza un error. Direcciona cualquier objeto que resulte sentarse tras ese valor de identificador, que un buen día falla visiblemente y un mal día extrae texto de la página equivocada a un documento orientado al cliente. Si envuelve la capa DA en su propio código de servicio, haga la traducción imposible de saltar: acepte números de página en la frontera, llame a DAFindPage inmediatamente, y pase solo refs internamente
Fusionar cientos de archivos con una lista nombrada
Para dos archivos, MergeFiles(First, Second, Output) es suficiente. El ensamblaje por lotes escala mejor a través de listas de archivos: registre entradas bajo un nombre de lista, luego fusione la lista en un solo pase
Lib.AddToFileList('Statements', 'jan.pdf');
Lib.AddToFileList('Statements', 'feb.pdf');
Lib.AddToFileList('Statements', 'mar.pdf');
Lib.MergeFileList('Statements', 'q1-statements.pdf');
// Verifica el resultado por la vía barata: otra vez acceso directo
Handle := Lib.DAOpenFileReadOnly('q1-statements.pdf', '');
Writeln('merged pages: ', Lib.DAGetPageCount(Handle));
Lib.DACloseFile(Handle);
La familia de fusión tiene tres variantes, y la diferencia no es velocidad sola. MergeFileListFast se salta la preservación del árbol de estructura; MergeFileListStrict hace cumplir modo estricto; la versión sin sufijo es el predeterminado equilibrado. La regla operativa que cae: si cualquier entrada es un PDF etiquetado cuya estructura de accesibilidad debe sobrevivir, lo producido para PDF/UA siendo el caso obvio, recurra al predeterminado o a la variante Strict, porque Fast descarta silenciosamente el árbol de estructura. Para archivos de escaneo simples sin etiquetado, Fast es rendimiento gratis. Decida por pipeline, no por humor del desarrollador, y registre la variante usada en el log del trabajo
Dividir sin cargar: extracción por rango
La división sigue la misma filosofía sin carga. ExtractFilePages(InputFileName, Password, OutputFileName, RangeList) jala un rango de páginas directamente de archivo a archivo, con una lista de rangos como '1-500', '501-1000', o selecciones separadas por comas, y la fuente nunca se convierte en un árbol de documento. Cuando un documento ya está cargado por otras razones, ExtractPageRanges produce un nuevo documento en memoria a partir del actual, y CopyPageRanges jala rangos de otro documento cargado por ID. Para división por extracto de flujos de impresión consolidados, la forma archivo-a-archivo es la que mantiene a una entrada de 4 GB de inflarse alguna vez en RAM
Archivos que mienten sobre su geometría
Los pipelines de archivos grandes se encuentran con archivos dañados a una tasa que los pipelines de archivos pequeños nunca ven, simplemente porque las entradas pasan por más sistemas. Dos formas de falla merecen manejo explícito
Primero, encabezados desplazados. Las pasarelas de correo y los spoolers de impresión a veces anteponen bytes a un PDF, así que el marcador %PDF ya no se sienta en el desplazamiento 0 y cada desplazamiento xref en el archivo está mal por la misma cantidad. El lector en flujo lo detecta y lo expone (DAShiftedHeader a nivel plano, ShiftedHeader en TSmartPDFReader), luego lo compensa durante las lecturas. La aritmética de desplazamiento casera típicamente no, que es por lo que "funciona en cada archivo que generamos, falla en archivos del cliente X" es el síntoma clásico
Segundo, tablas de referencia cruzada rotas. DACopyFile(InputFileName, OutputFileName, PageCount) hace fluir todo el archivo a una nueva copia mientras reconstruye el xref, devolviendo el conteo de páginas como subproducto. Correrlo como una etapa de normalización frente a un consumidor aguas abajo exigente convierte una clase de fallos de análisis intermitentes en un paso de reparación predecible. Y cuando sus propias ediciones necesitan guardarse, DAAppendFile las escribe como una actualización incremental, anexando una nueva revisión en lugar de reescribir gigabytes, lo que mantiene el costo de guardado proporcional al cambio en lugar de al archivo
Detalles de entrega: linealización y composición
Dos capacidades adyacentes completan un pipeline de archivos grandes. Cuando la salida ensamblada se sirve sobre HTTP para visualización en el navegador, LinearizeFile la reorganiza para flujo por rango de bytes así la primera página se muestra antes de que el resto de un paquete de 500 MB haya terminado de descargar. Córrelo como la etapa final, después de toda fusión, porque cualquier modificación posterior des-linealiza el archivo de nuevo. Y cuando los paquetes necesitan composición en lugar de concatenación simple, digamos una portada estampada tras cada extracto o dos páginas fuente impuestas sobre una hoja de salida, DACapturePage convierte cualquier página en una plantilla reutilizable que DADrawCapturedPage coloca sobre una página destino en un rectángulo arbitrario, todavía sin una carga completa de documento sobre la fuente de múltiples gigabytes
Límites y qué permanece solo lectura
El formato mismo se queda sin espacio mucho antes que el Acceso Directo. Los desplazamientos son Int64 en todo el camino a través de la capa DA, así que los techos reales son el disco disponible y el campo de desplazamiento xref de 10 dígitos de las tablas de referencia cruzada clásicas (no-flujo). Los archivos de escaneo de múltiples gigabytes son pocoremarkables en la práctica, y la memoria permanece acotada sin importar el tamaño del archivo porque los objetos se leen solo cuando una llamada los pide
Dos preguntas surgen lo suficiente para responder directamente. La fusión por la ruta predeterminada lleva la estructura del documento a través, así que los marcadores y enlaces sobreviven; la variante Fast es la que intercambia el árbol de estructura por velocidad, que es toda la razón para reservarla para entradas sin etiquetar. El hábito seguro es abrir la salida fusionada, recorrer su esquema, y verificar unos cuantos enlaces internos antes de enviarla. En cuanto a la edición: hay un terreno medio útil entre el sondeo solo lectura y una carga completa. Las operaciones a nivel de página funcionan sobre el identificador directamente, DARotatePage, DAMovePage y DAHidePage entre ellas, junto con lecturas de campo de formulario, y DAAppendFile persiste esas ediciones como una revisión incremental. La edición a nivel de contenido, cualquier cosa que reescriba los operadores de marcado dentro de una página, todavía pertenece a la capa de documento completa
Artículos relacionados
Si su salida fusionada debe permanecer accesible, los antecedentes del árbol de estructura se cubren en el artículo de accesibilidad de PDF etiquetado, que explica exactamente lo que la variante de fusión Fast descartaría. Para sacar contenido de los rangos que divide, vea la guía de extracción de texto, imágenes y fuentes
La lista completa de funciones de Direct Access viene con la biblioteca; las ediciones y las descargas de prueba están en la página del producto PDF Library for Delphi