Articolo tecnico

Esportazione e importazione XFDF in Delphi con PDFium Component

Il Componente PDFium per Delphi e Lazarus scambia i dati dei moduli e le annotazioni tramite XFDF, il formato di interscambio XML definito dallo standard ISO 19444-1. TPdf.ExportXFDF serializza ogni valore di campo del modulo e ogni annotazione supportata nel documento caricato in un file XFDF o in un TStream; TPdf.ImportXFDF legge a ritroso un documento XFDF, ricreando le annotazioni sulle relative pagine e applicando i valori dei campi. Questo singolo round-trip copre due flussi di lavoro di cui ogni applicazione documentale ha infine bisogno: un revisore evidenzia un PDF in Acrobat e invia i commenti da unire, oppure una regola di conformità stabilisce che i dati dei moduli debbano risiedere in un file separato, confrontabile e verificabile, anziché incorporati nel PDF stesso

PDFium supporta l'importazione e l'esportazione XFDF?

La libreria PDFium nativa non supporta queste operazioni. Le API C native non contengono alcuna funzione XFDF o FDF — non vi è nulla negli header pubblici che consenta di leggere o scrivere tali formati, per cui nessuna attività di wrapping può colmare questa lacuna. Il Componente PDFium implementa quindi l'intero motore XFDF in Pascal, all'interno di un'unità dedicata che compila e analizza l'XML direttamente sopra il modello dati esistente di annotazioni e campi modulo di TPdf. Il modulo di scrittura genera l'XML manualmente e il modulo di lettura è un parser a stati scritto a mano, per cui la funzionalità non introduce alcuna dipendenza da Delphi XMLDoc o dalle unità DOM di FPC e si comporta in modo identico su Delphi e Lazarus

Questa scelta progettuale è importante nella valutazione delle alternative. Se hai richiamato finora le funzioni grezze FPDF_* da Pascal, lo standard XFDF rappresenta un ostacolo insormontabile: la funzionalità semplicemente non è presente nella DLL. Il componente supera questo limite trattando XFDF come un puro problema di serializzazione — raccoglie i valori dei campi e i record delle annotazioni che il wrapper sa già leggere, li esporta come XML standard e inverte il processo in fase di importazione

Cosa contiene un file XFDF?

Lo standard ISO 19444-1 definisce XFDF como la rappresentazione XML di FDF, e la sua struttura si divide in due blocchi di primo livello. L'elemento <fields> contiene i nomi gerarchici dei campi modulo e i relativi valori — tutto ciò che l'utente ha digitato, selezionato o spuntato in un AcroForm. L'elemento <annots> contiene invece le annotazioni: TPdf.ExportXFDF esporta 18 sottotipi — text, highlight, underline, strikeout, squiggly, line, circle, square, caret, polygon, polyline, stamp, ink, freetext, fileattachment, sound, link, e redact. Le annotazioni widget sono volutamente escluse da <annots> poiché i loro dati viaggiano in <fields>, e i sottotipi interni del visualizzatore come Popup non fanno parte del vocabolario XFDF

