Articolo tecnico

Annotazioni PDF in Delphi con HotPDF: tipi e rettangoli

Un'annotazione non è contenuto di pagina. Quando chiami TextOut o disegni un rettangolo, i segni diventano parte del flusso di contenuto della pagina, incorporati nei byte che un renderer dipinge. Un'annotazione è un dizionario separato che si aggancia alla pagina attraverso il suo array /Annots, con il proprio rettangolo, la propria visualizzazione e il proprio ciclo di vita. Un lettore può aprirla, spostarla, nasconderla o rimuoverla senza toccare un singolo glifo della pagina sottostante. Quella separazione è l'intera ragione per cui le annotazioni esistono, ed è anche la fonte delle due cose che sorprendono le persone per prime: dove un'annotazione atterri e come appaia una volta che un determinato visualizzatore se ne impossessa.

HotPDF espone i sottotipi di annotazione ISO 32000 attraverso una famiglia di chiamate AddXxxAnnotation sull'oggetto pagina. Condividono tutte la stessa forma: un rettangolo che fissa l'annotazione sulla pagina nello spazio utente PDF, un payload (testo, un nome di timbro, una coppia di punti) e un colore. Azzecca il rettangolo e la maggior parte del lavoro è fatta. Il resto è sapere quali sottotipi portano la propria visualizzazione e quali si affidano al visualizzatore per disegnarli.

Una pagina PDF prodotta da HotPDF che mostra icone di note di testo, caselle di testo libero, marcature di rettangoli e linee, e timbri di approvazione posizionati sulla pagina
Una pagina con più sottotipi di annotazione contemporaneamente: note di testo, testo libero, marcature geometriche e timbri

Il rettangolo è l'annotazione, non il testo

Ogni chiamata di annotazione accetta un TRect, e quel rettangolo ha un significato diverso dalle coordinate che passi a TextOut. Per una nota di testo è il punto di click interattivo, la piccola regione dove si trova l'icona della nota e dove un click apre il commento. Per un rettangolo o una casella di testo libero è l'estensione visibile della marcatura. Per un timbro è il riquadro in cui viene scalata l'immagine del timbro. I numeri sono punti dello spazio utente PDF, misurati dall'angolo in basso a sinistra della pagina con Y che cresce verso l'alto, la stessa convenzione usata dal resto di HotPDF.

Una nota di testo è il sottotipo più leggero. Le fornisci il corpo del testo, un rettangolo per l'icona, un flag per indicare se si apre di default, un nome di icona e un colore.

Pdf.CurrentPage.AddTextAnnotation(
  'Reviewer: confirm the totals on this line before sign-off.',
  Rect(120, 700, 140, 720),   // icon hotspot, ~20pt square
  False,                      // closed until the reader clicks it
  taComment,                  // bubble icon
  clBlue);

Il rettangolo qui è deliberatamente piccolo, circa venti punti per lato, perché una nota di testo è solo un'icona finché qualcuno non ci clicca. Rendi il rettangolo grande e non ottieni una nota grande; ottieni un bersaglio di click sovradimensionato con l'icona fissata a un angolo. Il flag Open controlla se il popup è visibile quando si carica il documento. Imposta alcune note a True e si sovrappongono l'una all'altra e al contenuto, quindi riservalo per l'unica nota che vuoi far vedere immediatamente al lettore.

Il nome dell'icona proviene da THPDFTextAnnotationType, che si mappa alle icone delle note standard: taComment, taKey, taNote, taHelp, taParagraph, taNewParagraph e taInsert. L'icona è l'unica cosa che il tipo cambia. Non altera il comportamento, e vale la pena sapere che non tutti i visualizzatori disegnano tutte e sette; quelle sicure su lettori vecchi e nuovi sono taComment, taNote e taHelp.

Il testo libero scrive sulla pagina, ma rimane un'annotazione

Un'annotazione di testo libero assomiglia a contenuto perché il testo è visibile senza un click, seduto nel suo rettangolo come una didascalia. È ancora un'annotazione, con tutta la separabilità che ciò implica, il che è esattamente ciò che vuoi per un timbro di revisione o un'etichetta bozza che qualcuno dovrebbe poter rimuovere in seguito. La firma scambia l'icona e il flag di apertura con un valore di giustificazione.

