PDFlibPas può taggare un documento mentre viene disegnato. Attiva SetAutoTagMode e le normali chiamate DrawText diventano paragrafi, il testo disegnato subito dopo RegisterHeading diventa un titolo di quel livello, le testate e le piedini correnti diventano artefatti che un lettore salta, le immagini diventano figure e DrawTableRows porta la tabella, le sue righe e le sue celle nell'albero di struttura
L'alternativa — e fino a poco tempo fa l'unica opzione — era avvolgere ogni chiamata di disegno in BeginTag ed EndTag a mano. Funziona, e per documenti con struttura inconsueta è ancora lo strumento giusto. Per il rapporto, la fattura o l'estratto conto ordinario, significa che l'accessibilità dell'output dipende dal fatto che nessuno dimentichi mai una coppia, in ogni percorso di codice che disegna qualcosa
Cosa coprono i bit della modalità
SetAutoTagMode prende una maschera di bit e restituisce la modalità precedentemente in vigore. AUTOTAG_TEXT (1) tagga il testo come paragrafo, o come titolo quando ne è atteso uno. AUTOTAG_FURNITURE (2) marca testate, piè di pagina e numeri di pagina come artefatti. AUTOTAG_FIGURE (4) trasforma un'immagine disegnata in una figura, o in un artefatto quando è stata dichiarata decorativa. AUTOTAG_TABLE (8) porta le tabelle disegnate nell'albero di struttura. AUTOTAG_DEFAULT è 15, cioè tutti e quattro
Attivare la modalità marca anche il documento come tagged, e quel passo è meno cosmetico di quanto sembri. Un lettore considera un documento come non taggato a meno che il catalogo non dica diversamente (ISO 32000-1 §14.7.1), quindi un file che porta un albero di struttura completo senza dichiarazione /MarkInfo viene annunciato dalle tecnologie assistive come privo di struttura del tutto. L'albero c'è; niente lo legge
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.SetAutoTagMode(AUTOTAG_DEFAULT); // text + furniture + figures + tables
Lib.AddStandardFont(4);
Lib.SetTextSize(18);
Lib.RegisterHeading(1, 'Annual service report');
Lib.DrawText(72, 96, 'Annual service report'); // becomes H1
Lib.SetTextSize(11);
Lib.DrawText(72, 130, 'Every unit installed before 2024 was inspected.');
Lib.SaveToFile('report.pdf');
finally
Lib.Free;
end;
end;
Come fa un titolo a sapere a quale testo appartiene?
RegisterHeading nomina il livello per il prossimo testo disegnato, e resta in attesa di testo. Se un'immagine viene disegnata nel frattempo, l'immagine diventa una figura e il titolo resta in sospeso per il testo che segue. Quel comportamento è deliberato: l'alternativa, in cui l'immagine prende il livello di titolo, produceva documenti in cui una linea decorativa sotto un titolo veniva annunciata come il titolo stesso
La stessa regola del «consumato da un solo elemento» governa le figure. RegisterFigure fornisce la descrizione che la prossima immagine porta con sé, e RegisterDecoration dichiara la prossima immagine come una linea, un bordo o uno sfondo privi di significato. Entrambi vengono consumati da una sola immagine, così un'immagine successiva non eredita mai una descrizione destinata a una precedente — il che è il modo in cui il testo alternativo finisce attaccato all'immagine sbagliata nel codice taggato a mano
La descrizione conta più di qualsiasi altra singola stringa in un documento accessibile. Un lettore non vedente ottiene la descrizione al posto dell'immagine, e questo è tutto ciò che ottiene. «Grafico» non è una descrizione; «Ricavi trimestrali per regione, con la regione orientale più alta nel Q3» lo è
Lib.RegisterFigure('Exploded view of the gearbox assembly');
Lib.AddImageFromFile('gearbox.png', 0); // becomes a tagged Figure
Lib.RegisterDecoration; // meaningless rule
Lib.AddImageFromFile('divider.png', 0); // drawn inside a layout artifact
Tabelle, intestazioni e dove risiede la decisione sulla ripetizione
Con il bit di tabella attivo, DrawTableRows porta la tabella, le sue righe e le sue celle nell'albero di struttura, così un lettore può dire in quale colonna si trova un valore invece di leggere l'intera tabella come una sequenza di testo non correlato. SetTableHeaderRowCount nomina quante righe iniziali sono intestazioni; quelle righe vengono scritte come celle di intestazione che portano uno scope di colonna, il che è ciò che permette a un lettore di annunciare l'intestazione del valore su cui si trova l'utente
Le righe di intestazione nominate in questo modo restano dove sono. Ripeterle in cima a ogni pagina è una decisione di layout, e tale resta: DrawTaggedTableRows prende un argomento RepeatHeaderRows esattamente per quello scopo. Mantenere le due cose separate evita che l'albero di struttura acquisisca una seconda copia dell'intestazione per ogni salto di pagina, il che è ciò che produrrebbe una ripetizione automatica
var
TableID: Integer;
begin
TableID := Lib.CreateTable(40, 3);
Lib.SetTableHeaderRowCount(TableID, 1); // row 1 is the header band
Lib.SetTableCellContent(TableID, 1, 1, 'Part');
Lib.SetTableCellContent(TableID, 1, 2, 'Torque');
Lib.SetTableCellContent(TableID, 1, 3, 'Unit');
// ... fill the data rows ...
// Draw rows 1..40 into a 600pt band, repeating one header row per page
Lib.DrawTaggedTableRows(TableID, 72, 150, 600, 1, 40, 1);
end;
Mescolare tagging automatico e manuale
Il tagging automatico si fa da parte dentro un tag aperto a mano. Parte di un documento può essere descritta dal tuo codice e il resto lasciato alla libreria, senza che i due si annidino l'uno nell'altro — il che è l'assetto che la maggior parte dei documenti reali vuole. La pagina di copertina e il blocco della firma hanno una struttura che solo tu conosci; le duecento pagine di corpo testo in mezzo no
Due regole di sicurezza mantengono pulito l'output. Niente viene taggato dentro un artefatto, perché il contenuto marcato come artefatto non deve portare alcun elemento di struttura. E il testo vuoto non apre alcun elemento, così una DrawText dispersa con una stringa vuota non può produrre un elemento di struttura che un lettore annuncerebbe come vuoto. Entrambi sono il tipo di difetto che i documenti taggati a mano accumulano silenziosamente e che un validatore riporta in blocco mesi dopo
Cosa il tagging automatico ancora non decide per te
L'ordine di lettura oltre l'ordine di disegno, i ruoli semantici che non sono paragrafo, titolo, figura o tabella e le dichiarazioni di lingua. Il tagging automatico assegna la struttura nell'ordine in cui il contenuto viene disegnato: se il tuo codice di layout disegna la barra laterale prima del corpo, quello è l'ordine che l'albero registra. Per i documenti in cui l'ordine visivo e quello di lettura differiscono realmente, l'API di tagging manuale resta lo strumento giusto, e la guida su tagged PDF e struttura di accessibilità copre ruoli, scope e binding delle intestazioni in dettaglio
Quando il documento è finito, valida invece di presumere: le note su preflight PDF/A e PDF/UA mostrano come ottenere un verdetto sulla struttura che hai prodotto, e la guida sull'esportazione di rapporti guidata da dataset copre dove queste chiamate si inseriscono in un motore di report che genera il proprio layout dai dati
PDFlibPas è una libreria PDF nativa in Pascal per Delphi, C++Builder e Lazarus senza alcun runtime PDF esterno, quindi l'output accessibile è prodotto dallo stesso codice che disegna il documento — vedi la pagina prodotto PDFlibPas per la lista completa di API e piattaforme