Τεχνικό Άρθρο

Ανταλλαγή Δεδομένων Φορμών FDF, XFDF και XFA στο Delphi

Η losLab PDF Library μεταφέρει δεδομένα φορμών μέσα και έξω από ένα PDF με τρεις διαφορετικούς τρόπους: FDF και XFDF για τις τιμές πεδίων AcroForm, αυτές τις ίδιες δύο μορφές για σχόλια σχολιασμών, και ένα ολόκληρο πακέτο XFA XDP γραμμένο απευθείας στη φόρμα. Τα ExportFormDataToXFDF, ImportFormDataFromXFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF και SetXFAFromString είναι τα σημεία εισόδου, και καθένα από αυτά έχει μια παραλλαγή αρχείου και μια παραλλαγή συμβολοσειράς

Ο λόγος που υπάρχουν τόσες πολλές μέθοδοι είναι ότι τα δεδομένα φορμών PDF δεν είναι ένα ενιαίο πράγμα. Μια συμπληρωμένη AcroForm έχει τιμές πεδίων, μπορεί επίσης να φέρει σχόλια αναθεώρησης, και μια παλαιού τύπου φόρμα XFA ενσωματώνει μια ολόκληρη περιγραφή εφαρμογής XML που δεν έχει καμία σχέση με τα προηγούμενα. Η losLab PDF Library διατηρεί αυτές τις τρεις λειτουργίες σε ξεχωριστά API επίτηδες, επειδή η συγχώνευσή τους θα επέβαλλε λάθος μοντέλο δεδομένων σε τουλάχιστον δύο από αυτές. Η απόφαση για το ποια οικογένεια μεθόδων χρειάζεστε είναι η πρώτη σχεδιαστική επιλογή, και συνήθως καθορίζεται τη στιγμή που γνωρίζετε τι ακριβώς καταναλώνει το σύστημα λήψης στην άλλη πλευρά

Ποια είναι η διαφορά μεταξύ των δεδομένων φορμών FDF, XFDF και XFA;

Τα FDF και XFDF μεταφέρουν τις ίδιες πληροφορίες σε δύο διαφορετικές συντάξεις, και το XFA είναι ένας ξεχωριστός κόσμος. Το FDF είναι ένα έγγραφο σύνταξης PDF σε μικρογραφία (ISO 32000-2 §12.7.8): ένας πίνακας /Fields από λεξικά << /T (name) /V (value) >>, με χαρακτήρες διαφυγής (escaped) ακριβώς με τον τρόπο που διαφεύγουν οι κυριολεκτικές συμβολοσειρές μέσα σε ένα PDF. Το XFDF είναι η μορφή XML των ίδιων δεδομένων (ISO 19444), ένα δέντρο <fields> που το Acrobat και τα περισσότερα back ends φορμών διαβάζουν και γράφουν εγγενώς. Το XFA δεν είναι τίποτα από τα δύο: είναι ένα πρότυπο XML Forms Architecture συν τα δεδομένα του, αποθηκευμένο ως XML Data Package (XDP) στο οποίο αναφέρεται το PDF από το /AcroForm/XFA. Επιλέξτε FDF ή XFDF όταν αλληλεπιδράτε με τιμές πεδίων, και καταφύγετε στο XFA μόνο όταν συντηρείτε ένα έγγραφο που είχε αρχικά δημιουργηθεί ως φόρμα XFA

Μέσα στα FDF και XFDF, η losLab PDF Library τραβάει μια δεύτερη γραμμή: τιμές πεδίων έναντι σχολίων σχολιασμών. Η οικογένεια δεδομένων φορμών (ExportFormDataToFDF, ImportFormDataFromFDF, ExportFormDataToXFDF, ImportFormDataFromXFDF) διαβάζει και γράφει το υποδέντρο /Fields και δεν αγγίζει τίποτα άλλο. Η οικογένεια σχολιασμών (ExportAnnotationsToFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF, ImportAnnotationsFromXFDF) διαβάζει και γράφει το υποδέντρο /Annots, το οποίο είναι αυτό που το Acrobat εννοεί με την Εξαγωγή Σχολίων. Τα δύο δεν αλληλεπικαλύπτονται ποτέ, οπότε η εξαγωγή τιμών πεδίων δεν θα παρασύρει τυχαίες σημειώσεις αναθεώρησης, και η εισαγωγή σχολίων δεν θα διαταράξει τις τιμές που έχει ήδη πληκτρολογήσει ένας χρήστης. Το τι κάνει ένα γραφικό στοιχείο (widget) όταν γίνεται κλικ σε αυτό ή όταν επανυπολογίζεται είναι μια τρίτη λειτουργία, η οποία καλύπτεται στο συνοδευτικό σημείωμα σχετικά με τις διαδραστικές ενέργειες φορμών και JavaScript