Per garantire la fedeltà del round-trip, il record TPdfAnnotation è stato esteso con i metadati richiesti dallo standard XFDF: Name (l'identificatore univoco NM), Subject, ModificationDate, CreationDate, Icon, Opacity, punti finali delle linee, vertici di poligoni e polilinee, e tracciati dei tratti a inchiostro. Ciascun nuovo campo si associa a una sentinella booleana Has*, in modo che il codice che definiva annotazioni rispetto alle versioni precedenti continui a compilare e a produrre i medesimi dizionari — un campo non impostato non viene mai scritto. Se crei già marcatori a livello di programmazione, lo stesso record che utilizzi per le annotazioni di marcatori di testo con punti quad (quad points) ora include tutto ciò di cui XFDF ha bisogno

Come si esportano i dati dei moduli e le annotazioni in XFDF?

Una chiamata esegue l'intero documento. Carica il PDF con la compilazione dei moduli abilitata, richiama ExportXFDF, e il componente esamina ciascuna pagina, raccoglie i valori dei campi e le annotazioni, e scrive il codice XML. Il valore restituito rappresenta il numero di byte UTF-8 scritti, che fornisce un controllo di integrità immediato nei registri

var
  Pdf: TPdf;
  Bytes: Integer;
begin
  Pdf := TPdf.Create(Self);
  Pdf.FormFill := True;            // needed so field values are live
  Pdf.FileName := 'expense-report.pdf';
  Pdf.Active := True;
  Bytes := Pdf.ExportXFDF('expense-report.xfdf');
  ShowMessage(Format('%d bytes of XFDF written', [Bytes]));
end;

Entrambe le direzioni presentano un overload TStream, di conseguenza non è richiesta la creazione di file temporanei su disco. L'esportazione in un TMemoryStream è la scelta ideale quando il file XFDF è destinato a una risposta HTTP, a un blob di database o a una voce di archivio firmata

var
  Buffer: TMemoryStream;
begin
  Buffer := TMemoryStream.Create;
  try
    Pdf.ExportXFDF(Buffer);        // same XML, no file involved
    Buffer.Position := 0;
    // hand the stream to a web response, blob column, or zip entry
  finally
    Buffer.Free;
  end;
end;

In che modo ImportXFDF unisce i commenti a ritroso nel documento?

L'importazione è la fase in cui si conclude il flusso di lavoro del revisore. Un collega annota il contratto in Acrobat, esporta i commenti come XFDF e invia pochi kilobyte di XML invece di una seconda copia del file PDF. TPdf.ImportXFDF analizza tale file, crea ciascuna annotazione sulla pagina indicata dal relativo attributo page e scrive i valori dei campi nelle corrispondenti annotazioni widget. La funzione restituisce il conteggio combinato di campi e annotazioni applicati, consentendo all'interfaccia utente di confermare con precisione quanto importato

var
  Applied: Integer;
begin
  Applied := Pdf.ImportXFDF('review-comments.xfdf');
  StatusBar.SimpleText :=
    Format('%d fields and annotations merged', [Applied]);
end;

Il parser è un analizzatore di tag scritto a mano, progettato per essere tollerante piuttosto che restrittivo: gli elementi sconosciuti vengono saltati, le istruzioni di elaborazione, le dichiarazioni DOCTYPE e i commenti vengono ignorati, i nomi dei flag non riconosciuti vengono tralasciati e i valori degli attributi vengono decodificati attraverso il set completo di entità XML e riferimenti a caratteri numerici, inclusi i surrogati UTF-16. I file XFDF generati da Acrobat, da un visualizzatore web o da altri strumenti vengono integrati senza problemi. Una volta inserite le annotazioni nel documento, il ciclo di revisione prosegue con il meccanismo di stato e risposta descritto nell'implementazione di un flusso di revisione delle annotazioni con il Componente PDFium, e i valori dei campi importati seguono la normale logica dell'ordine di tabulazione trattata nella navigazione tra i campi del modulo

Perché i separatori decimali sono importanti per XFDF?

Ogni annotazione in XFDF è posizionata tramite stringhe di coordinate — ad esempio rect="70.5,540,200,560" — e lo standard ISO 19444-1 richiede il punto come separatore decimale. La formattazione predefinita dei numeri a virgola mobile in Delphi segue le impostazioni locali di Windows, per cui su un sistema tedesco o francese un semplice FloatToStr trasforma 70.5 in 70,5, corrompendo un elenco di coordinate separate da virgole in dati non validi che altri motori rifiutano o interpretano in modo errato. Il Componente PDFium normalizza ogni numero che scrive: il separatore decimale delle impostazioni locali viene convertito in un punto e gli zeri finali vengono eliminati, garantendo che il file esportato sia identico byte per byte sia che sia stato generato su una macchina en-US che su una de-DE. Se hai mai dovuto correggere un bug in uno strumento PDF che funzionava in ufficio ma falliva presso un cliente europeo, ti sei imbattuto proprio in questa categoria di problemi — ed è utile sapere che il componente la gestisce automaticamente

Quali sono i limiti del round-trip?

Due limiti meritano di essere esposti chiaramente. In primo luogo, XFDF trasferisce i dati delle annotazioni, non le loro rappresentazioni grafiche renderizzate — i flussi di aspetto (appearance stream) non fanno parte del formato, quindi il visualizzatore ricevente rigenera l'aspetto di ciascuna annotazione a partire dalle sue proprietà. Un'evidenziazione o un rettangolo verranno renderizzati correttamente ovunque; un timbro con aspetto personalizzato utilizzerà come alternativa quello generato dal visualizzatore di destinazione per quel nome di timbro. In secondo luogo, le API PDFium sottostanti contengono metodi di scrittura per le stringhe ma non per i numeri delle annotazioni o la geometria dei tracciati, per cui Opacity, punti finali delle linee, vertici e tratti a inchiostro sono funzionalità di sola esportazione: il componente li preserva fedelmente durante la scrittura del file XFDF, ma non può riscriverli in un PDF in fase di importazione. I valori dei campi, i contenuti, i colori, i rettangoli (rects), i punti quad, le date e i metadati di identità completano il round-trip in entrambe le direzioni

Per iniziare rapidamente, l'esempio XfdfLab viene fornito nei set di demo per Delphi, C++Builder e Lazarus, e collega entrambe le chiamate a pulsanti operativi su qualsiasi PDF aperto. Il supporto XFDF è incluso nella versione corrente del Componente PDFium, insieme alle API di annotazione e dei moduli su cui si basa