Un PDF accessibile poggia su una struttura che la pagina visibile non mostra mai: l'albero di struttura definito in ISO 32000-1 §14.7. È una gerarchia logica di intestazioni, paragrafi, tabelle e figure, stratificata sopra il contenuto dipinto e mappata a ruoli standard attraverso una mappa dei ruoli. Un lettore di schermo legge quell'albero, non i segni sulla pagina. Senza di esso, una fattura generata che sembra immacolata è semanticamente vuota, perché il flusso di contenuto registra l'ordine di disegno e nient'altro. Il totale può essere annunciato prima delle righe, il piè di pagina può tagliare un paragrafo, e la tabella delle voci può collassare in una corsa indifferenziata di parole. Il costo di prevenire ciò è sbilanciato a tuo favore. Emettere struttura mentre disegni sono minuti di codice; retroagirla in documenti finiti è un progetto di remediation. losLab PDF Library (PDF Library for Delphi) espone l'albero a Delphi e C++Builder attraverso un piccolo insieme di chiamate che avvolgono ciascuna operazione di disegno nel suo ruolo logico
Come il contenuto marcato si lega all'albero di struttura
Due strati cooperano. Nel flusso di contenuto, le operazioni di disegno sono racchiuse in sequenze di contenuto marcato, ciascuna delle quali porta un MCID intero. Nel catalogo del documento, l'albero di struttura mappa quei MCID in una gerarchia di elementi tipizzati (H1, P, Table, Figure) con attributi come testo alternativo e lingua. I tipi di elemento personalizzati sono legali, ma ciascuno deve risolvere a un ruolo standard attraverso la mappa dei ruoli (ISO 32000-1 §14.8.4). Il contenuto che non porta alcun significato, come regole, sfondi e arredamento di pagina ripetuto, è marcato come artefatto così la tecnologia assistiva lo salta invece di leggerlo a metà frase
PDF Library for Delphi mantiene entrambi gli strati dietro una coppia di parentesi. BeginTag apre un elemento di struttura e avvia la sequenza di contenuto marcato, le chiamate di disegno atterrano al suo interno, e EndTag chiude entrambi. La contabilità che fa inciampare il tagging fatto a mano — i MCID e l'albero padre e i riferimenti di pagina — avviene internamente dove non puoi sbagliarla
Due interruttori a livello di documento inquadrano il lavoro prima che qualsiasi tag si apra. SetMarkInfo scrive la bandiera di catalogo che dichiara il documento taggato, e IsTaggedPDF la rilegge, che è il sondaggio economico per decidere se un file in entrata abbia struttura che valga la pena preservare. La lingua ha due punti di ingresso. SetDocumentLanguage imposta il default del documento da solo, mentre SetPDFUAMode lo imposta come parte dell'abilitazione dell'output PDF/UA completo. Un file può essere utilmente taggato senza rivendicare conformità PDF/UA, e un rollout graduale spesso inizia proprio lì
Tagging durante il disegno, non dopo
Il pattern di generazione che funziona è trattare la parentesi di tag come parte della firma di ogni chiamata di disegno, mai come un passaggio successivo:
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1); // origine in alto a sinistra
Lib.SetPDFUAMode('en-US'); // alza la versione di salvataggio a PDF 1.7
Lib.SetInformation(1, 'Service Manual'); // /Title è obbligatorio per PDF/UA
Lib.AddRoleMap('ManualTitle', 'H1'); // tipo personalizzato -> ruolo standard
Lib.AddStandardFont(4);
Lib.SetTextSize(18);
Lib.BeginTagEx2('ManualTitle', '', '', 'en-US', '', 'h1-cover', '');
Lib.DrawText(72, 96, 'Service Manual');
Lib.EndTag;
Lib.BeginTag('Figure', 'Exploded view of the gearbox assembly', '');
Lib.AddImageFromFile('gearbox.png', 0);
Lib.EndTag;
Lib.BeginArtifact('Layout'); // decorazione di pagina: esclusa dalla lettura
// ... disegna righelli e tinta di sfondo ...
Lib.EndArtifact;
Lib.SaveToFile('manual.pdf');
finally
Lib.Free;
end;
end;
Tre chiamate in quella sequenza portano peso di conformità. SetPDFUAMode abilita l'output PDF/UA e silenziosamente innalza la versione del documento a PDF 1.7, che collide con il blocco della versione. Un documento bloccato a PDF 1.4 con LockSaveVersion si rifiuta di salvare e restituisce il codice di errore 602 una volta che la modalità UA è attiva, uno scontro che tende a emergere quando i profili di archiviazione e i requisiti di accessibilità sono configurati da team diversi. SetInformation(1, ...) scrive il titolo del documento, che ISO 14289 si aspetta che i visualizzatori mostrino al posto del nome file; la sua assenza è uno dei riscontri PDF/UA più comuni in circolazione. AddRoleMap registra il tipo personalizzato ManualTitle come un H1, e saltarlo lascia la diagnostica descritta sotto che segnala un ruolo non mappato
I livelli di intestazione meritano una politica deliberata, non scelte ad-hoc fatte per l'aspetto di una pagina. Gli utenti di lettore di schermo saltano tra le sezioni per scorciatoia di intestazione, quindi un modello che va da H1 a H3 perché il livello intermedio sembrava troppo grande nel design visivo rompe silenziosamente quella navigazione, e nessuna revisione visiva mai la coglierà. È esattamente il difetto che la diagnostica HEADING-LEVEL-SKIP esiste per nominare. Mappa gli stili visivi di ciascun modello a una scala di intestazioni fissa una volta, in un solo posto, e la deriva non inizia mai
Tabelle che un lettore di schermo può effettivamente navigare
Le linee di griglia disegnate non significano nulla fuori schermo. Ciò che i lettori di schermo navigano sono relazioni strutturali: quali celle sono intestazioni, cosa governa ciascuna intestazione, e come le celle di dati si legano alle intestazioni nei layout irregolari. Le chiamate di attributo dell'elemento di struttura gestiscono tutte e tre:
Lib.BeginTag('Table', '', '');
Lib.BeginTag('TR', '', '');
Lib.BeginTagEx2('TH', '', '', '', '', 'col-part', '');
Lib.SetStructElemScope('Column'); // valido solo mentre questo TH è aperto
Lib.DrawText(72, 120, 'Part');
Lib.EndTag;
Lib.BeginTagEx2('TH', '', '', '', '', 'col-torque', '');
Lib.SetStructElemScope('Column');
Lib.SetStructElemColSpan(2); // l'intestazione copre le colonne valore e unità
Lib.DrawText(200, 120, 'Tightening torque');
Lib.EndTag;
Lib.EndTag;
Lib.BeginTag('TR', '', '');
Lib.BeginTag('TD', '', '');
Lib.SetStructElemHeaders('col-part'); // collegamento esplicito per tabelle irregolari
Lib.DrawText(72, 140, 'M8 flange bolt');
Lib.EndTag;
Lib.EndTag;
Lib.EndTag; // Table
La regola di ordinamento è rigorosa e applicata in silenzio. Ciascuna chiamata SetStructElem* si applica al tag che è aperto in quel momento, tra il suo BeginTag e il suo EndTag, e restituisce 0 senza sollevare nulla quando nessun tag è aperto o l'attributo non si applica a quello attuale. Una chiamata fuori posto semplicemente svanisce. Avvolgere i valori di ritorno in asserzioni durante lo sviluppo coglie la deriva mentre puoi ancora vederla; lasciata sola, una visibilità mancante emerge solo quando un audit di accessibilità fa correre un vero lettore di schermo attraverso la tabella. Gli ID elemento passati attraverso BeginTagEx2 alimentano l'albero degli ID (ISO 32000-1 §14.7.4), e questo è ciò che rende il legame SetStructElemHeaders risolvibile in primo luogo
La stessa famiglia di attributi copre il resto di ciò su cui la tecnologia assistiva fa leva. SetStructElemListNumbering dichiara come gli elementi di lista sono etichettati, così un lettore di schermo annuncia la posizione nella lista invece di recitare glifi di punto-elenco. SetStructElemBBox registra il riquadro di delimitazione di figure e tabelle, che le viste di riflusso usano per collocare il contenuto. SetStructElemActualText fornisce testo di sostituzione per corse i cui glifi non mappano a caratteri leggibili, come un capolettera assemblato da arte vettoriale. Ciascuno segue la stessa regola: si lega al tag aperto, o svanisce
Artefatti, lingua e il cancello diagnostico di pre-salvataggio
L'arredamento di pagina ripetuto, cioè intestazioni ricorrenti, segni di piega, filigrane e tinte di sfondo, appartiene dentro parentesi BeginArtifact e EndArtifact così non entra mai nel flusso di lettura. La lingua è ereditabile. Il default del documento viene dall'argomento SetPDFUAMode, e una corsa in un'altra lingua lo sovrascrive per elemento attraverso BeginTagEx o SetStructElemLang. Questo è ciò che mantiene pronunciabile una citazione francese dentro un manuale inglese
Prima di salvare, GetPDFUADiagnostics fa correre i controlli strutturali della libreria sul documento in memoria e restituisce i riscontri come testo, dove una stringa vuota significa che non è stato trovato nulla. I codici nominano direttamente i classici errori di redazione: FIGURE-NO-ALT per un'immagine senza testo alternativo, HEADING-LEVEL-SKIP per un H3 che segue un H1, ROLEMAP-UNMAPPED per un tipo personalizzato che non è mai stato registrato. Cablalo nella build (genera l'insieme di documenti, fallisci il passo su diagnostica non vuota) e le regressioni di accessibilità diventano fallimenti in stile tempo-di-compilazione invece di riscontri di audit mesi dopo. Il verdetto di conformità completo appartiene ancora al preflight sul file salvato, trattato in preflight PDF/A e PDF/UA in Delphi, perché alcune normalizzazioni sono applicate solo durante la serializzazione
La navigazione delle annotazioni ha la sua manopola. PDF/UA si aspetta che l'attraversamento da tastiera dei campi modulo e dei link segua l'ordine di struttura, e SetTabOrderMode scrive la voce di ordine di tabulazione a livello di pagina che i visualizzatori onorano, con GetTabOrderMode disponibile per auditare i file in entrata. È il tipo di requisito che nessuno nota finché un utente solo-tastiera non registra il bug, e costa una chiamata per documento farlo bene
Gli alberi di struttura non sopravvivono a ogni fusione
I documenti taggati restano taggati solo quando ogni passo di elaborazione successivo preserva l'albero, e il bordo affilato dentro PDF Library for Delphi è la famiglia delle liste di fusione. MergeFileListFast scambia la preservazione dell'albero di struttura con la velocità. È lo scambio giusto per i batch di immagini scansionate e quello sbagliato per i report taggati, perché l'output si apre bene, si renderizza in modo identico, e ha silenziosamente perso il suo strato di accessibilità. Usa il MergeFileList di default o la variante rigorosa ogni volta che un input è un PDF taggato, e fai di IsTaggedPDF parte delle asserzioni post-assemblaggio così un batch appiattito non può essere consegnato senza che qualcuno noti. Le pipeline di assemblaggio per grandi insiemi di documenti portano più compromessi di questo tipo, esplorati in fusione, divisione e accesso diretto di PDF grandi
Il ciclo di verifica si chiude fuori dalla libreria: apri l'output in Acrobat, ispeziona il pannello dei tag, e leggi almeno un documento per famiglia di modelli con un vero lettore di schermo. La diagnostica coglie errori strutturali; solo un orecchio umano coglie un ordine di lettura che è tecnicamente valido e praticamente sconcertante. Le build di valutazione e il riferimento completo dell'API di tagging sono sulla pagina del prodotto losLab PDF Library per Delphi