Πώς συμπληρώνετε μια φόρμα PDF από FDF ή XFDF στο Delphi;

Φορτώστε το έγγραφο, καλέστε μια μέθοδο εισαγωγής και αποθηκεύστε το. Τα ImportFormDataFromXFDF και ImportFormDataFromFDF αναλύουν το καθένα τα εισερχόμενα δεδομένα φόρμας, αντιστοιχίζουν κάθε καταχώριση σε ένα πεδίο AcroForm με το πλήρως αναγνωρισμένο όνομά του (fully qualified name), ορίζουν την τιμή και επιστρέφουν τον αριθμό των πεδίων που ενημερώθηκαν. Και οι δύο μέθοδοι ενημερώνουν μόνο πεδία που υπάρχουν ήδη στο PDF προορισμού. Καμία δεν δημιουργεί πεδίο για ένα όνομα που δεν ορίζει η φόρμα, γεγονός που εμποδίζει ένα ξένο ή κακόβουλο αρχείο δεδομένων να επεκτείνει σιωπηλά τη φόρμα σας

var
  Lib: TPDFlib;
  FieldsSet: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('application-blank.pdf', '');
    // XFDF produced by a case-management system, already UTF-8 on disk
    FieldsSet := Lib.ImportFormDataFromXFDF('applicant-1042.xfdf');
    if FieldsSet > 0 then
      Lib.SaveToFile('application-filled.pdf');
  finally
    Lib.Free;
  end;
end;

Δύο λεπτομέρειες καθορίζουν εάν τα δεδομένα θα επιβιώσουν κατά τη μεταφορά. Η πρώτη είναι η διαφυγή (escaping): το FDF αποθηκεύει τιμές ως κυριολεκτικές συμβολοσειρές PDF, οπότε μια τιμή που περιέχει μια παρένθεση, μια κάθετο ή ένα μη εκτυπώσιμο byte φτάνει τυλιγμένη στην οκταδική διαφυγή που ορίζεται στο ISO 32000-2 §7.3.4.2, και η losLab PDF Library αντιστρέφει αυτή τη διαφυγή κατά την εισαγωγή, ώστε οι παρενθέσεις και οι κάθετοι να επιστρέφουν ως οι κυριολεκτικοί χαρακτήρες που πληκτρολόγησε ο χρήστης. Η δεύτερη είναι η κωδικοποίηση: οι μέθοδοι που βασίζονται σε αρχεία γράφουν και διαβάζουν XFDF και FDF ως UTF-8, το οποίο είναι αυτό που υπόσχεται η δήλωση XFDF και αυτό που χρειάζεται οποιαδήποτε τιμή πεδίου μη-ASCII (ένα όνομα με τόνους, ένα σύμβολο νομίσματος, μια διεύθυνση CJK) προκειμένου να ολοκληρήσει τη διαδρομή χωρίς αλλοίωση. Εάν δημιουργείτε το XFDF μόνοι σας, δηλώστε UTF-8 και γράψτε UTF-8, και η εισαγωγή θα συμφωνήσει μαζί σας

Ιεραρχικά πεδία, επιλογές πολλαπλών τιμών και εμπλουτισμένο κείμενο