Pdf.CurrentPage.AddFreeTextAnnotation(
  'DRAFT - not for distribution',
  Rect(200, 210, 400, 235),   // the box the text is laid into
  ftCenter,                   // ftLeftJust / ftCenter / ftRightJust
  clRed);

Qui il rettangolo conta più di quanto non faccia per una nota di testo, perché il testo si adatta e si allinea al suo interno. Dimensiona la casella troppo corta e il testo viene ritagliato al bordo inferiore; troppo stretta e va a capo in punti che non intendevi. La giustificazione proviene da THPDFFreeTextAnnotationJust e ha solo i tre valori. Poiché il testo libero è un'annotazione di marcatura, un lettore che apre il file in un editor può selezionarla, spostarla o eliminarla come unità, il che è la differenza che determina se scegli il testo libero o disegni semplicemente le parole con TextOut. Se l'etichetta deve essere permanente, disegnala. Se è editoriale e destinata a essere rimossa, rendila un'annotazione.

Marcature geometriche e di linea per indicare elementi

Rettangoli, cerchi e linee sono le marcature che usi per indicare una regione piuttosto che descriverla a parole. AddCircleSquareAnnotation copre le due forme di riquadro attraverso un THPDFCSAnnotationType di csCircle o csSquare, con il rettangolo che delimita la forma.

// A box drawn around a figure that needs attention
Pdf.CurrentPage.AddCircleSquareAnnotation(
  'Check this region against the source data',
  Rect(50, 300, 120, 360),
  csSquare,
  clGreen);

// A line, given two points rather than a rectangle
var
  StartPt, EndPt: THPDFCurrPoint;
begin
  StartPt.X := 130; StartPt.Y := 360;
  EndPt.X   := 250; EndPt.Y   := 320;
  Pdf.CurrentPage.AddLineAnnotation(
    'Points from the note to the figure',
    StartPt, EndPt,
    clBlue);
end;

Nota che l'annotazione di linea rompe il pattern del rettangolo: accetta due record THPDFCurrPoint, un inizio e una fine, perché una linea è definita dai suoi punti finali, non da un riquadro di delimitazione. Il colore imposta il tratto. Se vuoi le punte di freccia, HotPDF ha overload di AddLineAnnotation che accettano stili di terminazione di linea, ma la forma semplice a tre argomenti disegna una linea nuda, che è di solito ciò che un callout vuole.

I sottotipi di marcatura di testo operano su una regione che hai già impaginato. AddHighlightAnnotation accetta un rettangolo, un contenuto opzionale e un colore che per default è giallo, e tinge l'area come farebbe una penna evidenziatore. È pensato per stare sopra del testo reale, quindi il rettangolo dovrebbe corrispondere ai limiti delle parole che hai disegnato, il che significa che di solito lo calcoli dalle stesse coordinate che hai passato a TextOut anziché indovinare.

I timbri dipendono dal visualizzatore per il rendering

Un'annotazione di timbro è quella che più probabilmente avrà un aspetto diverso da un lettore all'altro, e la ragione vale la pena capire. AddStampAnnotation nomina un timbro standard attraverso THPDFStampAnnotationType, con valori come satApproved, satConfidential, satFinal, satDraft e satForComment.

Pdf.CurrentPage.AddStampAnnotation(
  'Approved for release on review',
  Rect(50, 400, 200, 440),
  satApproved,
  clGreen);

Il nome del timbro è una richiesta. PDF definisce l'insieme dei nomi di timbro standard ma non l'artwork dietro di essi, quindi ogni visualizzatore include la propria resa di "APPROVED" o "CONFIDENTIAL," e alcuni non renderizzano nulla per nomi che non riconoscono. Il rettangolo controlla il riquadro in cui viene scalata l'immagine, e il colore è un suggerimento che il visualizzatore può o meno rispettare. Se un timbro deve apparire identico ovunque, il percorso affidabile non è affatto un timbro standard: disegna il segno tu stesso con TextOut e le chiamate di disegno, oppure posizionalo come annotazione di testo libero la cui visualizzazione controlli tu. Usa il timbro standard quando vuoi l'aspetto familiare del visualizzatore e puoi tollerare la variazione.

