Teknisk artikkel

Automatisk strukturmerking for tilgjengelig PDF i Delphi

PDFlibPas kan merke et dokument mens det tegnes. Slå på SetAutoTagMode og vanlige DrawText-kall blir avsnitt, tekst tegnet rett etter RegisterHeading blir en overskrift på det nivået, løpende topptekster og bunntekster blir artefakter som en leser hopper over, bilder blir figurer, og DrawTableRows fører tabellen, radene og cellene inn i strukturtreet

Alternativet — og inntil nylig det eneste valget — var å pakke hvert tegnekall inn i BeginTag og EndTag for hand. Det virker, og for dokumenter med uvanlig struktur er det fortsatt rett verktøy. For den vanlige rapporten, fakturaen eller kontoutskriften betyr det at tilgjengeligheten til utdataen avhenger av at ingen noensinne glemmer et par, på tvers av hver kodebane som tegner noe som helst

Hva modusbitene dekker

SetAutoTagMode tar en bitmaske og returnerer modusen som var gjeldende før. AUTOTAG_TEXT (1) merger tekst som et avsnitt, eller som en overskrift når en er forfallent. AUTOTAG_FURNITURE (2) markerer løpende topptekster, bunntekster og sidetall som artefakter. AUTOTAG_FIGURE (4) gjør et tegnet bilde om til en figur, eller til en artefakt når det ble erklært dekorativt. AUTOTAG_TABLE (8) fører tegnede tabeller inn i strukturtreet. AUTOTAG_DEFAULT er 15, som er alle fire

Å slå på modusen markerer også dokumentet som merket, og det trinnet er mindre kosmetisk enn det høres ut som. En leser regner et dokument som umerket hvis ikke katalogen sier noe annet (ISO 32000-1 §14.7.1), så en fil som bærer et komplett strukturtre uten noen /MarkInfo-erklæring kunngjøres av assistiv teknologi som å ikke ha noen struktur i det hele tatt. Treet er der; ingenting leser det

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;

Hvordan vet en overskrift hvilken tekst den hører til?

RegisterHeading angir nivået for den neste teksten som tegnes, og den venter på tekst. Hvis et bilde tegnes i mellomtiden, blir bildet en figur og overskriften forblir ventende for teksten som følger. Den oppførselen er bevisst: alternativet, der bildet tar overskriftsnivået, produserte dokumenter der en dekorativ linje under en tittel ble annonsert som selve tittelen

Den samme «brukt på ett element»-regelen gjelder for figurer. RegisterFigure leverer beskrivelsen det neste bildet bærer, og RegisterDecoration erklærer det neste bildet som en linje, kant eller bakgrunn som ikke bærer noen mening. Begge konsumeres av ett bilde, slik at et senere bilde aldri arver en beskrivelse ment for et tidligere — noe som er måten alt-tekst ender opp festet til feil bilde i handkodet merking

Beskrivelsen betyr mer enn noen annen enkel streng i et tilgjengelig dokument. En blind leser får beskrivelsen i stedet for bildet, og det er hele det vedkommende får. «Diagram» er ikke en beskrivelse; «Kvartalsvis omsetning per region, med østregionen høyst i Q3» er en beskrivelse

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

Tabeller, overskrifter og hvor gjentakelsesbeslutningen bor

Med tabellbiten på fører DrawTableRows tabellen, radene og cellene inn i strukturtreet, slik at en leser kan si hvilken kolonne en verdi står i i stedet for å lese hele tabellen som en sammenhengende tekst uten relasjon. SetTableHeaderRowCount angir hvor mange ledende rader som er overskrifter; disse radene skrives som overskriftsceller med kolonneomfang, og det er det som lar en leser annonsere overskriften til verdien brukeren befinner seg på

Overskriftrader navngitt på denne måten blir værende der de er. Å gjenta dem på toppen av hver side er en layoutbeslutning, og den forblir det: DrawTaggedTableRows tar et RepeatHeaderRows-argument for nøyaktig det formålet. Å holde de to adskilt unngår at strukturtreet får en ekstra kopi av overskriften ved hvert sideskift, noe som er det en automatisk gjentakelse ville produsert

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;

Blanding av automatisk og manuell merking

Automatisk merking treder til side inne i en tag åpnet for hand. Deler av et dokument kan beskrives av koden din og resten overlates til biblioteket, uten at de to nøster seg inn i hverandre — noe som er det arrangementet de fleste virkelige dokumenter ønsker. Forsiden og signaturblokken har struktur bare du forstår; de to hundre sidene med brødtekst i mellom har ikke det

To sikkerhetsregler holder utdataen ren. Ingenting merkes inne i en artefakt, fordi innhold markert som en artefakt ikke må bære noe struktur-element. Og tom tekst åpner intet element, slik at et forvillet DrawText med en tom streng ikke kan produsere et struktur-element som en leser ville annonsert som blank. Begge er den typen defekt handkodet dokumenter samler i stillhet, og som en validator rapporterer i bulk måneder senere

Hva automatisk merking fortsatt ikke avgjør for deg

Leseorden utover tegneorden, semantiske roller som ikke er avsnitt, overskrift, figur eller tabell, og språkerklæringer. Automatisk merking tilordner struktur i den orden innhold tegnes — hvis layoutkoden din tegner sidefeltet før brødteksten, er det den orden treet registrerer. For dokumenter der visuell orden og leseorden genuint avviker, forblir det manuelle merkings-API-et det rette verktøyet, og gjennomgangen av merket PDF og tilgjengelighetsstruktur dekker roller, omfang og overskriftsbindinger i detalj

Når dokumentet er ferdig, valider i stedet for å anta: notatene om PDF/A- og PDF/UA-preflight viser hvordan du får en dom på strukturen du produserte, og gjennomgangen av datadrevet rapporteksport dekker hvor disse kallene hører hjemme i en rapportmotor som genererer layout fra data

PDFlibPas er et PDF-bibliotek i ren Pascal for Delphi, C++Builder og Lazarus uten noen ekstern PDF-runtime, så tilgjengelig utdata produseres av den samme koden som tegner dokumentet — se PDFlibPas-produktsiden for det fullstendige API-et og plattformlisten