PDF Library for Delphi combina dos documentos AcroForm con una política explícita para los campos que comparten un nombre. MergeDocumentEx toma el identificador del documento de origen y una de tres estrategias: dfsReject rechaza la combinación, dfsMerge conserva el nombre compartido y sincroniza los valores, y dfsAutoNumber renombra los campos entrantes de forma determinista. El escaneo de nombres ocurre antes de que cambie cualquier número de objeto, así que una combinación rechazada deja ambos documentos completamente utilizables
Cualquiera que haya ensamblado un paquete de solicitud en PDF se ha topado con esto. Tres formularios, cada uno con un campo llamado Signature o Date o Total, se combinan en un solo archivo. En un AcroForm, el nombre de campo totalmente calificado es la identidad del campo, así que dos campos con el mismo nombre no son en absoluto dos campos: llenar uno llena el otro, y una firma aplicada a uno cubre un alcance que nadie pretendía
¿Por qué se decide la colisión de nombres antes de la combinación?
El MergeDocument más antiguo concatena los dos arreglos de campos raíz de AcroForm y no ofrece ninguna opción. Peor aún, cuando el resultado no es utilizable, el descubrimiento ocurre después de que los números de objeto se renumeraron y los árboles de página se cosieron, lo que deja a quien llama con un documento en un estado en el que ninguno de los originales estaba
MergeDocumentEx invierte el orden. Recolecta los nombres de campo de nivel superior de ambos documentos, los compara, y aplica la estrategia antes de que se mueva nada. Un rechazo es, por lo tanto, una operación nula y limpia: el documento de destino no se toca, el documento de origen no se toca, y ambos permanecen abiertos y utilizables, lo cual la prueba de combinación verifica leyendo de vuelta un valor de campo del origen después de una combinación rechazada
La comparación usa un conjunto de nombres ordenado y sensible a mayúsculas, así que el costo es proporcional al conteo combinado de campos por un factor logarítmico, en lugar de al producto de los dos conteos. La sensibilidad a mayúsculas es la elección correcta aquí porque los nombres de campo de PDF son sensibles a mayúsculas; plegarlas combinaría campos que la especificación trata como distintos
Las tres estrategias, y cuándo es correcta cada una
dfsReject es la estrategia para canalizaciones automatizadas que no deben producir documentos ambiguos. La combinación devuelve cero y LastErrorCode reporta 705, un código dedicado para que los nombres duplicados puedan distinguirse de cualquier otro fallo de combinación y enrutarse hacia un remedio específico, generalmente renombrar campos más arriba en la canalización
dfsMerge conserva el nombre compartido de forma deliberada y sincroniza el valor de destino y el valor predeterminado en el campo de origen, así que un visor conforme trata los varios widgets como un solo campo con nombre lógico, que es el comportamiento estándar de AcroForm para un campo con varias anotaciones de widget. Lo que no hace es fusionar diccionarios de campo distintos en un solo objeto. Cada campo conserva su propia asociación de página, apariencia y acciones, porque colapsarlos descartaría en silencio el formato y el comportamiento que pertenecen al documento entrante
dfsAutoNumber renombra los duplicados entrantes agregando un sufijo numérico que empieza en _2 y toma el primero libre. El resultado es reproducible: depende solo de los nombres presentes, nunca de los números de objeto de campo, así que combinar el mismo par de documentos dos veces produce los mismos nombres ambas veces. Esa propiedad importa cuando código posterior, una importación FDF o un mapeo de base de datos hace referencia a campos por nombre
uses
PDFlibrary;
var
Lib: TPDFlib;
TargetDoc, SourceDoc: Integer;
begin
Lib := TPDFlib.Create;
try
TargetDoc := Lib.SelectedDocument;
Lib.LoadFromFile('application-part1.pdf', '');
SourceDoc := Lib.NewDocument;
Lib.LoadFromFile('application-part2.pdf', '');
Lib.SelectDocument(TargetDoc);
if Lib.MergeDocumentEx(SourceDoc, dfsReject) = 0 then
begin
if Lib.LastErrorCode = 705 then
begin
// Ambos documentos siguen intactos - reintentar con una política
Log('duplicate field names; retrying with auto-numbering');
Lib.MergeDocumentEx(SourceDoc, dfsAutoNumber);
end;
end;
Lib.SaveToFile('application-complete.pdf');
finally
Lib.Free;
end;
end;
Observa el patrón de dos pasos en ese código, que solo es posible porque el rechazo no es destructivo. Intenta primero la política estricta, inspecciona el error, y luego decide. Con una combinación que falla a mitad de camino, el respaldo tendría que empezar de nuevo recargando ambos archivos
Cómo se ve el formulario combinado después
Bajo dfsMerge, un campo de destino llamado Shared que lleva "Target value" y un campo de origen con el mismo nombre producen dos campos, ambos llamados Shared, ambos reportando el valor de destino, porque el valor de destino y el valor predeterminado se sincronizan en el campo entrante. Esa es la semántica prevista para un nombre compartido: un campo lógico, varios widgets, un valor
Bajo dfsAutoNumber, la misma entrada produce Shared y Shared_2 como campos separados con valores independientes. Elige entre las dos haciéndote una sola pregunta: ¿debería llenar un control llenar el otro? Para un nombre de firmante repetido en cada parte de un paquete, sí, y dfsMerge es correcto. Para un total que significa algo distinto en cada formulario, no, y la numeración automática es correcta
// Después de una combinación, enumera lo que realmente obtuviste
for I := 1 to Lib.FormFieldCount do
Log(Format('%d: %s = %s',
[I, Lib.GetFormFieldTitle(I), Lib.GetFormFieldValue(I)]));
Notas prácticas para ensamblar paquetes de formularios
Una combinación exitosa consume el documento de origen: se elimina de la lista de documentos de la biblioteca, que es por qué DocumentCount baja de dos a uno. No sigas usando el identificador de origen después. La versión del documento se eleva a la más alta de las dos, así que combinar un formulario PDF 2.0 en un documento 1.7 produce un archivo 2.0
El orden importa para los nombres. Combinar A en B y combinar B en A producen resultados de numeración automática distintos, ya que el documento que hace la combinación conserva sus nombres sin cambios. Cuando un paquete tiene un formulario primario canónico, haz que ese sea el destino
Los campos de firma merecen su propia consideración. Una firma que se aplicó antes de una combinación cubre solo la revisión que firmó, así que combinar la invalida en el sentido práctico de que el archivo ha cambiado desde la firma. Ensambla primero y firma el documento ensamblado, en lugar de combinar partes ya firmadas. Cuando la combinación se trata del contenido de página en lugar de formularios, la ruta más rápida descrita en combinación rápida de PDF con desplazamiento de referencias por bytes es la mejor herramienta
Por último, planifica el lado de los datos del paquete junto con la combinación. Si los valores de campo llegan desde un sistema externo, decide si ese sistema direcciona los campos por nombre antes de elegir la numeración automática, porque Shared_2 no coincidirá con un mapeo que espera Shared. Los formatos de importación y exportación se cubren en intercambio de datos de formulario FDF, XFDF y XFA, y el comportamiento de scripting a nivel de campo que también puede verse afectado por el renombrado se cubre en acciones de formulario interactivas y JavaScript
La combinación de formularios, el intercambio de datos y la firma se ejecutan en la misma biblioteca para Delphi, C++Builder y Free Pascal; la lista completa de funciones está en la página de PDF Library for Delphi