Gli allegati file seguono la stessa forma rettangolo-più-payload. AddFileAttachmentAnnotation accetta la descrizione, il percorso del file da incorporare, un rettangolo per l'icona della graffetta e un colore. Il file viaggia all'interno del PDF, e l'icona è il punto di accesso che un lettore usa per estrarlo.

In cosa le annotazioni differiscono dai campi AcroForm

La confusione che costa più tempo è trattare un'annotazione come se fosse un campo di modulo. Entrambi si agganciano alla pagina attraverso /Annots, e un campo di modulo è in realtà un sottotipo speciale di annotazione (un widget), motivo per cui sembrano correlati. Non sono intercambiabili. Un campo di modulo contiene un valore, ha un nome, partecipa all'ordine di tabulazione e può essere inviato, reimpostato o controllato da script; quelli si creano con le chiamate AddTextField, AddCheckBox e AddPushButton, non con le chiamate di annotazione di questa pagina. Un'annotazione di marcatura contiene un commento o una forma, non ha alcun valore da inviare ed è lo strumento sbagliato nel momento in cui hai bisogno di raccogliere input.

Il test pratico è semplice. Se un utente deve digitare, scegliere o fare click e il documento deve ricordarlo, vuoi un campo AcroForm. Se stai lasciando una nota, marcando una regione o apponendo uno stato che viaggia con il file ma non è un dato, vuoi un'annotazione. Mescolarli produce documenti che sembrano giusti e si comportano in modo sbagliato: un "campo" che nessuno può compilare, o un commento che scompare quando un modulo viene reimpostato. Il lato interattivo, con i tipi di campo, la validazione e le azioni di invio, è un argomento a sé stante trattato nella guida ai campi e alle azioni AcroForm.

Comporre una pagina

I pezzi si compongono come il resto di HotPDF. Imposta le proprietà del documento, chiama BeginDoc, disegna qualsiasi contenuto di pagina ti serva con le chiamate di testo e grafica, aggiungi le annotazioni sopra e chiudi con EndDoc. Le annotazioni si agganciano a CurrentPage, quindi dopo un AddPage atterrano sulla nuova pagina, e una nota destinata alla pagina uno apparirà silenziosamente sulla pagina due se la aggiungi dopo l'interruzione.

Pdf := THotPDF.Create(nil);
try
  Pdf.FileName := 'annotated.pdf';
  Pdf.Compression := cmFlateDecode;
  Pdf.FontEmbedding := True;
  Pdf.BeginDoc;

  Pdf.CurrentPage.SetFont('Arial', [], 11);
  Pdf.CurrentPage.TextOut(50, 740, 0, 'Quarterly figures, draft for review');

  Pdf.CurrentPage.AddTextAnnotation(
    'Confirm the totals before sign-off.',
    Rect(50, 720, 70, 740), False, taComment, clBlue);
  Pdf.CurrentPage.AddFreeTextAnnotation(
    'DRAFT', Rect(450, 720, 540, 745), ftCenter, clRed);
  Pdf.CurrentPage.AddStampAnnotation(
    'For comment', Rect(50, 660, 180, 695), satForComment, clGreen);

  Pdf.EndDoc;
finally
  Pdf.Free;
end;

Un ultimo riflesso utile da sviluppare quando l'output sembra sbagliato: apri il file in più di un visualizzatore prima di decidere che il codice sia difettoso. I timbri e le icone delle note più rare sono i soliti colpevoli, e poiché l'annotazione è una richiesta al lettore piuttosto che pixel dipinti, una differenza tra Acrobat e un visualizzatore leggero è spesso la specifica che funziona come previsto, non un bug nella tua chiamata.

Le chiamate di annotazione mostrate qui fanno parte del HotPDF Component per Delphi e C++Builder.