Τα ιεραρχικά ονόματα πεδίων είναι το πρώτο σημείο στο οποίο αποτυγχάνει ένας απλοϊκός εξαγωγέας. Το AcroForm απευθύνεται σε ένα φωλιασμένο πεδίο με έναν πλήρη τίτλο με τελείες, όπως το Applicant.FullName, αλλά το XFDF δεν τοποθετεί αυτή τη συμβολοσειρά σε ένα μόνο χαρακτηριστικό ονόματος. Το ISO 19444 τη φωλιάζει ως <field name="Applicant"><field name="FullName">. Η losLab PDF Library χωρίζει τον τίτλο με τελείες σε φωλιασμένα στοιχεία <field> κατά την εξαγωγή και επανασυναρμολογεί τα φωλιασμένα στοιχεία πίσω στον πλήρη τίτλο κατά την εισαγωγή, ώστε οι δύο κατευθύνσεις να παραμένουν συμμετρικές. Κατά την εξαγωγή παραλείπει επίσης μη τερματικά γονικά πεδία, επειδή ένας γονικός κόμβος σε ένα AcroForm φέρει μόνο το επίπεδο ονομασίας και δεν έχει δική του τιμή. Η εκπομπή του θα παρήγαγε μια κενή τιμή <value></value> που δεν ταιριάζει με την πραγματική φόρμα. Τα πλαίσια λίστας πολλαπλής επιλογής (multi-select list boxes) είναι η δεύτερη παγίδα: ένα πεδίο επιλογής μπορεί να κρατήσει πολλές επιλεγμένες τιμές ταυτόχρονα, τις οποίες το XFDF εκφράζει ως επαναλαμβανόμενα στοιχεία <value> και το FDF ως πίνακα /V, και η losLab PDF Library χωρίζει ένα πεδίο σε πολλαπλές τιμές μόνο όταν πρόκειται πραγματικά για επιλογή πολλαπλής επιλογής, οπότε ένα συνηθισμένο πεδίο κειμένου πολλαπλών γραμμών διατηρεί τις αλλαγές γραμμή του αντί να διασπαστεί σε ψευδείς τιμές

Το εμπλουτισμένο κείμενο (rich text) είναι το τρίτο, και αυτό που οι περισσότεροι κάνουν λάθος. Ένα πεδίο με μορφοποίηση αποθηκεύει τη σήμανσή του στην καταχώριση RV ως υποδέντρο XHTML, και το XFDF το μεταφέρει στο <value-richtext>. Η losLab PDF Library γράφει αυτό το υποδέντρο ως ζωντανό κομμάτι XML αντί να χρησιμοποιεί χαρακτήρες διαφυγής, έτσι ώστε ένα εργαλείο στη συνέχεια να διαβάζει πραγματικό εμπλουτισμένο κείμενο αντί για μια συμβολοσειρά ορατών ετικετών (tags). Κατά την εισαγωγή, διατηρεί το ακατέργαστο υποδέντρο RV και εξακολουθεί να εφαρμόζει την απλή τιμή <value> στο V. Όπου ένα πεδίο προσφέρει και τα δύο, η απλή τιμή κερδίζει για το V και το εμπλουτισμένο κείμενο συνυπάρχει στο RV, ο οποίος είναι ο κανόνας διαλειτουργικότητας που εμποδίζει μια εισαγωγή εμπλουτισμένου κειμένου να ξαναγράψει σιωπηλά μια τιμή που έχει ήδη ορίσει ένα άλλο εργαλείο. Όταν το εμπλουτισμένο κείμενό σας υπάρχει για να μεταφέρει τη δομή του εγγράφου σε υποστηρικτική τεχνολογία, αντιμετωπίστε το με τον ίδιο τρόπο που θα αντιμετωπίζατε τη σειρά ανάγνωσης που συζητήθηκε στο σημείωμα για το tagged PDF και τη δομή προσβασιμότητας

var
  Lib: TPDFlib;
const
  XFDF =
    '<?xml version="1.0" encoding="UTF-8"?>' +
    '<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">' +
    '<fields>' +
    '  <field name="Applicant">' +
    '    <field name="FullName"><value>Alice Example</value></field>' +
    '  </field>' +
    '  <field name="Skills">' +
    '    <value>Delphi</value><value>PDF</value>' +
    '  </field>' +
    '  <field name="Notes">' +
    '    <value>See attachment</value>' +
    '    <value-richtext><body><p>See <b>attachment</b></p></body></value-richtext>' +
    '  </field>' +
    '</fields></xfdf>';
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('intake.pdf', '');
    // Το Applicant.FullName επανασυναρμολογείται, το Skills συμπληρώνει τους πίνακες /V + /I, το Notes λαμβάνει V από το  και RV από το 
    Lib.ImportFormDataFromXFDFString(XFDF);
    Lib.SaveToFile('intake-filled.pdf');
  finally
    Lib.Free;
  end;
