Une action AcroForm est un dictionnaire attaché à un widget qui indique à la visionneuse ce qu'elle doit faire lorsque quelque chose arrive à ce widget. Cliquez sur un bouton et la visionneuse lit son dictionnaire d'actions : une action URI ouvre une adresse Web, une action JavaScript exécute un script, une action SubmitForm publie les valeurs de champ collectées vers un point de terminaison, une action ResetForm les réinitialise à leurs valeurs par défaut. L'action est une donnée, pas un comportement intégré au fichier. La norme ISO 32000-1 §12.6 définit la forme du dictionnaire ; la visionneuse fournit le moteur qui l'interprète. Cette séparation a de l'importance car une action parfaitement écrite dans le PDF ne fait toujours rien si le lecteur à l'autre bout n'a pas de moteur pour elle, et de nombreux tracas liés à AcroForm remontent à cette lacune plutôt qu'à un champ malformé
HotPDF écrit ces dictionnaires directement depuis Delphi et C++Builder, aux côtés des widgets de champ auxquels ils sont rattachés. Deux structures sont en jeu pour chaque formulaire interactif : le widget que l'utilisateur voit sur la page, et la machinerie du champ et de l'action en dessous qui porte les données et le câblage. Ils sont modifiés indépendamment, et l'un peut être incorrect alors que l'autre semble correct. Les sections ci-dessous détaillent le nommage des champs, les actions de bouton elles-mêmes, le JavaScript au niveau du champ et la classe de défauts qui survit à une vérification visuelle parce qu'elle réside entièrement dans la deuxième structure
Les noms de champs sont des clés de routage, pas des légendes
Chaque champ AcroForm porte un nom pleinement qualifié. La norme ISO 32000-1 §12.7.3 fait de ce nom, et non de la légende visible, la clé sous laquelle la valeur du champ voyage lorsque le formulaire est exporté ou soumis. Les développeurs venant de la conception VCL ont tendance à traiter le nom d'un contrôle comme un identifiant de code privé, ce qui n'est pas le cas ici. C'est le format de transmission
La première chose qui en découle est que deux champs ayant le même nom pleinement qualifié ne sont pas deux champs. PDF les traite comme deux annotations de widget d'un seul champ, partageant une valeur, de sorte que taper dans l'un met à jour l'autre sur-le-champ. C'est exactement ce que vous souhaitez lorsqu'un nom de client doit se répéter sur chaque page d'un contrat. C'est un bug lorsqu'une boucle de génération réutilise 'Field1' sur trois pages par accident. Aucune inspection visuelle ne détecte le second cas. Chaque page dessine toujours sa propre boîte, et le lien ne fait surface que lorsque quelqu'un commence à taper
Les noms avec des points tels que applicant.email construisent une hiérarchie. Le nœud parent applicant regroupe ses enfants, ce qui permet à une réinitialisation ou à une soumission de ne cibler qu'une partie d'un formulaire. Nommer les champs de cette façon dès le départ ne coûte rien, et cela s'amortit la première fois que le système de réception demande uniquement le bloc du demandeur
Les boutons radio ont leur propre règle. Les boutons qui doivent basculer ensemble doivent partager un nom de groupe. Dans HotPDF, les appels à AddRadioButton qui transmettent le même nom de groupe rattachent leurs widgets à un champ parent, et la valeur d'exportation de chaque bouton ('basic' ou 'full') identifie l'option choisie. Donnez à chaque bouton un nom distinct et vous obtenez une rangée de commutateurs marche/arrêt indépendants au lieu d'un seul groupe mutuellement exclusif, ce qui se rend de manière identique et se comporte de manière incorrecte
Création de l'ensemble de champs page par page
HotPDF place les champs via les méthodes THPDFPage, de sorte que chaque champ appartient à l'objet de page qui l'a créé. Le piège de séquencement à surveiller est AddPage. Il redirige CurrentPage vers la nouvelle page dès qu'il revient, de sorte que tout appel de champ ultérieur atterrit sur la nouvelle page même lorsque le champ appartenait logiquement à la page que vous venez de quitter. Terminez chaque page, le contenu dessiné et les champs ensemble, avant d'appeler 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;
Les coordonnées utilisent la convention PDF, avec l'origine dans le coin inférieur gauche de la page. C'est la même origine que TextOut utilise pour le texte dessiné, de sorte que Rect(50, 100, 200, 120) se situe près du bas d'une page Letter, pas en haut. VCL place Y en haut et l'agrandit vers le bas, de sorte qu'un tableau de mise en page porté tel quel ressort verticalement en miroir, chaque champ basculé au mauvais bout de la page. Effectuez la conversion une seule fois dans un assistant partagé au lieu de le faire à chaque site d'appel, et une seule correction corrige l'ensemble du formulaire
Câblage des boutons aux actions URI, JavaScript et de soumission
Un bouton-poussoir est inerte tant qu'une action ne lui est pas attachée. HotPDF expose les types d'actions de la norme ISO 32000-1 §12.6.4 via l'énumération THPDFButtonAction (baURI, baJavaScript, baSubmitURL, baResetForm, baHide, baShow, baNamed), et fournit deux méthodes qui créent le bouton et lient son action en un seul appel
// 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]);
Les indicateurs de soumission méritent plus de réflexion qu'ils n'en reçoivent habituellement. AddPushButtonWithSubmitAction prend un ensemble THPDFSubmitFormFlags, et un ensemble vide produit une publication url-encodée simple, qui est le format que de nombreux points de terminaison d'exemple acceptent et que de nombreux points de terminaison de production rejettent. L'ajout de sffXFDF fait passer la charge utile à XFDF. sffGetMethod modifie le verbe HTTP. sffIncludeNoValueFields conserve les champs vides dans la charge utile au lieu de les supprimer silencieusement, ce qui a de l'importance dès le moment où le consommateur distingue "absent" de "vide". L'ensemble d'indicateurs fait partie de votre contrat d'interface avec le point de terminaison de réception, alors convenez-en avec l'équipe qui analyse la soumission, et non après le premier lot rejeté
JavaScript au niveau du champ : frappe, format, validation
Les clics de bouton ne sont pas le seul endroit où résident les actions. HotPDF attache également du JavaScript aux événements par champ que les visionneuses capables de scripter déclenchent pendant qu'un utilisateur saisit des données. Il y a trois déclencheurs, et ils se déclenchent à différents moments du cycle de vie de la saisie. Une action de frappe (keystroke) s'exécute à l'arrivée de chaque caractère, et de nouveau lors de la validation. Une action de formatage réécrit la valeur affichée après la validation d'une modification, uniquement à des fins de présentation. Une action de validation a le dernier mot, acceptant ou refusant la valeur validée avant qu'elle ne devienne la valeur du champ
// 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;');
Définir event.rc = false dans un script de frappe ou de validation indique à la visionneuse de rejeter la saisie. Le hic, c'est que rien de tout cela ne s'exécute à moins que la visionneuse n'embarque un moteur JavaScript. Acrobat et quelques produits de bureau en possèdent un. La plupart des lecteurs mobiles, des moteurs de rendu intégrés aux navigateurs et des pipelines d'impression n'en ont pas, et ils abandonnent les scripts sans se plaindre. Ainsi, les scripts de champ améliorent la qualité des données pour le sous-ensemble d'utilisateurs dont le lecteur les exécute, et c'est tout ce qu'ils font. Ils ne constituent pas une barrière de sécurité. Chaque valeur soumise doit toujours être validée sur le serveur une fois qu'elle arrive, car vous ne pouvez pas supposer que le client a vérifié quoi que ce soit
Défauts qui passent l'examen visuel
Les défauts AcroForm les plus difficiles à détecter sont ceux qui résident dans la structure de données plutôt que dans le rendu, car ouvrir le fichier et le regarder ne vous apprend rien. Quatre d'entre eux reviennent assez souvent pour valoir la peine d'être nommés, et chacun dispose d'un test mécanique qui le trouve avant la sortie
- Dérive de la valeur d'exportation. Une case à cocher créée sous la forme
AddCheckBox('consent', 'Yes', ...)publieYes. Un consommateur qui correspond surYrejette chaque soumission alors que la page a l'air parfaite. Remplissez le formulaire, exportez-le en XFDF depuis Acrobat et comparez les valeurs par rapport au schéma auquel le consommateur s'attend réellement - Mise en miroir accidentelle de valeur. Deux champs qui partagent un nom pleinement qualifié fusionnent en un seul. Le symptôme se manifeste au moment de la saisie des données et jamais au moment de la génération, le test consiste donc à taper dans le formulaire, et non à le rendre et à regarder le résultat
- Valeurs combinées en dehors de la liste d'options. Lorsque la valeur courante transmise à
AddComboBoxne fait pas partie des options répertoriées, les visionneuses ne s'entendent pas sur la question de savoir s'il faut l'afficher, la masquer ou la signaler. Gardez la valeur par défaut à l'intérieur de la liste et le désaccord disparaît - Champs encore modifiables après la fermeture du flux de travail. HotPDF n'a pas d'appel d'aplatissement de l'apparence pour les champs AcroForm. La méthode prise en charge pour figer un formulaire rempli consiste à créer les champs avec l'indicateur
ffReadOnly, ce qui maintient la valeur visible via le propre flux d'apparence du champ tout en refusant les modifications. Le champ reste un objet de formulaire actif, ce que les outils d'assemblage et de signature en aval s'attendent à trouver
Un comportement du côté de la visionneuse mérite une note de régression même si aucune modification de code ne le traite. Les déploiements d'entreprise Acrobat peuvent désactiver JavaScript ou restreindre les cibles de soumission par stratégie, de sorte qu'une action qui a fonctionné à travers chaque build de développement peut rester sans effet sur un bureau client verrouillé. Prévoyez une solution de repli visible pour le cas où le bouton ne fait rien, même si cette solution de repli n'est qu'une instruction imprimée indiquant à l'utilisateur ce qu'il doit faire à la place
Où le travail sur les formulaires se connecte au reste du document
Un champ de signature est en soi un type de champ AcroForm. Un formulaire qui sera certifié ou contresigné plus tard a tout intérêt à réserver ce champ lors de la génération plutôt que de le faire corriger ultérieurement, et les raisons au niveau des octets pour cela se trouvent dans l'article d'accompagnement sur les signatures numériques et la signature PAdES avec HotPDF. Les entrées qui arrivent sous forme de paquets XFA plutôt que d'AcroForm natif constituent une situation différente : l'aplatissement de XFA en champs AcroForm est son propre flux de travail avec son propre modèle de perte, car les deux technologies de formulaires ne peuvent pas coexister dans un seul fichier
Les méthodes de champ, d'action et de déclenchement présentées ici font partie de l'API standard du Composant HotPDF pour Delphi et C++Builder ; la page du produit lie la référence complète, y compris les surcharges d'indicateurs de champ et l'énumération complète d'indicateurs de soumission