Saavutettava PDF nojaa yhteen rakenteeseen, jota näkyvä sivu ei koskaan näytä: ISO 32000-1 §14.7:n määrittelemään rakennepuuhun. Se on looginen hierarkia otsikoista, kappaleista, taulukoista ja kuvista, kerrostettu maalatun sisällön päälle ja kuvattu standardirooleihin role mapin kautta. Ruudunlukuohjelma lukee tuon puun, ei merkkejä sivulla. Ilman sitä generoitu lasku, joka näyttää virheettömältä, on semanttisesti tyhjä, koska sisältövirta tallentaa piirtojärjestyksen eikä mitään muuta. Yhteissumma voidaan ilmoittaa ennen rivejä, alatunniste voi tunkeutua kappaleeseen, ja rivit-taulukko voi romahtaa yhdeksi eriytymättömäksi sanajuoksuvaksi. Sen estämisen kustannus on epätasaisesti sinun eduksesi. Rakenteen emittoiminen piirron aikana on minuuttien koodia; sen jälkiasentaminen valmiisiin dokumentteihin on korjausprojekti. losLab PDF Library (PDF Library for Delphi) paljastaa puun Delphille ja C++Builderille pienen joukon kutsujen kautta, jotka kietovat kunkin piirto-operaation sen loogiseen rooliin
Kuinka merkitty sisältö sitoutuu rakennepuuhun
Kaksi kerrosta tekevät yhteistyötä. Sisältövirrassa piirto-operaatiot suljetaan merkityn sisällön sekvensseihin, joista kukin kantaa kokonaisluku-MCID:tä. Dokumentin luettelossa rakennepuu kuvaa nuo MCID:t tyypitettyjen elementtien (H1, P, Table, Figure) hierarkiaksi ominaisuuksineen kuten vaihtoehtoinen teksti ja kieli. Mukautetut elementtityypit ovat laillisia, mutta jokaisen on ratkaistava standardirooliin role mapin kautta (ISO 32000-1 §14.8.4). Sisältö, joka ei lainkaan kanna merkitystä — kuten viivat, taustat ja toistuva sivukaluste — merkitään artefaktiksi, jotta avustava teknologia ohittaa sen lauseen keskellä lukemisen sijaan
PDF Library for Delphi ylläpitää molempia kerroksia yhden sulku-parin takana. BeginTag avaa rakenneelementin ja aloittaa merkityn sisällön sekvenssin, piirtokutsut laskeutuvat sen sisään, ja EndTag sulkee molemmat. Kirjanpito, joka kompastelee käsin tehtyä tagitusta — MCID:t ja parent-puu ja sivuviitteet — tapahtuu sisäisesti, missä et voi saada sitä väärin
Kaksi dokumenttitason kytkintä kehystävät työn ennen kuin yksikään tagi avautuu. SetMarkInfo kirjoittaa luettelolipun, joka julistaa dokumentin tagitetuksi, ja IsTaggedPDF lukee sen takaisin, mikä on halpa ensitarkistus päätettäessä, onko saapuvalla tiedostolla lainkaan säilyttämisen arvoista rakennetta. Kielellä on kaksi tulokohtaa. SetDocumentLanguage asettaa dokumentin oletuksen yksinään, kun taas SetPDFUAMode asettaa sen osana täyden PDF/UA-tuotoksen kytkemistä. Tiedosto voi olla hyödyllisesti tagitettu väittämättä PDF/UA-vaatimustenmukaisuutta, ja vaiheittainen käyttöönotto alkaa usein juuri sieltä
Tagitusta piirron aikana, ei sen jälkeen
Toimiva generointimalli on käsitellä tagisulkua osana joka ikisen piirtokutsun allekirjoitusta, ei koskaan myöhempänä läpikäyntinä:
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1); // origo vasemmassa yläkulmassa
Lib.SetPDFUAMode('en-US'); // nostaa tallennusversion PDF 1.7:ään
Lib.SetInformation(1, 'Service Manual'); // /Title on pakollinen PDF/UA:lle
Lib.AddRoleMap('ManualTitle', 'H1'); // mukautettu tyyppi -> standardirooli
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'); // sivun koristelu: suljettu pois lukemisesta
// ... piirrä viivat ja taustasävy ...
Lib.EndArtifact;
Lib.SaveToFile('manual.pdf');
finally
Lib.Free;
end;
end;
Kolme kutsua tuossa sekvenssissä kantavat vaatimustenmukaisuuspainoa. SetPDFUAMode kytkee PDF/UA-tuotoksen päälle ja hiljaisesti nostaa dokumenttiversiota PDF 1.7:ään, mikä törmää versiolukitukseen. PDF 1.4:ään LockSaveVersion:llä lukittu dokumentti kieltäytyy tallentumasta ja palauttaa virhekoodin 602 kun UA-tila on aktiivinen, yhteentörmäys joka pyrkii nousemaan esiin kun arkistoprofiilit ja saavutettavuusvaatimukset määrittävät eri tiimit. SetInformation(1, ...) kirjoittaa dokumentin otsikon, jonka ISO 14289 odottaa katseluohjelmien näyttävän tiedostonimen sijaan; sen puuttuminen on yksi yleisimmistä PDF/UA-löydöksistä luonnossa. AddRoleMap rekisteröi mukautetun ManualTitle-tyypin H1:ksi, ja sen ohittaminen jättää alla kuvatun diagnostiikan liputtamaan kuvaamattoman roolin
Otsikkotasot ansaitsevat harkitun käytännön, ei ad-hoc-valintoja, joita tehdään sivun näön vuoksi. Ruudunlukuohjelman käyttäjät hyppäävät osien välillä otsikkopikakomennolla, joten malli, joka menee H1:stä H3:een koska välitaso näytti liian suurelta visuaalisessa suunnittelussa, rikkoo hiljaisesti tuon navigoinnin, eikä yksikään visuaalinen tarkistus koskaan saa sitä kiinni. Se on juuri se vika, jonka HEADING-LEVEL-SKIP-diagnostiikka on olemassa nimeämään. Kukin mukautetun mallin visuaaliset tyylit kiinteään otsikkotikkaan kerran, yhdessä paikassa, ja harhailu ei koskaan ala
Taulukot, joita ruudunlukuohjelma todella voi navigoida
Piirretyt ruudukon viivat eivät tarkoita mitään ruudun ulkopuolella. Sitä mitä ruudunlukuohjelmat navigoivat, ovat rakenteelliset suhteet: mitkä solut ovat otsikoita, mitä kukin otsikko hallitsee, ja miten datasolut sitoutuvat otsikoihin epäsäännöllisissä asetteluissa. Rakenneelementin ominaisuuskutsut käsittelevät kaikki kolme:
Lib.BeginTag('Table', '', '');
Lib.BeginTag('TR', '', '');
Lib.BeginTagEx2('TH', '', '', '', '', 'col-part', '');
Lib.SetStructElemScope('Column'); // kelvollinen vain, kun tämä TH on auki
Lib.DrawText(72, 120, 'Part');
Lib.EndTag;
Lib.BeginTagEx2('TH', '', '', '', '', 'col-torque', '');
Lib.SetStructElemScope('Column');
Lib.SetStructElemColSpan(2); // otsikko ulottuu arvo- ja yksikkösarakkeiden yli
Lib.DrawText(200, 120, 'Tightening torque');
Lib.EndTag;
Lib.EndTag;
Lib.BeginTag('TR', '', '');
Lib.BeginTag('TD', '', '');
Lib.SetStructElemHeaders('col-part'); // eksplisiittinen sidonta epäsäännöllisille taulukoille
Lib.DrawText(72, 140, 'M8 flange bolt');
Lib.EndTag;
Lib.EndTag;
Lib.EndTag; // Table
Järjestyssääntö on tiukka ja vangitaan hiljaa. Jokainen SetStructElem*-kutsu kohdistuu tagiin, joka on sillä hetkellä auki, sen BeginTag:n ja EndTag:n välissä, ja se palauttaa 0 nostamatta mitään kun yksikään tagi ei ole auki tai ominaisuus ei koske nykyistä. Väärin sijoitettu kutsu yksinkertaisesti katoaa. Paluuarvojen kietominen assertteihin kehityksen aikana nappaa harhailun silloin kun vielä näet sen; jätettyinä, puuttuva näkyvyys nousee esiin vasta kun saavutettavuusauditointi ajaa todellisen ruudunlukuohjelman taulukon yli. Elementti-ID:t, jotka viedään BeginTagEx2:n läpi, ruokkivat ID-puuta (ISO 32000-1 §14.7.4), ja se on se, mikä tekee SetStructElemHeaders-sidonnasta ratkaistavissa ensinnä
Sama ominaisuusperhe kattaa loputkin, mihin avustava teknologia nojaa. SetStructElemListNumbering julistaa, miten luettelokohteet nimetään, joten ruudunlukuohjelma ilmoittaa sijainnin luettelossa luettelomerkin glyfien resitoinnin sijaan. SetStructElemBBox tallentaa kuvien ja taulukoiden rajaavan laatikon, jota uudelleenvirtausnäkymät käyttävät sisällön sijoittamiseen. SetStructElemActualText toimittaa korvaavan tekstin juoksuille, joiden glyfit eivät kuvaudu luettaviksi merkeiksi, kuten vektoritaiteesta koottuun alkukirjaimeen. Jokainen noudattaa samaa sääntöä: se sitoutuu avoimeen tagiin, tai se katoaa
Artefaktit, kieli ja pre-save-diagnostiikkaportti
Toistuva sivukaluste — eli juoksevat otsikot, taittokomerkit, vesileimat ja taustasävyt — kuuluu BeginArtifact- ja EndArtifact-sulkujen sisään, jotta se ei koskaan mene lukuvirtaan. Kieli on perittävä. Dokumentin oletus tulee SetPDFUAMode-argumentista, ja toista kieltä oleva juoksu ohittaa sen per elementti BeginTagEx:n tai SetStructElemLang:n kautta. Se on sitä, mikä pitää ranskalaisen lainauksen englanninkielisessä käsikirjassa lausuttavana
Ennen tallennusta GetPDFUADiagnostics ajaa kirjaston rakenteelliset tarkistukset muistissa olevan dokumentin yli ja palauttaa löydökset tekstinä, missä tyhjä merkkijono tarkoittaa, ettei mitään löytynyt. Koodit nimeävät klassiset laadintavirheet suoraan: FIGURE-NO-ALT kuvalle, jolla ei ole vaihtoehtoista tekstiä, HEADING-LEVEL-SKIP H3:lle H1:n jälkeen, ROLEMAP-UNMAPPED mukautetulle tyypille jota ei koskaan rekisteröity. Sido tämä buildiin (generoi dokumenttijoukko, kaada vaihe ei-tyhjällä diagnostiikalla) ja saavutettavuusregressiot muuttuvat käännösaikaisen tyylisiksi virheiksi kuukausien auditointilöydösten sijaan. Täysi vaatimustenmukaisuustuomio kuuluu yhä tallennetun tiedoston preflightille, joka on käsitelty PDF/A- ja PDF/UA-preflight Delphissä, koska jotkut normalisoinnit tehdään vain sarjallistamisen aikana
Merkintänavigoinnilla on oma säätimensä. PDF/UA odottaa, että lomakekenttien ja linkkien näppäimistöläpikäynti noudattaa rakennejärjestystä, ja SetTabOrderMode kirjoittaa sivutason sarkainjärjestys-merkinnän, jota katseluohjelmat kunnioittavat, GetTabOrderMode ollessa saatavilla saapuvien tiedostojen auditointiin. Se on sellainen vaatimus, jota kukaan ei huomaa ennen kuin vain-näppäimistö-käyttäjä tekee virheraportin, ja se maksaa yhden kutsun per dokumentti saadakseen sen oikein
Rakennepuut eivät selviä joka yhdistämisestä
Tagitetut dokumentit pysyvät tagitettuina vain kun joka myöhempi käsittelyvaihe säilyttää puun, ja PDFlibPasin sisällä terävä reuna on merge-list-perhe. MergeFileListFast vaihtaa rakennepuun säilyttämisen nopeuteen. Se on oikea vaihto skannatuille kuvaerille ja väärä tagitetuille raporteille, koska tuloste avautuu hyvin, renderöityy identtisesti ja on hiljaa menettänyt saavutettavuuskerroksensa. Käytä oletus-MergeFileList:ä tai tiukkaa varianttia aina kun yksikään syöte on tagitettu, ja tee IsTaggedPDF:stä osa kokoonpanon jälkeisiä assertteja, jotta litistetty erä ei voi lähteä ilman että joku huomaa. Suuria dokumenttijoukkoja varten kokoonpanoputket kantavat lisää tämän kaltaisia kompromisseja, joita on tutkittu suurten PDF-tiedostojen yhdistämisessä, jakamisessa ja suorassa pääsyssä
Varmistusluuppi sulkeutuu kirjaston ulkopuolella: avaa tuloste Acrobatissa, tarkista tagit-paneeli ja lue vähintään yksi dokumentti per malliperhe todellisella ruudunlukuohjelmalla. Diagnostiikka nappaa rakenteelliset virheet; vain ihmiskorva nappaa lukujärjestyksen, joka on teknisesti kelvollinen ja käytännössä hämmentävä. Evaluaatioversiot ja täydellinen tagitus-API-viite ovat losLab PDF Library for Delphi -tuotesivulla