end;

Πώς πραγματοποιείτε κυκλική διαδρομή (round-trip) σχολίων σχολιασμών ως FDF ή XFDF;

Τα σχόλια ταξιδεύουν στην οικογένεια σχολιασμών, και το υποστηριζόμενο υποσύνολο είναι σκόπιμα μικρό. Τα ExportAnnotationsToXFDF και ImportAnnotationsFromXFDF, μαζί με τα αντίστοιχα FDF, μεταφέρουν σήμανση τύπου κειμένου με τα πεδία που ολοκληρώνουν την κυκλική διαδρομή καθαρά: τον υποτύπο σχολιασμού, το ορθογώνιό του, τον δείκτη σελίδας με βάση το 0, τον συγγραφέα στο T, το θέμα στο Subj, τα απλά περιεχόμενα (Contents) και το χρώμα, το οποίο αντιστοιχίζει την τριάδα DeviceRGB του PDF /C από και προς ένα χαρακτηριστικό XFDF #RRGGBB. Η εισαγωγή αποδέχεται μόνο γνωστά ονόματα στοιχείων και παραλείπει οποιαδήποτε καταχώριση της οποίας η σελίδα είναι εκτός εύρους ή της οποίας το ορθογώνιο λείπει, οπότε ένα χειροκίνητα επεξεργασμένο ή ξένο XFDF δεν μπορεί να εισαγάγει έναν κακοσχηματισμένο σχολιασμό στο έγγραφο. Αυτό που δεν μεταφέρει ακόμα το υποσύνολο αξίζει να αναφερθεί ξεκάθαρα: τα περιεχόμενα εμπλουτισμένου κειμένου (RC), τα αναδυόμενα παράθυρα (popups), τα quadpoints, οι λίστες μελανιού (ink lists), οι κορυφές (vertices) και οι έτοιμες ροές εμφάνισης (appearance streams) είναι εκτός πεδίου εφαρμογής προς το παρόν, επομένως η γεωμετρία επισήμανσης και οι προσαρμοσμένες εμφανίσεις σφραγίδων δεν θα επιβίνουν από αυτήν τη διαδρομή. Για να επιβεβαιώσετε τι πράγματι προσγειώθηκε, διατρέξτε τους σχολιασμούς σελίδας ή το ευρύτερο δέντρο στοιχείων που καλύπτεται στο αναζήτηση κειμένου και απαρίθμηση στοιχείων σελίδας

var
  Src, Dest: TPDFlib;
  Xfdf: WideString;
begin
  Src := TPDFlib.Create;
  Dest := TPDFlib.Create;
  try
    Src.LoadFromFile('reviewed.pdf', '');
    Xfdf := Src.ExportAnnotationsToXFDFString;   // υποσύνολο  μόνο

    Dest.LoadFromFile('clean-copy.pdf', '');     // οι σελίδες πρέπει να ευθυγραμμίζονται κατά δείκτη
    Dest.ImportAnnotationsFromXFDFString(Xfdf);
    Dest.SaveToFile('clean-copy-commented.pdf');
  finally
    Dest.Free;
    Src.Free;
  end;
end;

Εγγραφή πλήρους πακέτου XFA XDP με το SetXFAFromString

