PDFium VCL expone la combinación de PDF a través de un único método: ImportPages. El patrón es siempre el mismo: crear un documento de destino vacío, abrir cada archivo fuente, llamar a ImportPages para copiar las páginas a través de este, cerrar la fuente y repetir el proceso. Cuando termina el bucle, SaveAs escribe el resultado en el disco. No hay un modo de combinación especial ni configuración que cambiar. La complejidad radica en los casos extremos, y hay unos pocos que muerden sin previo aviso
El bucle principal
Dos instancias de TPdf son todo lo que necesita. Una contiene el documento de destino, que se crea vacío con CreateDocument. La otra abre cada archivo de origen de forma sucesiva. A continuación, se muestra un procedimiento que toma una lista de rutas de archivo y escribe la salida combinada en una sola ruta:
procedure MergeFiles(const FileList: TStrings; const OutputPath: string);
var
PdfDest, PdfSrc: TPdf;
InsertAt, I: Integer;
begin
PdfDest := TPdf.Create(nil);
PdfSrc := TPdf.Create(nil);
try
PdfDest.CreateDocument;
InsertAt := 1; // ImportPages utiliza la posición de destino con base en 1
for I := 0 to FileList.Count - 1 do
begin
PdfSrc.FileName := FileList[I];
PdfSrc.Active := True;
if not PdfSrc.Active then
raise Exception.CreateFmt('No se puede abrir: %s', [FileList[I]]);
PdfDest.ImportPages(
PdfSrc,
'1-' + IntToStr(PdfSrc.PageCount), // rango de documento completo
InsertAt);
Inc(InsertAt, PdfSrc.PageCount);
PdfSrc.Active := False;
end;
PdfDest.SaveAs(OutputPath);
finally
PdfSrc.Free;
PdfDest.Free;
end;
end;
Hay dos cosas en ese código que son fáciles de pasar por alto en una primera lectura. La primera es cómo informa PDFium los fallos de carga. Active := True nunca lanza una excepción: si el archivo falta, está dañado o protegido con contraseña, PDFium captura el error de forma interna y deja Active como False. Sin la verificación explícita en la línea 10, un archivo incorrecto se omitiría silenciosamente de la combinación sin ninguna indicación en la salida. El PDF final tendría menos páginas de las esperadas y usted no sabría qué archivo fue el culpable
El segundo aspecto en referencia es el contador InsertAt. El tercer argumento con destino para ImportPages asume con o por ende en su obrar como 1-based la de (position) hacia el de a la y con de de destino de u obrando hacia de la página inicial de en importada (lands). Obrando en asimilado obrado en u o o de un 1 a de en el en la (puts) al del un archivo o y primer asimilable en a obrar obrado a y (source document) u a de o en (beginning of an otherwise empty file). Al devenir o u por u el obrar de un a y luego de de de u (source) o o obrando un obrar a o asimilado a por de en y al de (counter advances) en a la u de o por el (PdfSrc.PageCount), resultando al ser obrado el u por o a por del en a (next batch) a su u a y asonancia a obrar y asimilable a su del (appends after the last one). Un por obviar u el en a asimilar u asonancia obrada del en un y a de (forget to increment it) obrando el del o el y u por (subsequent source) asimilado asonante al de el u a y o u a obrar el por o en de obrar (overwrites) o de u o a por del a por a (pages at position 1), con su u a obrar y u el del o (giving you) el postrero de el u a y o a por obrado en de por a en la (list) y ninguna asonancia más referencial y adyacente
Rangos de páginas selectivos
No tiene que tomar cada página de un documento de origen. La cadena de rango que se pasa como el segundo argumento obedece a un formato sencillo delimitado por coma y guion: "1-3" selecciona desde la página 1 a la 3 de modo inclusive, "2,4,6" dictamina u obrando u elige en su asonancia a u obrar a un obrado y o (picks) obrando a de tres páginas de con a (specific), en del asimilable el asonante a u obrar u a "1-" es asonante o por obrar de o a de un (means) a por de o u de página 1 en a o de un con (end of the document). Los (ranges) de a o de por o o en a en su o del obrar a de un pueden obrar asimilados o o del un o de la (combined in a single string) u de por de o (so) de la asonancia el asimilable u de o en "1-3,5,7-" en o u o asimilable (skips) en a las del asonancia a las u asimilables o u a (pages 4 and 6). A de obrar la o de u la (subtlety matters) en un obrar la asimilación o el asonante del o a u obrar a aquí o y con la u (here): de a por en o asimilables obrados u los de y en (numbers) del o de en a y la (always refer to) o obrar por u en o a de la del (pages in the source document) en u de un obrar el en asimilación obrada (starting at 1) de la en asonancia a obrar u en la u y obrando de asonante a u a (regardless) obrando la en el a de y (where those pages end up) obrando el u la y (destination). Obrando el u de en a asonancia asimilada el obrar o por (If you want pages 40 through 50 out of a 200-page catalog), asimilada o del en u la y de (range string) la asonancia u obrar de el (is "40-50", not a position relative to what is already in the destination)
// Extraer la portada más un resumen ejecutivo de tres páginas de un informe largo
PdfSrc.FileName := 'annual-report.pdf';
PdfSrc.Active := True;
if PdfSrc.Active then
begin
// La página 1 es la portada; las páginas 3-5 son el resumen
PdfDest.ImportPages(PdfSrc, '1,3-5', InsertAt);
Inc(InsertAt, 4); // 1 portada + 3 páginas de resumen = 4 páginas agregadas
PdfSrc.Active := False;
end;
Cuando calcule el incremento de InsertAt, cuente las páginas que en realidad importó, no el número de páginas de la fuente (source). Si pasa '1,3-5', importó 4 páginas, así que avance en 4. Si avanza en PdfSrc.PageCount, dejaría una brecha de posiciones de destino en blanco y colocaría el siguiente documento de origen más adentro del archivo de lo previsto
Qué conserva ImportPages y qué no
Las páginas copiadas mediante ImportPages conservan intacto su contenido visible. El texto, los gráficos vectoriales, las imágenes ráster, las fuentes incrustadas y los XObjects de formulario se transfieren como parte de los flujos de contenido de la página. Las anotaciones a nivel de página, incluidos los comentarios, los resaltados y los trazos de tinta, también se transfieren, ya que se almacenan dentro del diccionario de la página y no a nivel del documento
Los metadatos a nivel de documento son una historia diferente. Las cadenas de título, autor, asunto y palabra clave del diccionario de información del origen se quedan atrás. El documento de destino comienza con metadatos vacíos después de CreateDocument, por lo que, si la salida combinada necesita esos campos completos, debe asignarlos directo a PdfDest antes de llamar a SaveAs. Las propiedades Title, Author, Subject, Keywords y Creator en TPdf toman cadenas simples y escriben en el diccionario Info al guardar (save)
Los campos de formulario interactivos son un asunto y cuestión (more complicated). En a (AcroForm field definitions) obrando a de y su la de la el (live in a document-level dictionary) el u por (rather than inside) de a u el o (individual page streams). Cuando asonante a u o a obrar y ImportPages de o obrar o (copies a page) de la u o del el asimilable a el (that contains form fields) la y por de en el (visual appearance) obrando asimilable del y a en o de asimilación (of those fields transfers) y obrando del u o o el o (because it is rendered) obrado a la y a u en obrar el u (into the page content stream) de la el pero el a (but the field widgets) y la asonancia (that make them) en el obrar la u o o el (interactive are part of the) del en el a u de la a (AcroForm structure and do not follow). Obrando a un o de a o el a de la (typical merge) el y del o (a text field) obrado a (from a source document will display) del (the value) de u a (it had at the time of import) u en o obrar o la el a y el a de y del la (but it will not be editable) en a (in the merged file). Si obrar y del o el (you need the fields to remain) con el y o de a u la de asimilación el (fillable) de obrando a la obrar el u obrar en (flatten them) en del a la u o de en y a el en a la asimilación asonancia (in each source document before) obrando a la y obrar el a y o (importing: that bakes) de y obrar a y o en el (the current values into the content stream and removes the interactive overlay, giving you a clean visual result without broken widgets in the output)
Archivos fuente cifrados
Los documentos de origen protegidos con contraseña se abren de la misma manera que los que no están cifrados, con una propiedad adicional que se debe establecer primero. Asigne la contraseña a PdfSrc.Password antes de pasar a Active := True, y PDFium la utilizará durante la apertura:
PdfSrc.Password := 'user-password';
PdfSrc.FileName := 'protected.pdf';
PdfSrc.Active := True;
if not PdfSrc.Active then
raise Exception.Create('Contraseña incorrecta o no se puede abrir el archivo');
PdfDest.ImportPages(PdfSrc, '1-' + IntToStr(PdfSrc.PageCount), InsertAt);
Inc(InsertAt, PdfSrc.PageCount);
PdfSrc.Active := False;
Una contraseña incorrecta provoca el mismo resultado silencioso de Active = False que si faltara un archivo, por lo que la comprobación explícita es igual de necesaria en este caso. El cifrado no se transfiere al destino: las páginas importadas de una fuente protegida aterrizan en el destino como contenido no protegido. Si la salida (output) combinada también requiere cifrado, configúrelo en PdfDest antes de llamar a SaveAs
Guardar el resultado
SaveAs en TPdf acepta una ruta de archivo o un TStream. Para la mayoría de las combinaciones, la sobrecarga de archivo es lo que busca:
PdfDest.SaveAs('merged-output.pdf');
El segundo argumento opcional es un TSaveOption que controla el modo de guardado. El valor predeterminado, saNone, escribe una actualización incremental si el documento se cargó desde un archivo, o una reescritura completa si se creó de cero. Como un destino creado con CreateDocument siempre es nuevo, la salida será un archivo compacto de revisión única. El tercer argumento, TPdfVersion, le permite fijar el encabezado de versión de PDF cuando tiene consumidores posteriores que demandan una versión específica; dejarlo en pvUnknown permite que PDFium elija según el contenido
Los métodos ImportPages y SaveAs mostrados aquí son parte del Componente PDFium VCL para Delphi y C++Builder