Runtime interactif de widgets XFA dynamiques
TXFAWidgetRuntime fournit un modèle d'interaction neutre vis-à-vis de l'hôte pour les formulaires XFA dynamiques sans les convertir en champs AcroForm ni aplatir leur contenu
Le runtime expose des limites et un état de widget déterministes afin qu'un bureau, un service ou un rendu personnalisé puisse fournir sa propre entrée, son propre dessin, son accessibilité et sa boucle d'événements
Créer un runtime
Créez le runtime directement à partir des octets XDP ou appelez THotPDF.CreateLoadedXFAWidgetRuntime après avoir chargé un PDF dont l'AcroForm contient une entrée /XFA à flux unique ou à tableau de paquets
var
Runtime: TXFAWidgetRuntime;
State: TXFAWidgetState;
begin
Runtime := PDF.CreateLoadedXFAWidgetRuntime;
try
if (Runtime <> nil) and (Runtime.WidgetCount > 0) then
begin
State := Runtime.Widgets[0];
Runtime.FocusWidget(State.ID);
Runtime.BeginEdit(State.ID);
Runtime.ReplaceSelection(0, Length(State.Value), 'Updated value');
if not Runtime.CommitEdit then
raise Exception.Create(Runtime.LastDiagnostic);
end;
finally
Runtime.Free;
end;
end;
La fabrique de document chargé est en lecture seule vis-à-vis du graphe d'objets PDF et des XFAFlattenWarnings existants ; l'enregistrement du document conserve ses entrées d'origine /XFA et /NeedsRendering
Modèle d'interaction
FocusWidgetetClearFocusmaintiennent un widget focalisé unique, exécutent les scripts enter/exit et publient des callbacks ordonnés après validationBeginEdit,ReplaceSelection,CancelEditetCommitEditpréservent le texte Unicode y compris les espaces de tête et de finHitTestmappe les coordonnées de page sur le widget visible le plus en avantOnWidgetInvalidated,OnLayoutChangedetOnFocusChangedlaissent l'hôte actualiser uniquement l'état affecté
Les commits interactifs synchronisent les valeurs scalaires natives existantes vers les alias hidden et invisible, y compris les scopes de données répétées réels et un rollback exact à l'octet près
Validation et calcul atomiques
Une validation de commit résout les liaisons SOM explicites et le contexte de données de ligne répétée courant avant d'exécuter les scripts de validation et de calcul
La valeur éditée, les valeurs calculées, les nœuds de données, le modèle de widget, l'état de focus et d'édition, les avertissements et les compteurs de passe sont publiés ensemble seulement après que la validation et la mise en page se stabilisent
Les scripts rejetés, les budgets épuisés, les limites de sélection UTF-16 invalides et les exceptions de mise en page ou de mesure d'hôte restaurent tout l'état précédent et définissent LastDiagnostic
Budgets
TXFAWidgetRuntimeOptions limite le nombre de widgets, la longueur de la valeur éditée, les passes de calcul, les passes de réécoulement, les opérations de mise en page, les opérations de script, le temps de script écoulé et les autres ressources FormCalc ou JavaScript
Le plafond de widgets s'applique pendant l'ajout des éléments de mise en page et des fragments de pagination, tandis que la décompression XFA chargée et l'assemblage des paquets s'arrêtent à la limite d'entrée du DOM XFA avant l'analyse
MaxLayoutOperations vaut 200000 par défaut et borne à la fois le parcours du document et le travail de mise en page ; la profondeur de mise en page est limitée à 128, la profondeur de cible d'événement et d'initialisation d'instance à 64, et le dispatch récursif d'événements est rejeté
Événements dynamiques littéraux par défaut
DispatchEvent accepte des opérations littérales séparées par des points-virgules dans les scripts d'événements de champs correspondants, avec types de contenu FormCalc ou JavaScript ; ces opérations sont analysées directement et n'exigent pas de DLL JavaScript
row.instanceManager.addInstance(true);
row.instanceManager.removeInstance(0);
target.presence = "hidden";
addInstance ajoute une instance et accepte true, false, 1 ou 0 comme argument de fusion ; un argument omis vaut true, et l'écriture abrégée _row.addInstance(1) est également acceptée
Le modèle pris en charge lie chaque groupe de données homonyme existant, si bien qu'une nouvelle instance reçoit un nouveau groupe de données initialisé à partir des valeurs par défaut du modèle quel que soit l'argument de fusion ; la création d'instance ne clone pas les valeurs saisies de la rangée précédente
removeInstance utilise un index entier à partir de zéro parmi les groupes de données homonymes du subform sélectionné ; le subform doit avoir un élément occur avec un maximum répétable, et les deux opérations appliquent son minimum et son maximum, y compris max="-1"
La mutation d'instance exige une liaison implicite de jeu de données nommé et des noms de données XML ASCII ; la liaison explicite par référence de données et les contextes parents ambigus sont rejetés avant la publication de la transaction
Les cibles se résolvent via les enfants nommés du template dans les scopes englobant l'événement, y compris les chemins pointés et this.parent ; un manager imbriqué dans un scope répété utilise le groupe de données propre à l'événement
presence accepte visible, hidden et invisible sur les champs, draws, subforms et exclusion groups du template ; le contenu hidden n'occupe aucun espace de flux, tandis que le contenu invisible conserve son espace et est omis des widgets et de la sortie d'aplatissement
Avec le parseur littéral par défaut, les changements de présence s'appliquent au nœud de template sélectionné et donc à toutes ses occurrences répétées ; la présence indexée par instance, inactive, les expressions arbitraires, variables, conditionnelles, boucles et autres opérations de scripts d'événements exigent un hôte d'événements supplémentaire
Un lot d'événements publie ensemble les changements de données, calculs, mise en page, focus et état d'édition après stabilisation du réécoulement ; toute opération rejetée ou tout budget d'entrée, d'opération, d'instance, de valeur, de widget, de mise en page ou de temps écoulé épuisé restaure l'état complet précédent du document et de l'interaction
La suppression d'une rangée préserve le focus et les éditions en attente des groupes de données survivants même lorsque leurs index de widget changent ; la suppression ou la dissimulation du widget focalisé efface le focus et publie le callback de focus correspondant après succès
Exécution générale d'événements JavaScript et FormCalc
Définissez TXFAWidgetRuntimeOptions.ScriptOptions.EnableJavaScript pour activer le pont QuickJS borné livré pour l'exécution des événements ; les événements JavaScript prennent alors en charge fonctions, closures, tableaux, conditions, boucles et exceptions plutôt que la grammaire littérale par défaut
Options := TXFAWidgetRuntimeOptions.Default;
Options.ScriptOptions.EnableJavaScript := True;
Runtime := TXFAWidgetRuntime.Create(XDPBytes, 595, 842, Options);
L'hôte d'événements expose this, les champs et subforms nommés englobants, parent, rawValue, presence et access inscriptibles, instanceManager.count/min/max, addInstance, removeInstance, insertInstance, moveInstance et setInstances, xfa.resolveNode, xfa.resolveNodes, la résolution au niveau des nœuds et xfa.layout.relayout
Les listes de nœuds prennent en charge l'indexation numérique, length et item ; les valeurs des champs répétés conservent leurs propres cibles de jeux de données, et les chemins SOM acceptent les enfants nommés avec des index numériques ou wildcard pour les nœuds hôtes vivants
const values = xfa.resolveNodes("main.row[*].amount[*]");
let total = 0;
for (const field of values) total += Number(field.rawValue);
xfa.resolveNode("main.total").rawValue = total;
if (total > 100) this.parent.warning.presence = "visible";
Chaque subform répété possède son propre objet vivant et ses champs enfants ; index, les getters enfants parents et les requêtes SOM ultérieures suivent immédiatement les opérations d'insertion, de déplacement et de suppression à l'intérieur du script
addInstance et insertInstance renvoient un sous-arbre vivant initialisé à partir des valeurs par défaut du template, si bien que le script peut écrire les valeurs de ses champs avant que l'événement ne publie ; les handles supprimés rejettent les lectures ou écritures ultérieures
Les changements de présence et d'accès des scripts généraux persistent dans le paquet form XFA standard par occurrence, tandis que les valeurs de champs et les opérations de répétition persistent dans datasets ; insert, move et remove maintiennent l'état de formulaire correspondant aligné avec les groupes de données
Avec le même opt-in, les scripts d'événements FormCalc se compilent vers le moteur borné et prennent en charge var, if/elseif/else, for avec upto ou downto, while, foreach, func avec résultats implicites, l'arithmétique et les comparaisons, la concaténation de chaînes, l'agrégation de nœuds wildcard et l'affectation implicite de valeurs de champs
La bibliothèque de fonctions d'événements inclut Sum, Count, Avg, Min, Max, Round, les fonctions mathématiques courantes, le découpage de chaînes, la conversion de casse, le trim, le remplacement, HasValue, Exists, Within, Oneof et Choose ; les noms intégrés sont insensibles à la casse, At(source, search) suit l'ordre des arguments XFA y compris le comportement de recherche vide, les fonctions financières suivent l'ordre des paramètres XFA, et les catalogues de dates et de finance sont décrits dans FormCalc Functions
Les mutations de script sont enregistrées à l'intérieur du moteur, validées et rejouées dans la transaction du runtime ; exceptions, interruptions, Unicode invalide, opérations hôtes invalides et budgets épuisés ne publient aucun changement partiel de document
L'exécution générale de scripts reste désactivée par défaut, n'utilise ni navigateur ni processus externe et n'expose aucune API de système de fichiers, de réseau ou d'application ; l'appelant peut fournir JavaScriptEvaluator à la place du pont
Objets de scripts persistants
Les objets script JavaScript à l'intérieur de l'élément variables d'un subform exposent leurs variables et fonctions via le nom du script ; les variables lexicales, l'état des objets et les closures imbriquées restent vivants pendant la session du runtime, avec l'ombrage lexical normal et les directives strict préservés
<variables>
<script name="Helpers" contentType="application/x-javascript"><![CDATA[
let count = 0;
const nextPrivate = (() => { let value = 0; return () => ++value; })();
function next() { return ++count + ":" + nextPrivate(); }
]]></script>
</variables>
Le code d'événement peut appeler Helpers.next() ; les occurrences répétées de subform possèdent des objets de scripts indépendants, et les fonctions résolvent les champs nommés dans le contexte de formulaire vivant courant
Les nœuds de champs et managers capturés suivent une identité de données stable à travers les déplacements et la publication des instances nouvellement créées ; accéder à un nœud supprimé rejette l'événement et annule l'état de son module
Les scripts généraux de calcul et de validation partagent la session et les objets de scripts ; JavaScript prend en charge les valeurs de complétion implicites et le return explicite, tandis que FormCalc renvoie sa dernière expression
Les transactions en échec restaurent l'état lexical et des closures en rejouant le journal virtuel commité, avec le temps et les entrées aléatoires enregistrés ; le replay et l'exécution courante partagent l'échéance de transaction, et les limites par défaut du journal sont de 64 Mio de scripts/résultats et 8192 entrées
L'état des objets de scripts vit dans la session du runtime et est réinitialisé après un rechargement du XDP ; les valeurs standard du document et les surcharges de formulaire continuent de persister via SaveToBytes
Les callbacks JavaScriptEvaluator personnalisés conservent l'enveloppe native existante de calcul/validation ; la session persistante est fournie par le moteur livré
Configurer un transport hôte explicite
Définissez HostTransport et l'option OnHostTransactionCompleted dans TXFAWidgetRuntimeOptions pour exécuter les dialogues hôtes, l'impression, la navigation, la soumission et le transfert de données via des callbacks applicatifs
Le transport hôte XFA explicite fournit xfa.host.messageBox, response, beep, print, gotoURL, submitForm, importData, exportData et les FormCalc Get, Post et Put ; l'application renvoie des résultats typés et contrôle les effets externes
Les retries et le replay de session utilisent les réponses enregistrées, si bien que les callbacks s'exécutent une fois par requête dispatchée ; l'achèvement en échec permet à l'application d'écarter les effets préparés ou de compenser les opérations réversibles
Des HostTransportLimits positifs bornent le nombre de requêtes, le nombre d'arguments et les octets agrégés de requêtes/réponses à travers la transaction du runtime englobant
Exécution de locales et de pictures
Les événements FormCalc généraux et les scripts de calcul et de validation prennent en charge les fonctions de locales et de pictures, notamment Format, Parse, les conversions de date/heure localisées, les unités, l'encodage, les UUID et les nombres en mots anglais
FormCalc Eval dynamique exécute des calculs fournis dynamiquement avec des variables et fonctions isolées, un accès relatif aux champs, une compilation native, des réponses enregistrées et un rollback transactionnel
Les références FormCalc explicites prennent en charge l'affectation write-through, le rebinding, le détachement null, les arguments de fonctions et des handles stables retenus par les objets de scripts à travers les déplacements d'instances répétées
Les chemins SOM vivants prennent en charge les index d'occurrences inférés, absolus et relatifs, les sélecteurs de classe et de descendants, les conteneurs transparents, les prédicats candidats et l'affectation indexée FormCalc directe
Le Data DOM natif expose les datasets via $data et $record, synchronise immédiatement les lectures et écritures liées du formulaire, reflète les instances répétées et préserve les références de données retenues via une publication d'identité native
Le Property DOM natif expose les enfants de propriétés déclarés de valeur, de police, d'UI et autres, synchronise les valeurs de champs typées et persiste les attributs par instance utilisés par la mesure de polices et le styling d'aplatissement pris en charge
Le nœud en exécution hérite de son attribut de locale le plus proche ; les entrées localeSet du document passent outre les symboles et patterns du système, tandis que les noms de locales calculés sont résolus via des requêtes internes bornées et préservés dans le journal de session
Num2Date sans picture utilise désormais le pattern de date par défaut ambiant ; fournissez explicitement YYYY-MM-DD quand une sortie ISO est requise
Modèle hôte avec état
Le modèle hôte XFA fournit les métadonnées d'application, le vrai nombre de pages, la navigation de pages, un titre Unicode, les drapeaux de calcul/validation, un reset à scope et le focus de champ avec un cycle de vie transactionnel enter/exit
TXFAWidgetRuntimeOptions.HostModel configure l'état initial de l'application ; TXFAWidgetRuntime.HostModel renvoie l'état courant, et les événements en échec le restaurent ensemble avec les snapshots de document et d'interaction
Un déplacement de focus sans script enter/exit, édition en attente ou cycle de vie initialisé change le focus sans démarrer de calcul de script ni de traitement d'échéance ; le focus scripté et les commits d'éditions en attente utilisent toujours les budgets transactionnels, la validation et le recalcul
Les clauses picture avancées s'exécutent via les mêmes transactions du runtime, le même journal de retry de locale et le même cycle de vie de calcul, avec un parsing composé borné
Initialiser et maintenir le cycle de vie
Appelez InitializeForm après avoir configuré le runtime et les callbacks pour exécuter les événements initialize dans l'ordre du template, puis le calcul et la validation, les événements form-ready et les événements layout-ready ; l'appel est idempotent après succès
Les nouvelles instances créées par l'initialisation ou les événements ready reçoivent leurs propres événements initialize avant leurs handlers ready ; l'exécution layout-ready ne se répète que lorsque la mise en page change, dans les budgets de réécoulement et de transaction
Après l'initialisation, un dispatch d'événement et un commit d'édition réussis initialisent les instances nouvellement créées et exécutent calculate, validate et layout-ready ; les occurrences existantes conservent leur état d'initialisation à travers les déplacements et le rollback transactionnel
Les échecs de cycle de vie annulent ensemble les octets du document, l'interaction des widgets et la comptabilité d'initialisation, et les callbacks ne publient qu'après le succès de la transaction englobante
Enregistrer le formulaire mis à jour
SaveToBytes renvoie le XDP complet mis à jour du runtime, y compris les jeux de données modifiés et l'état de formulaire standard par instance ; les changements de présence du parseur littéral restent des attributs du template ; utilisez ces octets avec SetXFADocument pour écrire un PDF ou avec HPDFXFAFlatten pour la sortie d'aplatissement mise à jour
La fabrique de document chargé crée un runtime indépendant, si bien que les événements du runtime ne modifient pas automatiquement le graphe d'objets du PDF source
Profil optionnel de présentation d'instances
La présentation d'instances XFA dynamiques ajoute des liaisons répétées explicites, des IDs stables par instance, une présence et un accès indexés, des événements conditionnels bornés, la peinture sur canvas hôte et l'accessibilité via un helper indépendant ; le runtime par défaut et sa grammaire d'événements restreinte restent compatibles
UpdateDocument fournit des actions de document transactionnelles ; des callbacks optionnels de propriété de runtime, de scope de liaison, d'identité, de début de résolution et de validation de document prennent en charge le helper tandis que des valeurs par défaut nil préservent les appelants existants
API d'événements et de formulaires de plus bas niveau
HPDFXFACompileFormCalcEvent compile la syntaxe d'événements ; HPDFXFAExecuteJavaScriptEvent exécute le contexte virtuel décrit par TXFAEventBinding et TXFAEventBindings
TXFAJavaScriptSession fournit une exécution persistante et des points de contrôle bornés pour les appelants de plus bas niveau
Les valeurs TXFAEventAction renvoyées utilisent TXFAEventActionKind ; le replay transactionnel est fourni par le runtime
HPDFXFAResolveFormInstanceNode et HPDFXFAResolveFormInstanceProperty fournissent une recherche bornée standard dans le paquet de formulaires
Limites actuelles
Le runtime est un objet hôte mono-thread ; la GUI et le dessin restent de la responsabilité de l'hôte ou du helper optionnel de présentation
L'état hôte pris en charge, le DOM de formulaire vivant et le cycle de vie ne couvrent pas encore toutes les propriétés d'hôte d'Acrobat, les pictures d'ères alternatives et de chiffres localisés, les requêtes synchrones de réécoulement dynamique, ni tous les opérateurs SOM
Voir FormCalc et JavaScript XFA bornés, DOM de paquets XFA, et traitement interactif de documents