Luo raportti, upota TrueType-fontti ja tuloste avautuu oikein jokaisessa kokeilemassasi katseluohjelmassa. Glyyfit ovat oikein, teksti on valittavissa, tiedosto on voimassa. Ainoa asia on vialla on koko. Asiakirja, jossa on käytetty muutamaa kymmentä latinalaista merkkiä, kantaa koko 350 kt:n fontin. Asiakirja, joka tulosti kappaleen kiinaa, kantaa 14 megatavun CJK-fonttia sen puolimegatavun siivun sijaan, jonka sen pitäisi tarvita. Poikkeusta ei nostettu, varoitusta ei kirjattu ylös ja tiedosto läpäisi vahvistuksen. Tältä väärässä järjestyksessä oleva viimeistelyvaihe näyttää ulkopuolelta: mikään ei epäonnistu, ja ainoa todiste on numero, joka on liian suuri
Sen tuottama vika asui HotPDF:ssä yhdellä julkaisurivillä, ja se on sittemmin korjattu. Kirjoittamisen arvoinen ei ole vikailmoituksena vaan opetuksena, koska virheen muoto on yleinen. Millä tahansa asiakirjamoottorilla on viimeistelyvaihe, joka muuntaa objektit juuri ennen niiden kirjoittamista, ja kyseisen vaiheen oikeellisuus riippuu täysin sen vaiheiden järjestyksestä suhteessa sarjoitukseen. Vie askel väärälle puolelle kirjoitusta, niin se ei tee mitään hiljaa
Mitä fontin osajoukon on tarkoitus tehdä
Alijoukkofontti on TrueType-tiedoston osa, jota asiakirja todella käyttää. ISO 32000-1 §9.9 kuvaa, kuinka upotettu fonttiohjelma kulkee kirjasinkuvaajan viittaamassa virrassa, ja TrueType-ohjelman kohdalla kyseinen virta on /FontFile2, ja /Length1 antaa pakkaamattoman tavumäärän. Alijoukko kirjoittaa glyf- ja loca-taulukot uudelleen siten, että ne sisältävät vain ne kuviot, joihin asiakirja viittaa, numeroi kuviotunnukset uudelleen ja liittää /BaseFont nimen eteen kuusikirjaimisen tunnisteen, kuten ABCDEF+ merkitsemään fontin alajoukkoksi juuri niin kuin spesifikaatio vaatii. Latinalaiset kasvot, jotka ovat alijoukon kymmeneen tai viiteentoista kilotavuun, erottavat toisistaan laihan PDF-tiedoston ja PDF-tiedoston, joka lähettää kokonaisen kirjasintyypin yhden otsikon vuoksi
Sillä, missä vaiheessa tämä tapahtuu, on merkitystä. Alijoukon muodostaminen ei ole muunnos, jota sovelletaan levyllä oleviin tavuihin. Se muokkaa muistissa olevaa objektikaaviota: se kutistaa virran /FontFile2 sisältöä, korjaa parametrin /Length1 ja kirjoittaa uudelleen merkkijonon /BaseFont. Kaiken tämän on oltava paikallaan, kun serialisaattori kävelee kuvaajan sisällä ja lähettää tavuja. Jos muokkaukset saapuvat tavujen kirjoittamisen jälkeen, ne päivittävät objekteja, joita kukaan ei koskaan lue
Oire ja miksi mikään ei valittanut
Ilmoitettu toiminta oli täysiä fontteja ulostulossa ilman diagnoosia. Käyttäjä, joka rekisteröi Unicode TrueType-fontin ja laati normaalin asiakirjan, huomasi, että upotettu fonttiobjekti oli yhtä pitkä kuin lähdetiedosto .ttf ja että nimessä /BaseFont ei ollut kuusikirjaimista alajoukon etuliitettä. Tulostus ei koskaan kutistunut sellaisten ajojen välillä, jotka käyttivät kymmentä glyyfiä, ja ajojen, jotka käyttivät kymmentä tuhatta
Mahdollisten virheiden puuttuminen tekee tästä vikaluokasta kalliin. Väärään aikaan suoritettava alajoukon rutiini toimii edelleen. Se käy läpi kertyneen koodipisteen käytön, rakentaa täydellisen oikean osajoukon ja soveltaa sitä muistissa olevaan objektikaavioon. Sisäisesti työ tehdään ja puhelu palaa puhtaana. Ainoa väärä asia on, että sen muokkaama objektikaavio ei ole enää kirjoitettava asia, koska kirjoittaja on jo valmis. Soittajan näkökulmasta asiakirja tuotettiin ja tallennettiin ilman vaaratilanteita, mikä on juuri se vaikutelma, jonka hiljainen vika antaa
Perimmäinen syy oli viimeistelyjärjestys
HotPDF:ssä sulkemistyö tapahtuu koodissa EndDoc. Alajoukkovaihe on sisäinen rutiini nimeltä BuildAndApplyUnicodeFontSubset. Se lukee asiakirjakohtaisen käytettyjen koodipisteiden joukon, joka on pidetty bittikartassa, jonka tekstin lähetyspolku täyttää kuvioiden näkyessä, kartoittaa jokaisen käytetyn koodipisteen välimuistissa olevan koodipisteestä glyyfiin -taulukon kautta todelliseksi kuviotunnisteeksi ja kirjoittaa uudelleen fonttiohjelman kyseisen sulkemisen ympärillä. Kun Unicode TrueType -kirjasin rekisteröidään, emissiopolku asettaa bitin käytetyissä koodipisteissä jokaiselle piirtämälleen hahmolle, joten kun asiakirja sulkeutuu, moottori tietää tarkalleen, mitkä glyyfit osajoukon on säilytettävä
Vika oli siinä, että koodia BuildAndApplyUnicodeFontSubset kutsuttiin sen jälkeen, kun SaveToStream tai SaveToFile oli jo särjoittanut asiakirjan. Alijoukon muokkaukset parametriin /FontFile2, sen korjattuun /Length1 ja kuusikirjaiminen /BaseFont -etuliite laskettiin kaikki sellaista objektikaaviota vastaan, joka oli jo muutettu tavuiksi. Korjaus oli yksilinjainen uudelleenjärjestely: siirrä alajoukkokutsu ennen serialisointia, jotta kirjoittaja säteilee alajoukkoon kuuluvan fontin alkuperäisen sijaan. Korjattu sekvenssi suorittaa alijoukon ensin ja sarjoittaa sen jälkeen
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansSC-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Noto Sans SC', [], 12);
Pdf.CurrentPage.TextOut(72, 760, 0, '报表标题 Report Heading');
Pdf.EndDoc; // alajoukko kulkee täällä, ennen kirjoitusta
Pdf.SaveToFile('Report.pdf');
finally
Pdf.Free;
end;
end;
Kun tilaus on korjattu, mikään kutsukoodissa ei muutu. Osajoukon määritys on oletusarvoisesti käytössä, kun Unicode TrueType-fontti on rekisteröity. Rekisteröit fontin, aloitat asiakirjan, piirrät ja lopetat sen, ja alijoukko on rakennettu kuvioista, joita käytit ennen kuin tavut poistuvat muistista
Miksi yksi väärin sijoitettu askel on kokonainen luokka
Syy siihen, miksi tämä on oppitunnin arvoinen alaviitteen sijaan, on se, että EndDoc lähettää luettelon sulkemisvaiheista ja jokainen niistä on herkkä sen sijainnille suhteessa kirjoitukseen. Fontin alajoukon määrittäminen on yksi. PDF/A-tulos vaatii /CIDSet-virran, joka luettelee tarkalleen alajoukossa olevat glyyfitunnisteet, rajoitus, jonka ISO 19005 asettaa, jotta validaattori voi vahvistaa upotetun ohjelman vastaavan fonttikuvaimen vaatimuksia. Tämä virta lähetetään samassa viimeistelyikkunassa ja riippuu siitä, että alijoukko on rakennettu ensin. PDF/UA-1 edellyttää standardin ISO 14289-1 §7.18.3 mukaan, että jokainen sivu, jossa on huomautus, ilmoittaa koodin /Tabs arvolla /S, ja sisäinen rutiini nimeltä EnsurePDFUATabsOnAnnotatedPages leimaa avaimen samassa vaiheessa. Myös tavoitetarkistukset suoritetaan siellä
Sama järjestysvirhe, joka poisti alajoukon käytöstä, pudotti myös PDF/UA-sarkainjärjestysavaimen huomautetuilla sivuilla, koska kyseinen vaihe osui samalle väärälle puolelle kirjoitusta. veraPDF ja PAC ilmoittavat puuttuvan koodin /Tabs /S Matterhorn-protokollan tarkistuspisteen 21-001 rikkomisesta. Joten yksi väärin kohdistettu puhelu ei vain paisuttanut tiedostokokoa; se rikkoi hiljaisesti esteettömyysvaatimuksen samanaikaisesti, samalla virheellä. Se on viimeistelyvaiheen vaara: sen vaiheet jakavat ennakkoehdon, ja yksittäinen tilausvirhe voi poistaa useita niistä kerralla samalla, kun jokainen puhelu tuottaa edelleen onnistumisen
Kuinka hiljainen lähetysvika todella ilmenee
Virhettä, joka ei aiheuta poikkeusta, ei jää kiinni ohjelman suorittamisesta. Se jää kiinni tarkastamalla tuotoksen ja vertaamalla sitä siihen, mitä panoksen olisi pitänyt tuottaa. Fonttien osajoukon osalta tarkastukset ovat konkreettisia. Vertaa tulostetiedoston kokoa karkeaan odotukseen: asiakirja, joka kosketti kourallista kuviota, ei saisi olla täyden kirjasintyypin kokoinen. Avaa upotettu fonttiobjekti ja lue sen tavun pituus; alajoukkoon kuuluva /FontFile2 latinalaisille kasvoille on pieni osa lähdetiedostosta. Lue /BaseFont-nimi ja varmista, että kuusikirjaiminen etuliite on olemassa, koska sen puuttuminen on suora signaali siitä, että mitään alijoukkoa ei ole käytetty
var
Pdf: THotPDF;
Output: TMemoryStream;
begin
Output := TMemoryStream.Create;
try
Pdf := THotPDF.Create(nil);
try
Pdf.RegisterUnicodeTTF('C:\Fonts\DejaVuSans.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('DejaVu Sans', [], 11);
Pdf.CurrentPage.TextOut(72, 760, 0, 'Subset me');
Pdf.EndDoc;
Pdf.SaveToStream(Output);
finally
Pdf.Free;
end;
// Muutama kuvio noin 700 kilotavun pinnalta ei saa tuottaa usean satojen kilotavujen virtaa.
if Output.Size > 100 * 1024 then
raise Exception.Create('Fontin alijoukko ei kutistanut tulostetta');
finally
Output.Free;
end;
end;
PDF/A-tulostuksen kohdalla tarkistus on vielä terävämpi, koska kelpuuttaja tekee työn puolestasi. Aseta vaatimustenmukaisuustaso ja suorita tulos veraPDF:n läpi: puuttuva /CIDSet tai alijoukko, joka ei täsmää kuvaimen kanssa, raportoidaan epäonnistuneena lausekkeena sen sijaan, että se jätettäisiin sinun huomioitavaksi silmämääräisesti. Vaatimustenmukaisuuskytkimet, jotka ohjaavat tätä viimeistelytyötä, ovat asiakirjan ominaisuuksia. PDFACompliance ottaa merkkijonon, kuten '2B' tasolle PDF/A-2 B, ja PDFUACompliance on boolen arvo, joka ottaa käyttöön merkityn PDF:n ja sarkainjärjestyksen vaatimukset
Pdf := THotPDF.Create(nil);
try
Pdf.PDFACompliance := '2B'; // PDF/A-2 Taso B, käyttää /CIDSet-päästöä
Pdf.PDFUACompliance := True; // leimaa /Tabs /S annotoituihin sivuihin
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansSC-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Noto Sans SC', [], 12);
Pdf.CurrentPage.TextOut(72, 760, 0, '合规报告');
Pdf.EndDoc;
Pdf.SaveToFile('Report_PDFA.pdf');
finally
Pdf.Free;
end;
Insinööritunti
Tästä putoaa kaksi sääntöä. Ensimmäinen on se, että jokaisen objektin muuntavan viimeistelyvaiheen on suoritettava ennen näiden objektien sarjoittamista, ja asiakirjamoottorin sulkemisvaihe on luettava järjestettynä liukuhihnana, jossa serialisointi on viimeinen toiminto, ei yksi monien joukossa. Toinen on se, joka maksoi tässä eniten aikaa: päästövaiheessa virheen puuttuminen ei ole todiste onnistumisesta. Rutiini, joka rakentaa oikean alajoukon ja soveltaa sitä väärään, jo kirjoitettuun kaavioon, ilmoittaa, ettei mitään väärää ole, koska sen omasta näkökulmasta mikään ei ollut. Vahvistuksessa on tarkasteltava artefaktia, ei palautuskoodia. Tarkista tulosteen koko, lue upotetun fontin tavun pituus ja sen etuliite /BaseFont ja anna veraPDF:n arvioida PDF/A-tulos, kun puuttuva /CIDSet muuttaa hiljaisen vajeen nimetyksi epäonnistumiseksi
Fonttien käsittelyn tuottajapuoli, kuinka kasvot rekisteröidään ja upotetaan raportin tulostukseen, käsitellään raportin tulostuksen fontteja ja kuvia koskevassa artikkelissamme. Vahvistuspuolta, jossa nämä viimeistelyvaiheet tarkistetaan standardien perusteella, käsitellään PDF/A- ja PDF/UA-vahvistusta koskevassa oppaassa. Molemmat parit muodostavat tässä kuvatun alajoukon ja vaatimustenmukaisuuden työn, joka toimitetaan osana Delphin ja C++Builderin HotPDF Component-komponenttia sekä lataus-, muokkaus-, salaus- ja allekirjoitussovellusliittymiä, joita on käsitelty muualla tässä blogissa