PDFlibPas poate marca un document în timp ce este desenat. Activați SetAutoTagMode, iar apelurile obișnuite DrawText devin paragrafe, textul desenat imediat după RegisterHeading devine titlu la acea nivelare, anteturile și subsolurile curente devin artefacte pe care un cititor le ignoră, imaginile devin figuri, iar DrawTableRows duce tabelul, rândurile și celulele sale în arborele de structură
Alternativa — și până recent singura opțiune — era încadrarea fiecărui apel de desen în BeginTag și EndTag manual. Funcționează, iar pentru documentele cu structură neobișnuită este încă instrumentul potrivit. Pentru raportul, factura sau extrasul obișnuit, înseamnă că accesibilitatea ieșirii depinde de faptul că nimeni nu uită vreodată o pereche, pe toate căile de cod care desenează ceva
Ce acoperă biții de mod
SetAutoTagMode ia o mască de biți și returnează modul care era în vigoare anterior. AUTOTAG_TEXT (1) marchează textul ca paragraf, sau ca titlu când unul este datorat. AUTOTAG_FURNITURE (2) marchează anteturile curente, subsolurile și numerele de pagină drept artefacte. AUTOTAG_FIGURE (4) transformă o imagine desenată într-o figură, sau într-un artefact când a fost declarată decorativă. AUTOTAG_TABLE (8) duce tabelele desenate în arborele de structură. AUTOTAG_DEFAULT este 15, adică toate patru
Activarea modului marchează de asemenea documentul ca etichetat, iar acel pas este mai puțin cosmetic decât sună. Un cititor consideră un document ca neetichetat dacă nu spune altfel în catalog (ISO 32000-1 §14.7.1), așa că un fișier care poartă un arbore de structură complet fără o declarație /MarkInfo este anunțat de tehnologia asistivă ca neavând nicio structură. Arborele este acolo; nimic nu îl citește
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;
Cum știe un titlu cărui text îi aparține?
RegisterHeading numește nivelul pentru următorul text desenat și așteaptă după text. Dacă o imagine este desenată între timp, imaginea devine figură, iar titlul rămâne în așteptare pentru textul care urmează. Acel comportament este intenționat: alternativa, în care imaginea preia nivelul titlului, producea documente în care o riglă decorativă sub un titlu era anunțată ca fiind titlul
Permisiunea „consumat de un singur element” guvernează figurile. RegisterFigure furnizează descrierea pe care o va purta următoarea imagine, iar RegisterDecoration declară următoarea imagine drept riglă, chenar sau fundal care nu poartă niciun înțeles. Ambele sunt consumate de o singură imagine, astfel încât o imagine ulterioară nu moștenește niciodată o descriere destinată uneia anterioare — exact mecanismul prin care textul alternativ ajunge atașat la imaginea greșită în codul marcat manual
Descrierea contează mai mult decât orice alt șir într-un document accesibil. Un cititor nevăzător primește descrierea în locul imaginii, iar asta este tot ce primește. „Grafic” nu este o descriere; „Venit trimestrial pe regiuni, cu regiunea estică cea mai ridicată în T3” este
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
Tabele, anteturi și unde stă decizia de repetare
Cu bitul de tabel activat, DrawTableRows duce tabelul, rândurile și celulele sale în arborele de structură, astfel încât un cititor poate spune în ce coloană stă o valoare, în loc să citească tot tabelul ca o secvență de text fără legătură. SetTableHeaderRowCount numește câte rânduri din capăt sunt anteturi; acele rânduri sunt scrise ca celule de antet purtând un perimetru de coloană, exact ceea ce îi permite unui cititor să anunțe titlul valorii pe care se află utilizatorul
Rândurile de antet numite în acest fel rămân unde sunt. Repetarea lor în capătul fiecărei pagini este o decizie de aspect, și rămâne una: DrawTaggedTableRows ia un argument RepeatHeaderRows exact pentru acest scop. Menținerea celor două separate evită ca arborele de structură să capete o a doua copie a antetului la fiecare pauză de pagină, ceea ce ar produce o repetare automată
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;
Combinarea marcării automate și manuale
Marcarea automată se retrage în interiorul unei etichete deschise manual. O parte a documentului poate fi descrisă de codul dumneavoastră, iar restul lăsat în seama bibliotecii, fără ca cele două să se imbrice — un aranjament pe care majoritatea documentelor reale îl doresc. Pagina de copertă și blocul de semnături au o structură pe care doar dumneavoastră o înțelegeți; cele două sute de pagini de text-corp dintre ele nu
Două reguli de siguranță păstrează ieșirea curată. Nimic nu este marcat în interiorul unui artefact, deoarece conținutul marcat ca artefact nu trebuie să poarte niciun element de structură. Și textul gol nu deschide niciun element, astfel încât un DrawText rătăcit cu un șir gol nu poate produce un element de structură pe care un cititor l-ar anunța ca gol. Ambele sunt defectele pe care documentele marcate manual le acumulează tăcut și pe care un validator le raportează în masă luni mai târziu
Ceea ce marcarea automată încă nu decide pentru dumneavoastră
Ordinea de citire dincolo de ordinea de desen, rolurile semantice care nu sunt paragraf, titlu, figură sau tabel, și declarațiile de limbă. Marcarea automată atribuie structura în ordinea în care este desenat conținutul — dacă codul de aspect desenează bara laterală înaintea corpului, aceasta este ordinea pe care o înregistrează arborele. Pentru documentele în care ordinea vizuală și ordinea de citire diferă cu adevărat, API-ul de marcare manuală rămâne instrumentul potrivit, iar prezentarea detaliată a PDF-ului etichetat și a structurii de accesibilitate acoperă rolurile, perimetrele și legăturile de antet în detaliu
Când documentul este gata, validați în loc să presupuneți: notele despre preflight PDF/A și PDF/UA arată cum să obțineți un verdict asupra structurii produse, iar prezentarea detaliată despre exportul de rapoarte condus de seturi de date acoperă unde se încadrează aceste apeluri într-un motor de rapoarte care își generează aspectul din date
PDFlibPas este o bibliotecă PDF nativă în Pascal pentru Delphi, C++Builder și Lazarus, fără niciun runtime PDF extern, astfel încât ieșirea accesibilă este produsă de același cod care desenează documentul — vedeți pagina produsului PDFlibPas pentru lista completă de API și platforme