veraPDF izvještaj koji kaže da se širina glifa ne slaže s ugrađenim font programom ne govori vam gotovo ništa o tome koji je glif ni zašto. PDFlibPas na to pitanje odgovara tako što svaki znakovni kod razriješi kroz ugrađeni cmap do indeksa glifa, normalizira metriku programa na 1000 jedinica po emu, i uspoređuje tamo
Zašto se širine glifova ne slažu?
Jer dva uspoređivana broja žive u različitim koordinatnim sustavima, i ništa u PDF rječniku ne govori konverziju. Font rječnik piše /Widths u prostoru glifova, koji PDF fiksira na tisućiti dio ema (ISO 32000-1 §9.2.4). Tablica hmtx unutar ugrađenog TrueType programa piše napredovanja u jedinicama dizajna fonta, i tablica head odlučuje koliko ih čini em: 2048 za većinu TrueType pisama, 1000 za CFF-izvedena, povremeno nešto sasvim drugo. Usporedite sirove vrijednosti i svaki 2048-upem font u vašem korpusu izgleda pokvaren. To je zamka koju ISO 14289-1 §7.21.5 postavlja svakome tko pokuša revidirati širine čitajući polja rječnika
PDFlibPas normalizira pri učitavanju. TPDFTrueTypeParser sprema Advance * 1000 div unitsPerEm u svoje polje širina, pa Parser.GetWidth(GID) već odgovara u istim tisućitima ema koje koristi PDF, a GetRawWidth ostaje dostupan kad vam trebaju jedinice dizajna nazad. To još ostavlja težu polovicu: doći od znakovnog koda do indeksa glifa. Za jednostavni TrueType font put ovisi o Symbolic zastavici u FontDescriptoru, bit 3 od /Flags
Parser := TPDFTrueTypeParser.Create;
try
Parser.LoadFromString(FontProgram);
if Symbolic then
begin
// Simbolična pisma adresiraju se izravno kroz cmap programa,
// s (3,0) konvencijom visokog bajta kao rezervnim putem
GID := Parser.GetGlyphIndex(Code);
if GID = 0 then
GID := Parser.GetGlyphIndex($F000 + Code);
end
else
begin
// Nesimbolično: kod -> ime glifa preko kodiranja, ime -> Unicode
// preko Adobe Glyph List, Unicode -> GID preko cmap programa
UnicodeValue := GetGlyphUnicode(EncodingNames[Code and $FF]);
if UnicodeValue = 0 then
Continue;
GID := Parser.GetGlyphIndex(UnicodeValue);
end;
if (GID > 0) and (GID < Parser.GlyphCount) then
if Abs(PDFWidth - Parser.GetWidth(GID)) > 1 then
Inc(MismatchCount);
finally
Parser.Free;
end;
Dvije pojedinosti u tom ulomku nose težinu. Tolerancija je jedna jedinica, a ne nula, jer je normalizacija cjelobrojno dijeljenje i zakonito proizvedena datoteka može odstupiti za jedinicu; to je točno formulacija "unutar jednog tisućitog ema" koju dijagnostika 10036 izvještava. I čuvar GID < Parser.GlyphCount nije dekoracija. GetWidth napisan je da bude popustljiv prema pozivateljima prikazivanja, steže indeks izvan raspona na zadnju stavku u hmtx i vraća 750 kad tablice nema. Popustljivo je ispravno za prikazivanje i pogrešno za reviziju, pa revizija odbacuje indeks prije nego što zatraži širinu umjesto da vjeruje stezanju
CIDFontType2 dodaje još jedno posredovanje
PDFlibPas prolazi složene fontove na isti način, s /CIDToGIDMap umetnutim između CID-a i glifa. Širine stižu u /W polju, koje ISO 32000-1 §9.7.4.3 daje u dva oblika koja se slobodno izmjenjuju u jednom polju: početni CID iza kojega slijedi polje uzastopnih širina, ili prvi CID, zadnji CID i jedna širina primijenjena na cijeli raspon. Revizija parsira oba, zatim svaki nastali par predaje istoj usporedbi i izvještava ukupno pod dijagnostikom 10037. Korak mapiranja je mjesto gdje se složeni fontovi razlikuju, i zato dijagnostika 10021 za nedostajuću mapu važi prije nego što pročitate bilo koju širinu — odsutan ili deformiran /CIDToGIDMap ne krši samo §7.21.3.2, on pitanje širine čini neodgovorivim
// /CIDToGIDMap je ime /Identity ili tok big-endian 16-bitnih
// indeksa glifova, jedan po CID-u (ISO 32000-1 odjeljak 9.7.4.2)
Obj := DerefIndRef(FDoc, CIDFont.FindValueByKeyName('CIDToGIDMap'));
if (Obj is TPDFName) and (TPDFName(Obj).Name = 'Identity') then
begin
GID := CID;
Result := True;
end
else if Obj is TPDFStream then
begin
Data := TPDFStream(Obj).GetDecodedStream;
P := CID * 2 + 1; // Pascal nizovi indeksiraju se od 1
if (P >= 1) and (P + 1 <= Length(Data)) then
begin
GID := (Integer(Byte(Data[P])) shl 8) or Integer(Byte(Data[P + 1]));
Result := True;
end;
end;
Što bi revizor trebao učiniti kad se font program ne može dekodirati?
Ništa ne reći. Provjere potpunosti /CharSet i /CIDSet koje ISO 14289-1 §7.21.4.2 traži — dijagnostika 10038 i 10039 — mjesto su gdje se previše revan verificiratelj pretvara u teret, jer izvještaj "vaš CharSet je nepotpun" osobi koja ga čita nije razlučiv od "naš Type 1 dekoder se predao". PDFlibPas zato izvještava nedostajuću stavku samo kad tri stvari uspiju: font program se dekodira, mapiranje koda u glif se razriješi, i sam se skup dekodira. TPDFType1Decoder.LoadPFBFromString mora vratiti True i dati broj charstringova prije nego što se bilo koje ime glifa provjeri protiv /CharSet niza; /CIDSet put treba da se tok dekomprimira i da se broj glifova vrati pozitivan prije ispitivanja jednog jedinog bita. Svaka iznimka usput sažme se u "bez nalaza", a ne u nedostatak
To je namjerna pristranost prema lažno negativnim nalazima, i vrijedi je izreći otvoreno, a ne zakopati. Pokvarena CFF tablica, nepodržana Type 1 varijanta ili /CIDSet kraći od raspona glifova proizvode tišinu umjesto dijagnostike. Obrazloženje jest da se PDF/UA revizije prosljeđuju autorima koji nisu gradili alate, i lažna optužba košta više od promašene: autor sagori dan dokazujući da usklađena datoteka jest usklađena i prestaje vjerovati cijelom izvještaju. Matterhorn Protocol istu razliku čini u drugom obliku kad razdvaja provjere koje stroj može odlučiti od provjera koje mora čovjek, i njegova Fonts kontrolna točka (31) mjesto je gdje ovo živi. Ako trebate strože čitanje, pustite PDFlibPas kao brzu kapiju i namjenski verificiratelj kao drugo mišljenje — taj par opisan je u prošetnji PDF/A i PDF/UA preflightom
Stranični /Contents popis je, a ne tok
Najskuplja pojedinačna pogreška u reviziji tokova sadržaja jest tretirati /Contents kao jedan tok. ISO 32000-1 §7.7.3.3 dopušta stranici držati polje tokova čija je konkatenacija, s razmakom između dijelova, stranični program; proizvođači režu na proizvoljnim mjestima, i BT može sjediti u jednom članu sa svojim odgovarajućim ET u sljedećem. Procesor sadržaja drži stanje — dubinu gniježđenja označenog sadržaja, font odabran posljednjim Tf, zastavicu tekst objekta — i Process to stanje resetira pri ulasku. Pozovite ga jednom po članu polja i svaki tok nakon prvog kreće bez trenutačnog fonta, pa tekst koji je bio savršeno označen čita se kao neoznačeni šum bez fonta. PDFlibPas najprije konkatenira pa jednom obrađuje
function ContentObjectData(FDoc: TSmartPDFDocument; Obj: TPDFObject): AnsiString;
var
I: Integer;
begin
Result := '';
Obj := DerefIndRef(FDoc, Obj);
if Obj is TPDFStream then
Result := TPDFStream(Obj).GetDecodedStream
else if Obj is TPDFArray then
for I := 0 to TPDFArray(Obj).Count - 1 do
Result := Result + ContentObjectData(FDoc, TPDFArray(Obj).Item[I]) + #10;
end;
// Jedan Process poziv nad cijelom konkatenacijom, nikad jedan poziv po članu
Scanner.Process(ContentObjectData(FDoc, PageDict.FindValueByKeyName('Contents')));
Koji Form XObjects stvarno računaju se kao nestrukturirani?
Samo oni koje stranica stvarno pozove, s pozivnog mjesta izvan označenog sadržaja, i čiji vlastiti sadržaj pokazuje tekst. Dijagnostika 10040 provodi ISO 14289-1 §7.20 bilježeći tri neovisne činjenice po broju objekta — ima tekst, bio je pozvan, bio je pozvan unutar označenog sadržaja — i izvještava samo presjek prvih dviju umanjen za treću. Svaka od te dvije prečice pogrešna je na način koji biste isporučili: označiti svaki Form s tekstom u /Resources kazni knjižnicu predložaka iz koje nitko ne crta, a označiti svaki pozvani Form kazni vektorske logotipe koji ne nose tekst i ne trebaju označavanje. Mjesto poziva razrješuje se brojem objekta, a ne imenom resursa, jer se isti Form redovito dohvaća različitim imenima na različitim stranicama. Prateća dijagnostika 10041 prolazi isti konkatenirani program za §7.21.8, razrješujući svaki operand koji pokazuje tekst kroz font u opsegu i brojeći kodove koji padnu na .notdef, što je zabranjeno bez obzira na tekstni način prikazivanja — uključujući nevidljivi način korišten iza skeniranih slika. Kako preživjele Form objekte treba umotati pitanje je stabla strukture, obrađeno u članku o izgradnji tagged PDF strukture
Fontovi bez ikakvog FontDescriptora
Neugrađen font valjan je ulaz ove revizije, a ne stanje pogreške, i svaki pomoćnik ispod provjere ugrađivanja mora ga preživjeti. Kad PDFlibPas ne nađe /FontDescriptor, ili deskriptor bez FontFile, FontFile2 ili FontFile3, bilježi dijagnostiku 10020 — ili 10022 kad je ime jedno od Standard 14, koje §7.21.4 NOTE 5 oštroumno odbija izuzeti — i zatim nastavlja kroz ostatak datoteke. U tom je poanta izvještaja: autor želi svaki nalaz u jednom prolazu, a ne jedan nalaz po pokretanju. Zato referenca deskriptora predana pomoćnicima širine, cmap, CharSet i CIDSet može biti Nil, i svaki od njih to ispituje pri ulasku umjesto da pretpostavi da je ranija provjera prekinula reviziju. Ako je popravak ugraditi ono što nedostaje, mehanika je u napomeni o ugrađivanju nedostajućih fontova u postojeći PDF
Pokretanje revizije
Jedan poziv, nad datotekom koju niste nužno proizveli. TPDFlib.CheckFileCompliance uzima birač usklađenosti — 2 za PDF/UA-1 pod ISO 14289-1:2014 — i vraća nulu ili handle liste znakovnih nizova čije su stavke brojčani kod, dvotočka i čitljiva poruka. Nalazi o fontovima i tokovima sadržaja o kojima ovdje raspravljamo zauzimaju 10020 do 10041 u tom rasponu, brojčano odvojeni od 00xxx PDF/A kodova da miješani dnevnik ostane čitljiv. Prosljeđivanje 1 u Options kratko spoji na prvom nalazu, što želite u build kapiji, a ne u alatu za autorstvo. Za dokument još otvoren u memoriji, GetPDFUADiagnostics izvodi istovjetni pregled bez zaokruživanja preko diska
var
Issues, Count, I: Integer;
begin
// ComplianceTest = 2 bira PDF/UA-1; Options = 0 izvještava svaki nalaz
Issues := PDF.CheckFileCompliance('delivery.pdf', '', 2, 0);
if Issues = 0 then
WriteLn('delivery.pdf: PDF/UA-1 conformant')
else
begin
Count := PDF.GetStringListCount(Issues);
for I := 1 to Count do
WriteLn(' ', PDF.GetStringListItem(Issues, I)); // npr. 10037 CIDFontType2 ...
end;
end;
Ništa od ovoga ne treba vanjsku verificirateljsku binarnu datoteku na mašini, a to je razlika između provjere koja izvodi na svakom buildu i provjere koja izvodi kad se netko sjeti. API-ji usklađenosti i dijagnostike opisani ovdje stižu u standardnoj PDFlibPas Delphi PDF Library, čija stranica proizvoda nosi potpunu tablicu dijagnostičkih kodova za PDF/UA-1 uz PDF/A, PDF/X i PDF/E testne skupove