Μια ενέργεια AcroForm είναι ένα λεξικό συνδεδεμένο με ένα widget που λέει στο πρόγραμμα προβολής τι να κάνει όταν συμβεί κάτι σε αυτό το widget. Κάντε κλικ σε ένα κουμπί και το πρόγραμμα προβολής διαβάζει το λεξικό ενεργειών του: μια ενέργεια URI ανοίγει μια διεύθυνση ιστού, μια ενέργεια JavaScript εκτελεί σενάριο, μια ενέργεια SubmitForm αποστέλλει τις συλλεγμένες τιμές πεδίων σε ένα endpoint, μια ενέργεια ResetForm τις επαναφέρει στις προεπιλογές τους. Η ενέργεια είναι δεδομένα, όχι συμπεριφορά ενσωματωμένη μέσα στο αρχείο. Το ISO 32000-1 §12.6 ορίζει τη μορφή του λεξικού· το πρόγραμμα προβολής παρέχει τη μηχανή που το ερμηνεύει. Αυτός ο διαχωρισμός έχει σημασία επειδή μια ενέργεια γραμμένη τέλεια μέσα στο PDF εξακολουθεί να μην κάνει τίποτα αν ο αναγνώστης στην άλλη άκρη δεν διαθέτει μηχανή γι' αυτήν, και μεγάλο μέρος της ταλαιπωρίας με τα AcroForm εντοπίζεται σε αυτό το κενό και όχι σε ένα κακοδιατυπωμένο πεδίο
Το HotPDF γράφει αυτά τα λεξικά απευθείας από το Delphi και το C++Builder, δίπλα στα widgets πεδίων από τα οποία εξαρτώνται. Δύο δομές εμπλέκονται σε κάθε διαδραστική φόρμα: το widget που βλέπει ο χρήστης στη σελίδα, και ο μηχανισμός πεδίου συν ενεργειών από κάτω που μεταφέρει τα δεδομένα και τη συνδεσμολογία. Επεξεργάζονται ανεξάρτητα, και οποιαδήποτε από τις δύο μπορεί να είναι λανθασμένη ενώ η άλλη φαίνεται εντάξει. Οι παρακάτω ενότητες εξετάζουν την ονοματοδοσία πεδίων, τις ίδιες τις ενέργειες κουμπιών, το JavaScript σε επίπεδο πεδίου, και την κατηγορία ελαττωμάτων που επιβιώνει από έναν οπτικό έλεγχο επειδή ζει εξολοκλήρου μέσα στη δεύτερη δομή
Τα ονόματα πεδίων είναι κλειδιά δρομολόγησης, όχι λεζάντες
Κάθε πεδίο AcroForm φέρει ένα πλήρως εξειδικευμένο όνομα (fully qualified name). Το ISO 32000-1 §12.7.3 καθιστά αυτό το όνομα, και όχι την ορατή λεζάντα, το κλειδί κάτω από το οποίο ταξιδεύει η τιμή του πεδίου όταν η φόρμα εξάγεται ή υποβάλλεται. Οι προγραμματιστές που έρχονται από τον σχεδιασμό VCL τείνουν να αντιμετωπίζουν το όνομα ενός στοιχείου ελέγχου ως ιδιωτικό αναγνωριστικό κώδικα, αλλά εδώ δεν είναι κάτι τέτοιο. Είναι η μορφή μετάδοσης δεδομένων (wire format)
Το πρώτο πράγμα που προκύπτει είναι ότι δύο πεδία με το ίδιο πλήρως εξειδικευμένο όνομα δεν είναι δύο πεδία. Το PDF τα αντιμετωπίζει ως δύο widget annotations ενός πεδίου, που μοιράζονται μία τιμή, οπότε η πληκτρολόγηση στο ένα ενημερώνει το άλλο επιτόπου. Αυτό είναι ακριβώς αυτό που θέλετε όταν το όνομα ενός πελάτη πρέπει να επαναλαμβάνεται σε κάθε σελίδα ενός συμβολαίου. Είναι σφάλμα όταν ένας βρόχος δημιουργίας επαναχρησιμοποιεί κατά λάθος το 'Field1' σε τρεις σελίδες. Καμία οπτική επιθεώρηση δεν εντοπίζει τη δεύτερη περίπτωση. Κάθε σελίδα εξακολουθεί να σχεδιάζει το δικό της πλαίσιο, και η σύνδεση αναδύεται μόνο μόλις κάποιος αρχίσει να πληκτρολογεί
Ονόματα με τελείες όπως το applicant.email χτίζουν μια ιεραρχία. Ο γονικός κόμβος applicant ομαδοποιεί τα παιδιά του, κάτι που επιτρέπει σε μια ενέργεια reset ή submit να στοχεύει μόνο ένα τμήμα της φόρμας. Η ονοματοδοσία των πεδίων με αυτόν τον τρόπο από την αρχή δεν κοστίζει τίποτα, και αποσβένεται την πρώτη φορά που το σύστημα παραλαβής ζητά μόνο το τμήμα applicant
Τα radio button έχουν δικό τους κανόνα. Τα κουμπιά που πρέπει να εναλλάσσονται μαζί πρέπει να μοιράζονται ένα όνομα ομάδας. Στο HotPDF, οι κλήσεις AddRadioButton που περνούν το ίδιο όνομα ομάδας προσαρτούν τα widgets τους σε ένα κοινό γονικό πεδίο, και η τιμή εξαγωγής κάθε κουμπιού ('basic' ή 'full') προσδιορίζει την επιλεγμένη επιλογή. Δώστε σε κάθε κουμπί ξεχωριστό όνομα και θα καταλήξετε με μια σειρά ανεξάρτητων διακοπτών on/off αντί για μία αμοιβαία αποκλειόμενη ομάδα, κάτι που αποδίδεται πανομοιότυπα αλλά συμπεριφέρεται λανθασμένα
Δημιουργία του συνόλου πεδίων σελίδα προς σελίδα
Το HotPDF τοποθετεί πεδία μέσω μεθόδων THPDFPage, οπότε κάθε πεδίο ανήκει στο αντικείμενο σελίδας που το δημιούργησε. Η παγίδα ακολουθίας που πρέπει να προσέξετε είναι το AddPage. Ανακατευθύνει το CurrentPage προς τη νέα σελίδα τη στιγμή που επιστρέφει, οπότε κάθε κλήση πεδίου μετά από αυτό καταλήγει στη νέα σελίδα, ακόμη και όταν το πεδίο ανήκε λογικά στη σελίδα που μόλις αφήσατε. Ολοκληρώστε κάθε σελίδα, περιεχόμενο σχεδίασης και πεδία μαζί, πριν καλέσετε το AddPage
procedure BuildClaimForm(Pdf: THotPDF);
begin
// Σελίδα 1: τμήμα applicant
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 δείχνει τώρα στη σελίδα 2
Pdf.CurrentPage.AddListBox('riders', 'None',
['None', 'Flood', 'Earthquake'], Rect(50, 500, 200, 600));
end;
Οι συντεταγμένες χρησιμοποιούν τη σύμβαση του PDF, με την αρχή στην κάτω αριστερή γωνία της σελίδας. Αυτή είναι η ίδια αρχή που χρησιμοποιεί το TextOut για σχεδιασμένο κείμενο, οπότε το Rect(50, 100, 200, 120) βρίσκεται κοντά στο κάτω μέρος μιας σελίδας Letter, όχι στην κορυφή. Το VCL τοποθετεί το Y στην κορυφή και το αυξάνει προς τα κάτω, οπότε ένας πίνακας διάταξης που μεταφέρεται αυτούσιος βγαίνει κατακόρυφα ανεστραμμένος, με κάθε πεδίο γυρισμένο στο λάθος άκρο της σελίδας. Κάντε τη μετατροπή μία φορά σε έναν κοινόχρηστο βοηθό αντί σε κάθε σημείο κλήσης, και μία μόνο διόρθωση επιδιορθώνει ολόκληρη τη φόρμα
Σύνδεση κουμπιών με ενέργειες URI, JavaScript και submit
Ένα push button είναι αδρανές μέχρι να επισυναφθεί μια ενέργεια σε αυτό. Το HotPDF εκθέτει τους τύπους ενεργειών από το ISO 32000-1 §12.6.4 μέσω της απαρίθμησης THPDFButtonAction (baURI, baJavaScript, baSubmitURL, baResetForm, baHide, baShow, baNamed), και παρέχει δύο μεθόδους που δημιουργούν το κουμπί και συνδέουν την ενέργειά του σε μία κλήση
// Άνοιγμα σελίδας βοήθειας στο πρόγραμμα περιήγησης του συστήματος
Pdf.CurrentPage.AddPushButtonWithAction('btnHelp', 'Help',
'https://www.example.com/claims-help', Rect(320, 700, 420, 730), baURI);
// Εκτέλεση JavaScript από την πλευρά του προγράμματος προβολής
Pdf.CurrentPage.AddPushButtonWithAction('btnRecalc', 'Recalculate',
'app.alert("Totals updated.");', Rect(320, 660, 420, 690), baJavaScript);
// Υποβολή ως XFDF και διατήρηση κενών πεδίων στο payload
Pdf.CurrentPage.AddPushButtonWithSubmitAction('btnSubmit', 'Submit claim',
'https://api.example.com/claims', Rect(320, 620, 420, 650),
[sffXFDF, sffIncludeNoValueFields]);
Οι σημαίες submit αξίζουν περισσότερη σκέψη από όση συνήθως παίρνουν. Το AddPushButtonWithSubmitAction δέχεται ένα σύνολο THPDFSubmitFormFlags, και ένα κενό σύνολο παράγει ένα απλό url-encoded post, μορφή που πολλά δοκιμαστικά endpoints αποδέχονται αλλά πολλά endpoints παραγωγής απορρίπτουν. Η προσθήκη του sffXFDF μεταβάλλει το payload σε XFDF. Το sffGetMethod αλλάζει το ρήμα HTTP. Το sffIncludeNoValueFields διατηρεί τα κενά πεδία στο payload αντί να τα αποκλείει σιωπηλά, κάτι που έχει σημασία τη στιγμή που ο παραλήπτης διακρίνει το "απόν" από το "κενό". Το σύνολο σημαιών αποτελεί μέρος του συμβολαίου διεπαφής σας με το endpoint παραλαβής, οπότε συμφωνήστε το με την ομάδα που αναλύει την υποβολή, όχι μετά την πρώτη απορριφθείσα παρτίδα
JavaScript σε επίπεδο πεδίου: keystroke, format, validate
Τα κλικ κουμπιών δεν είναι το μόνο σημείο όπου ζουν οι ενέργειες. Το HotPDF επισυνάπτει επίσης JavaScript στα συμβάντα ανά πεδίο που πυροδοτούν τα προγράμματα προβολής με δυνατότητα σεναρίων ενώ ο χρήστης εισάγει δεδομένα. Υπάρχουν τρεις ενεργοποιητές, και πυροδοτούνται σε διαφορετικά σημεία του κύκλου ζωής της εισαγωγής. Μια ενέργεια keystroke εκτελείται καθώς φτάνει κάθε χαρακτήρας, και ξανά κατά το commit. Μια ενέργεια format ξαναγράφει την εμφανιζόμενη τιμή αφού μια αλλαγή έχει γίνει commit, αποκλειστικά για παρουσίαση. Μια ενέργεια validate έχει τον τελευταίο λόγο, αποδεχόμενη ή απορρίπτοντας την τιμή που έγινε commit πριν αυτή γίνει η τιμή του πεδίου
// Απόρριψη τιμών commit που δεν είναι πιθανές διευθύνσεις email
Pdf.AttachFieldKeyStrokeAction('applicant.email',
'if (event.willCommit && !/^[\w.-]+@[\w.-]+\.\w+$/.test(event.value)) event.rc = false;');
// Εμφάνιση αριθμών τηλεφώνου ΗΠΑ ως (NNN) NNN-NNNN
Pdf.AttachFieldFormatAction('applicant.phone',
'event.value = event.value.replace(/(\d{3})(\d{3})(\d{4})/, "($1) $2-$3");');
// Απόρριψη αιτούντων κάτω των 18 ετών κατά το commit
Pdf.AttachFieldValidateAction('applicant.age',
'if (parseInt(event.value) < 18) event.rc = false;');
Ο ορισμός event.rc = false μέσα σε ένα σενάριο keystroke ή validate λέει στο πρόγραμμα προβολής να απορρίψει την εισαγωγή. Το πρόβλημα είναι ότι τίποτα από αυτά δεν εκτελείται εκτός αν το πρόγραμμα προβολής διαθέτει μηχανή JavaScript. Το Acrobat και μερικά προϊόντα desktop διαθέτουν. Οι περισσότεροι αναγνώστες κινητών, οι renderers ενσωματωμένοι σε πρόγραμμα περιήγησης και οι αγωγοί εκτύπωσης δεν διαθέτουν, και απορρίπτουν τα σενάρια σιωπηλά χωρίς παράπονο. Έτσι τα σενάρια πεδίου βελτιώνουν την ποιότητα δεδομένων μόνο για το υποσύνολο χρηστών των οποίων ο αναγνώστης τα εκτελεί, και αυτό είναι το μόνο που κάνουν. Δεν αποτελούν όριο ασφαλείας. Κάθε υποβληθείσα τιμή πρέπει και πάλι να επικυρωθεί στον server μόλις φτάσει, επειδή δεν μπορείτε να υποθέσετε ότι ο client έλεγξε οτιδήποτε
Ελαττώματα που περνούν τον οπτικό έλεγχο
Τα πιο δύσκολα ελαττώματα AcroForm να εντοπιστούν είναι αυτά που ζουν στη δομή δεδομένων και όχι στην απόδοση, επειδή το άνοιγμα του αρχείου και η παρατήρησή του δεν σας λέει τίποτα. Τέσσερα εμφανίζονται αρκετά συχνά ώστε να αξίζει να αναφερθούν, και το καθένα διαθέτει ένα μηχανικό τεστ που το εντοπίζει πριν την κυκλοφορία
- Απόκλιση τιμής εξαγωγής. Ένα checkbox που δημιουργήθηκε ως
AddCheckBox('consent', 'Yes', ...)αποστέλλειYes. Ένας παραλήπτης που ταιριάζει μεYαπορρίπτει κάθε υποβολή ενώ η σελίδα φαίνεται τέλεια. Συμπληρώστε τη φόρμα, εξάγετέ την ως XFDF από το Acrobat, και συγκρίνετε τις τιμές με το σχήμα που πράγματι αναμένει ο παραλήπτης - Ακούσιος καθρεφτισμός τιμών. Δύο πεδία που μοιράζονται το ίδιο πλήρως εξειδικευμένο όνομα συγχωνεύονται σε ένα. Το σύμπτωμα εμφανίζεται κατά τη στιγμή εισαγωγής δεδομένων και ποτέ κατά τη στιγμή δημιουργίας, οπότε το τεστ είναι να πληκτρολογήσετε μέσα στη φόρμα, όχι να την αποδώσετε και να ελέγξετε οπτικά το αποτέλεσμα
- Τιμές combo εκτός της λίστας επιλογών. Όταν η τρέχουσα τιμή που περνάτε στο
AddComboBoxδεν είναι μία από τις παρατιθέμενες επιλογές, τα προγράμματα προβολής διαφωνούν για το αν θα την εμφανίσουν, θα την αδειάσουν ή θα τη σημαιοφορήσουν. Κρατήστε την προεπιλογή μέσα στη λίστα και η διαφωνία εξαφανίζεται - Πεδία που παραμένουν επεξεργάσιμα αφού η ροή εργασίας έκλεισε. Το HotPDF δεν διαθέτει κλήση ισοπέδωσης εμφάνισης (appearance-flattening) για πεδία AcroForm. Ο υποστηριζόμενος τρόπος να παγώσετε μια ολοκληρωμένη φόρμα είναι να δημιουργήσετε τα πεδία με τη σημαία
ffReadOnly, η οποία κρατά την τιμή ορατή μέσα από τη δική της ροή εμφάνισης του πεδίου ενώ αρνείται επεξεργασίες. Το πεδίο παραμένει ζωντανό αντικείμενο φόρμας, κάτι που αναμένουν να βρουν τα εργαλεία συναρμολόγησης και υπογραφής παρακάτω στη ροή
Μία συμπεριφορά από την πλευρά του προγράμματος προβολής αξίζει μια σημείωση οπισθοδρόμησης παρόλο που καμία αλλαγή κώδικα δεν την αντιμετωπίζει. Οι εταιρικές αναπτύξεις Acrobat μπορούν να απενεργοποιήσουν το JavaScript ή να περιορίσουν τους στόχους submit μέσω πολιτικής, οπότε μια ενέργεια που λειτουργούσε σε κάθε build ανάπτυξης μπορεί να παραμείνει νεκρή σε έναν κλειδωμένο υπολογιστή πελάτη. Σχεδιάστε μια ορατή εναλλακτική λύση για την περίπτωση όπου το κουμπί δεν κάνει τίποτα, ακόμη κι αν αυτή η εναλλακτική είναι μόνο μια τυπωμένη οδηγία που λέει στον χρήστη τι να κάνει αντ' αυτού
Πού συνδέεται η εργασία φόρμας με το υπόλοιπο έγγραφο
Ένα πεδίο υπογραφής είναι από μόνο του ένας τύπος πεδίου AcroForm. Μια φόρμα που θα πιστοποιηθεί ή θα αντι-υπογραφεί αργότερα είναι προτιμότερο να δεσμεύει αυτό το πεδίο κατά τη δημιουργία παρά να το προσθέτει αργότερα, και οι λόγοι σε επίπεδο byte βρίσκονται στο συνοδευτικό άρθρο για ψηφιακές υπογραφές και υπογραφή PAdES με το HotPDF. Οι είσοδοι που φτάνουν ως πακέτα XFA αντί για εγγενές AcroForm αποτελούν διαφορετική περίπτωση: η ισοπέδωση XFA σε πεδία AcroForm είναι δική της ροή εργασίας με δικό της μοντέλο απώλειας, επειδή οι δύο τεχνολογίες φορμών δεν μπορούν να συνυπάρξουν σε ένα αρχείο
Οι μέθοδοι πεδίων, ενεργειών και ενεργοποιητών που παρουσιάζονται εδώ αποτελούν μέρος του τυπικού API του HotPDF Delphi Component για Delphi και C++Builder· η σελίδα προϊόντος συνδέει την πλήρη αναφορά, συμπεριλαμβανομένων των υπερφορτώσεων σημαιών πεδίου και της πλήρους απαρίθμησης σημαιών submit