Το XFA είναι η εξαίρεση, και το SetXFAFromString είναι ο τρόπος με τον οποίο γράφετε ολόκληρο το πακέτο ταυτόχρονα. Η μέθοδος λαμβάνει ένα πλήρες XML Data Package, το έγγραφο <xdp:xdp> με τα πακέτα προτύπων (template) και συνόλων δεδομένων (datasets), και το αποθηκεύει ως τη ροή (stream) που αναφέρεται από το /AcroForm/XFA. Η losLab PDF Library δημιουργεί το κοντέινερ AcroForm κατ' απαίτηση όταν το έγγραφο δεν διαθέτει κανένα, οπότε δεν χρειάζεται να προσθέσετε ένα πρόχειρο πεδίο μόνο και μόνο για να δώσετε στο XFA ένα σημείο να κρεμαστεί, και απορρίπτει οποιαδήποτε αναλυμένη κατάσταση XFA που κρατούσε, ώστε μια μεταγενέστερη ανάγνωση να αντικατοπτρίζει το πακέτο που μόλις γράψατε και όχι μια παλιά προσωρινή μνήμη (cache). Επειδή ένα πακέτο XFA είναι XML και όχι απαραίτητα UTF-8, η βιβλιοθήκη ανιχνεύει ένα byte-order mark UTF-16 και αποκωδικοποιεί σωστά το πακέτο πριν από την ανάλυση, κάτι που έχει σημασία για πακέτα που παράγονται από εργαλεία που έχουν ως προεπιλογή το UTF-16

Μόλις το πακέτο τοποθετηθεί, τα GetXFAFormFieldValue και SetXFAFormFieldValue απευθύνονται σε μεμονωμένα πεδία μέσω της διαδρομής Scripting Object Model (SOM) που διαθέτουν. Η losLab PDF Library αποδέχεται τις τυπικές ρίζες SOM καθώς και απλές σχετικές διαδρομές, οπότε τα form1.FullName, $data.form1.FullName και xfa.datasets.data.form1.FullName επιλύονται όλα στον ίδιο κόμβο δεδομένων, και η πλευρά του template αποδέχεται τα $template και xfa.template με τον ίδιο τρόπο. Αυτή η ανοχή έχει σημασία όταν οι διαδρομές SOM παράγονται από άλλο σύστημα που εκπέμπει πάντα την πλήρως αναγνωρισμένη μορφή

var
  Lib: TPDFlib;
const
  Xdp =
    '<?xml version="1.0" encoding="UTF-8"?>' +
    '<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">' +
    '  <template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">' +
    '    <subform name="form1">' +
    '      <field name="FullName"><ui><textEdit/></ui></field>' +
    '    </subform>' +
    '  </template>' +
    '  <xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">' +
    '    <xfa:data><form1><FullName>Alice Example</FullName></form1></xfa:data>' +
    '  </xfa:datasets>' +
    '</xdp:xdp>';
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetXFAFromString(Xdp, 0);      // δημιουργεί το κοντέινερ AcroForm εάν δεν υπάρχει κανένα
    // read back through the data DOM with an SOM path
    if Lib.GetXFAFormFieldValue('form1.FullName') = 'Alice Example' then
      Lib.SetXFAFormFieldValue('form1.FullName', 'Bob Example');
    Lib.SaveToFile('xfa-packet.pdf');
  finally
    Lib.Free;
  end;
end;

Η ειλικρινής σύνοψη είναι ότι οι τιμές πεδίων πραγματοποιούν πλήρη κυκλική διαδρομή μέσω FDF και XFDF, συμπεριλαμβανομένης της ιεραρχίας, της πολλαπλής επιλογής και του εμπλουτισμένου κειμένου. Τα σχόλια σχολιασμών πραγματοποιούν κυκλική διαδρομή ως καθορισμένο υποσύνολο σήμανσης κειμένου με σαφή κενά, και το XFA γράφεται και διαβάζεται ως ολόκληρο πακέτο με πρόσβαση SOM ανά πεδίο από πάνω. Αντιστοιχίστε τη μορφή στον καταναλωτή, δηλώστε UTF-8 για οτιδήποτε κατασκευάζετε με το χέρι, και θυμηθείτε ότι οι μέθοδοι εισαγωγής αγγίζουν μόνο πεδία και σχολιασμούς που ταιριάζουν ήδη στο έγγραφο. Οι μέθοδοι δεδομένων φορμών, σχολιασμών και XFA που περιγράφονται εδώ αποτελούν μέρος της losLab PDF Library για Delphi και C++Builder, της οποίας η αναφορά φέρει την πλήρη λίστα παραμέτρων για κάθε σημείο εισόδου εξαγωγής και εισαγωγής