Un equipo de siniestros publica un formulario de consentimiento generado desde Delphi que en Acrobat parece impecable: todas las casillas se dibujan, todos los rótulos quedan alineados y la maqueta coincide con el original en papel. Tres semanas después, el servicio de recepción empieza a rechazar un tercio de los envíos. El backend espera que la casilla de consentimiento publique el valor Y; el formulario exporta Yes, porque quien lo construyó dio por hecho que el rótulo visible y el valor de exportación eran lo mismo. Nada en la página renderizada revela la diferencia. Esa es la propiedad que define el desarrollo con AcroForm: el widget visible y el contrato de datos que hay debajo son estructuras separadas, y solo una de ellas queda verificada cuando alguien abre el fichero y lo mira
HotPDF es un componente VCL nativo que escribe diccionarios de campos AcroForm directamente desde código Delphi y C++Builder, de modo que ambas estructuras quedan bajo control explícito del programa, y ambas pueden estar mal de forma independiente. Lo que sigue cubre creación de campos, acciones de botones y JavaScript a nivel de campo, con especial atención a los puntos donde la vista previa de la página y los datos del formulario se separan sin hacer ruido
Los nombres de campo son claves de enrutamiento, no rótulos
Cada campo AcroForm lleva un nombre plenamente cualificado, e ISO 32000-1 §12.7.3 define ese nombre, nunca el rótulo visible, como la clave bajo la que viaja el valor del campo en envíos FDF, XFDF y HTTP. De ahí se derivan dos consecuencias que suelen sorprender a quienes vienen del diseño de formularios VCL, donde el nombre de un control es poco más que un identificador en código
Primero, dos campos con el mismo nombre plenamente cualificado no son dos campos. El modelo PDF los trata como dos anotaciones de widget de un único campo, que comparten un solo valor: escriba en uno y el otro se actualiza inmediatamente. Repetir el nombre del cliente en cada página de un contrato es un uso legítimo de este comportamiento. Obtenerlo por accidente, porque un bucle de generación reutilizó 'Field1' en tres páginas, es un fallo que ninguna inspección visual detecta; cada página sigue mostrando su propio widget, y el reflejo solo aparece cuando el usuario empieza a escribir
Segundo, los nombres con puntos como applicant.email crean una jerarquía: el nodo padre applicant agrupa sus hijos para operaciones de reinicio y envío que afectan solo a una parte del formulario. Nombrar los campos jerárquicamente desde el principio no cuesta nada y se amortiza la primera vez que el sistema receptor pide "solo el bloque applicant"
Los botones de radio añaden una tercera regla: los botones que deben alternarse como un único grupo tienen que compartir nombre de grupo. En HotPDF, las llamadas a AddRadioButton que usan el mismo nombre de grupo adjuntan sus widgets a un campo padre, y el valor de exportación de cada botón, 'basic', 'full', identifica la opción seleccionada. Dé un nombre único a cada botón y obtendrá interruptores independientes de activado/desactivado, no un grupo mutuamente excluyente
Crear el conjunto de campos página a página
HotPDF coloca los campos mediante métodos de THPDFPage, por lo que cada campo pertenece al objeto de página que lo creó. Aquí domina una trampa de secuencia: AddPage redirige inmediatamente CurrentPage a la página nueva, así que cualquier llamada de campo emitida después cae en esa página nueva aunque lógicamente pertenezca a la anterior. Construya cada página completa, contenido dibujado y campos juntos, antes de llamar a AddPage
procedure BuildClaimForm(Pdf: THotPDF);
begin
// Page 1: applicant block
Pdf.CurrentPage.AddTextField('applicant.name', '', Rect(50, 700, 300, 722));
Pdf.CurrentPage.AddTextField('applicant.email', '', Rect(50, 660, 300, 682));
Pdf.CurrentPage.AddCheckBox('consent', 'Y', Rect(50, 620, 70, 640), False);
Pdf.CurrentPage.AddRadioButton('coverage', 'basic', Rect(50, 580, 70, 600), True);
Pdf.CurrentPage.AddRadioButton('coverage', 'full', Rect(90, 580, 110, 600), False);
Pdf.CurrentPage.AddComboBox('plan', 'Standard',
['Basic', 'Standard', 'Premium'], Rect(50, 540, 200, 565));
Pdf.AddPage; // CurrentPage now points at page 2
Pdf.CurrentPage.AddListBox('riders', 'None',
['None', 'Flood', 'Earthquake'], Rect(50, 500, 200, 600));
end;
Las coordenadas siguen la convención de PDF: el origen está en la esquina inferior izquierda de la página, la misma convención que usa TextOut para texto dibujado. Por tanto, Rect(50, 100, 200, 120) queda cerca de la parte inferior de una página Letter, no de la superior. Los equipos que migran tablas de maquetación desde VCL, donde Y crece hacia abajo desde arriba, producen de forma fiable un primer borrador con todos los campos reflejados verticalmente. Convierta las coordenadas en un único helper compartido, no en cada punto de llamada, para que la corrección llegue a todo a la vez
Conectar botones a acciones URI, JavaScript y submit
Un botón pulsador no hace nada hasta que se le adjunta una acción. HotPDF expone los tipos de acción de ISO 32000-1 §12.6.4 mediante la enumeración THPDFButtonAction: baURI, baJavaScript, baSubmitURL, baResetForm, baHide, baShow, baNamed, además de dos métodos que crean el botón y vinculan su acción en un solo paso
// Open a help page in the system browser
Pdf.CurrentPage.AddPushButtonWithAction('btnHelp', 'Help',
'https://www.example.com/claims-help', Rect(320, 700, 420, 730), baURI);
// Run viewer-side JavaScript
Pdf.CurrentPage.AddPushButtonWithAction('btnRecalc', 'Recalculate',
'app.alert("Totals updated.");', Rect(320, 660, 420, 690), baJavaScript);
// Submit as XFDF and keep empty fields in the payload
Pdf.CurrentPage.AddPushButtonWithSubmitAction('btnSubmit', 'Submit claim',
'https://api.example.com/claims', Rect(320, 620, 420, 650),
[sffXFDF, sffIncludeNoValueFields]);
Los indicadores de envío merecen más diseño del que suelen recibir. AddPushButtonWithSubmitAction recibe un conjunto THPDFSubmitFormFlags, y un conjunto vacío produce un post url-encoded simple, el formato que muchos endpoints de ejemplo aceptan y muchos endpoints de producción no. Añadir sffXFDF cambia la carga útil a XFDF; sffGetMethod cambia el verbo HTTP; sffIncludeNoValueFields hace que los campos vacíos aparezcan en la carga útil en lugar de descartarse silenciosamente, algo importante cuando el consumidor distingue entre "ausente" y "en blanco". El conjunto de flags forma parte efectiva de su contrato de interfaz con el endpoint receptor, así que elíjalo junto con el equipo que analiza el envío, no después del primer lote rechazado
JavaScript a nivel de campo: keystroke, format, validate
Más allá de los clics de botón, HotPDF adjunta JavaScript a los eventos por campo que los visores con motor de script disparan durante la entrada de datos. Los tres disparadores corresponden a momentos distintos del ciclo de entrada: las acciones keystroke se ejecutan cuando llegan caracteres, las acciones format reescriben el valor mostrado después de confirmar un cambio, y las acciones validate aceptan o rechazan el valor confirmado
// Reject committed values that are not plausible email addresses
Pdf.AttachFieldKeyStrokeAction('applicant.email',
'if (event.willCommit && !/^[\w.-]+@[\w.-]+\.\w+$/.test(event.value)) event.rc = false;');
// Display US phone numbers as (NNN) NNN-NNNN
Pdf.AttachFieldFormatAction('applicant.phone',
'event.value = event.value.replace(/(\d{3})(\d{3})(\d{4})/, "($1) $2-$3");');
// Refuse applicants under 18 at commit time
Pdf.AttachFieldValidateAction('applicant.age',
'if (parseInt(event.value) < 18) event.rc = false;');
Establecer event.rc = false dentro de un script keystroke o validate hace que el visor rechace la entrada. La limitación que conviene tener presente: este código solo se ejecuta en visores que incorporan un motor JavaScript. Acrobat y unos pocos productos de escritorio lo ejecutan; la mayoría de lectores móviles, renderizadores integrados en el navegador y tuberías de impresión lo ignoran por completo. Los scripts de campo elevan la calidad de los datos para los usuarios que los reciben; no son una frontera de seguridad, y cada valor enviado sigue necesitando validación en el servidor cuando llega
Defectos que pasan una revisión visual
Algunos defectos de formulario son estructuralmente invisibles para una comprobación de abrir y mirar. Estos cuatro explican la mayoría de escalados AcroForm que llegan a soporte, y cada uno puede detectarse mecánicamente antes de publicar
- Deriva del valor de exportación. Una casilla creada como
AddCheckBox('consent', 'Yes', ...)publicaYes; un consumidor que compara conYrechaza todos los envíos mientras la página parece perfecta. Exporte el formulario rellenado como XFDF desde Acrobat y compare los valores con el esquema del consumidor - Reflejo accidental de valores. Los nombres plenamente cualificados duplicados fusionan campos en uno. El síntoma aparece al introducir datos, nunca al generarlos, así que pruebe escribiendo en el formulario, no renderizándolo
- Valores de combo fuera de la lista de opciones. Si el valor actual pasado a
AddComboBoxno está entre las opciones, los visores discrepan sobre si mostrarlo, dejarlo en blanco o marcarlo. Mantenga el valor por defecto dentro de la lista - Campos editables después de cerrar el flujo. HotPDF no tiene una llamada de aplanado de apariencia para campos AcroForm; la forma soportada de congelar un formulario completado es crear los campos con el flag
ffReadOnly, que mantiene el valor renderizado mediante el flujo de apariencia propio del campo y bloquea las ediciones. Un campo de solo lectura sigue siendo un objeto de formulario vivo, que las herramientas posteriores de ensamblado y firma gestionan de forma predecible
Un comportamiento del lado del visor merece una nota de regresión aunque ningún cambio de código lo arregle: las implantaciones empresariales de Acrobat pueden desactivar JavaScript o restringir destinos de envío por política, por lo que una acción que funcionó durante todo el desarrollo puede quedar inerte en el escritorio de un cliente. Proporcione una alternativa visible para el envío, al menos una instrucción imprimible, para el caso en que el botón no haga nada
Cómo el trabajo con formularios conecta con el resto del documento
Un campo de firma es en sí mismo un tipo de campo AcroForm, por lo que un formulario que se vaya a certificar o contrafirmar más adelante debería reservar ese campo durante la generación en lugar de parchearlo después; la mecánica a nivel de bytes se cubre en el artículo complementario sobre firmas digitales y firma PAdES con HotPDF. Si las entradas llegan como paquetes XFA en lugar de AcroForm nativo, aplanar XFA en campos AcroForm es un flujo aparte con su propio modelo de pérdidas, ya que las dos tecnologías de formulario son mutuamente excluyentes dentro de un mismo fichero
Los métodos de campos, acciones y disparadores mostrados aquí forman parte de la API estándar de HotPDF Component para Delphi y C++Builder; la página del producto enlaza la referencia completa, incluidas las sobrecargas de flags de campo y la enumeración completa de flags de envío