Če izvozite dokument iz Microsoft Worda ali Excela z možnostjo Shrani kot PDF, je datoteka na disku največkrat hibridno referencirana datoteka. Svoje informacije o navzkrižnih referencah nosi dvakrat: enkrat kot klasično tabelo s fiksno širino, ki je zaključevala vsak PDF do različice 1.4, in enkrat kot stisnjen tok navzkrižnih referenc, od katerega je dejansko odvisen večji del dokumenta. Posamezen ključ v zaključku (trailer), /XRefStm, združi ta dva pogleda, in ali orodje vidi celoten dokument, je odvisno od tega, ali sledi temu ključu
Ta članek obravnava hibridne datoteke s strani uporabe: kako izgledajo bajti na koncu datoteke, kako se dva pogleda med urejanjem razideta in kako lahko Delphi cevovod zazna ter usmeri hibridne vhode. O tem, kako nalagalnik združi ta dva pogleda in zakaj njun vrstni red ni stvar pogajanj, pišemo v našem članku o nalaganju hibridno referenciranih datotek za HotPDF; tukaj pa se bomo posvetili zgolj prepoznavanju same razporeditve struktur
Zakaj izvozi iz Office zapišejo indeks dvakrat
PDF 1.5 je uvedel dve funkciji, ki sta spremenili obliko datoteke: tokove navzkrižnih referenc (cross-reference streams), ki shranjujejo indeks objektov kot stisnjene binarne podatke namesto običajne besedilne tabele, in tokove objektov (object streams), ki zapakirajo veliko majhnih objektov v en sam s Flate algoritmom stisnjen vsebnik. Zapisovalnik, ki jih uporablja, ustvari manjše datoteke, vendar bralnik standarda PDF 1.4 rezultata ne more odpreti, saj strukture, na katere se zanaša — ključna beseda xref in slovar za trailer — ne obstajajo več
ISO 32000-1 §7.5.8.4 opredeljuje kompromis. Hibridno referencirana datoteka zapiše oboje: klasično tabelo navzkrižnih referenc, ki naslavlja objekte, ki jih mora doseči star bralnik, med njimi katalog in drevo strani, ter tok navzkrižnih referenc, ki indeksira vse ostalo. Objekti, združeni v tokovih objektov, so v klasični tabeli označeni kot prosti, tako da jih bralnik 1.4 preskoči brez pritoževanja; njihove prave lokacije obstajajo samo v toku. Klasičen trailer nato nosi ključ /XRefStm, ki vsebuje bajtni odmik tega toka. Star pregledovalnik nikoli ne prebere tega ključa in datoteko upodobi iz pogleda tabele. Sodoben pregledovalnik pa mu sledi in vidi celoten dokument. Word in Excel sta točno takšno strukturo izdajala že leta, zato hibridne datoteke niso zgolj nek eksotičen redek primer, temveč velik delež tega, kar prejemajo poslovni cevovodi
Kako izgleda konec hibridne datoteke
Postavitev je najlažje razumeti iz bajtov. Tukaj je konec majhne hibridne datoteke z okrajšanimi bajtnimi odmiki; v pravem izvozu iz Officea je vrednost /XRefStm običajno velik odmik blizu konca datoteke. Vrstni red branja je od zadaj naprej, kot je opisano v našem pregledu strukture PDF datoteke: poišči %%EOF, preberi startxref in skoči na tabelo
% ... body objects, including object streams and, at byte 116,
% the cross-reference stream (a stream object with /Type /XRef) ...
xref % classic section: what startxref points at
0 4
0000000000 65535 f % slot 0: head of the free list, always present
0000000017 00000 n % object 1: the catalog, visible to any reader
0000000000 65535 f % object 2: marked free -- lives in an object stream
0000000000 65535 f % object 3: same; only the stream view locates it
trailer
<<
/Size 4
/Root 1 0 R
/XRefStm 116 % byte offset of the cross-reference stream
>>
startxref
7164 % byte offset of the 'xref' keyword above
%%EOF
Dve podrobnosti v tem izpisu poganjata celoten mehanizem. Prvič, startxref namenoma kaže na klasični odsek: to je naslov, na katerem mora pristati star bralnik. Tok navzkrižnih referenc je dosegljiv samo prek ključa /XRefStm v slovarju trailerja, zato razčlenjevalnik, ki nikoli ne išče tega ključa, sploh ne izve, da tok obstaja. Drugič, objekta 2 in 3 sta nenevarna laž. Klasična tabela ju razglaša za prosta (free), čeprav sta to resnična objekta, ki se nahajata znotraj stisnjenega vsebnika; oznaka za prosto je tista, ki bralniku različice 1.4 preprečuje, da bi se spotaknil ob vnose, ki jih ne more uporabiti. Porabnik, ki zaupa zgolj klasičnemu pogledu, sklepa, da večina tega dokumenta sploh ne obstaja
Kako se dva pogleda razideta
Hibridna datoteka neposredno iz Worda je notranje dosledna: oba pogleda opisujeta isti dokument, vsak v svojem deklariranem obsegu. Težave se začnejo, ko datoteko uredi orodje, ki razume samo enega od pogledov. Pomislite na pripomoček za žigosanje, ki doda postopno posodobitev v klasičnem slogu: nove objekte, nov xref odsek, /Prev verigo do prejšnjega odseka in nov trailer. Če ta trailer opusti ključ /XRefStm, postane pogled toka osirotel; če pa prekopira staro vrednost naprej, pogled toka še vedno opisuje dokument takšen, kot je bil pred urejanjem. V obeh primerih se zdaj indeksoma ne ujemata več glede tega, kaj datoteka vsebuje
Nastala datoteka ima značilen vzorec napak: objekti, ki so vidni v enem pogledu, manjkajo ali pa so zastareli v drugem. Bralnik, ki razrešuje poglede prek toka, najde različico posodobljenega objekta pred urejanjem ali pa sploh ne najde vnosa za dodanega. Bralnik, ki se zanaša na pogled tabele, vidi urejanje, a izgubi sled za stisnjenimi objekti, ki jih locira izključno tok. V praksi se to pokaže kot polja obrazcev, ki preživijo v enem pregledovalniku in izginejo v drugem, kot opombe, za katere se zdi, da jih je prehod za žigosanje izbrisal, ali pa kot iskanja, ki pristanejo na popolnoma napačnem objektu
Odkrivanje napak v teh datotekah je zelo drago, ker jih Adobe Acrobat običajno odpre brez pritoževanja: ko se indeks ne ujema z bajti, po tihem obnovi podatke o navzkrižnih referencah s skeniranjem glav objektov, tako da tisti, ki je ustvaril pokvarjeno datoteko, ne opazi nič narobe. Napaka se pokaže šele kasneje, ko datoteka doseže strogega porabnika, orodje za preverjanje pred tiskom, storitev za podpisovanje ali postopek vnosa v arhiv, ki zaupa deklarirani strukturi in javi manjkajoče objekte ali neujemajoče se navzkrižne reference. "V Acrobatu se odpre brez težav" – tako se začne skoraj vsak prijavljen primer hibridne desinchronizacije
Zaznavanje hibridne datoteke v navadnem Delphiju
Razvrščanje vhodnih datotek ne zahteva PDF knjižnice. Ključ /XRefStm se lahko pojavi le znotraj slovarja klasičnega trailerja, aktivni trailer pa se nahaja v zadnjih nekaj kilobajtih datoteke, saj specifikacija zahteva, da se %%EOF pojavi blizu fizičnega konca. Branje omejenega okna na koncu datoteke in njegovo iskanje zadostuje za triažo:
uses
System.SysUtils, System.Classes, System.StrUtils, System.Math;
function IsHybridReferencePdf(const FileName: string): Boolean;
const
TailWindow = 2048;
var
Stream: TFileStream;
Buf: TBytes;
Tail: string;
Len, TrailerPos, NextPos, KeyPos, StartXrefPos: Integer;
begin
Result := False;
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
if Stream.Size < 48 then
Exit;
Len := Min(TailWindow, Integer(Stream.Size));
SetLength(Buf, Len);
Stream.Position := Stream.Size - Len;
Stream.ReadBuffer(Buf[0], Len);
finally
Stream.Free;
end;
// Every keyword involved is 7-bit ASCII, so a byte-wise decode is safe
Tail := TEncoding.ANSI.GetString(Buf);
// Find the LAST 'trailer' keyword: with incremental updates,
// the newest trailer is the one that governs the file
TrailerPos := 0;
NextPos := Pos('trailer', Tail);
while NextPos > 0 do
begin
TrailerPos := NextPos;
NextPos := PosEx('trailer', Tail, NextPos + 1);
end;
if TrailerPos = 0 then
Exit; // no classic trailer: a pure xref-stream file, not hybrid
// A hybrid trailer carries /XRefStm between 'trailer' and 'startxref'
KeyPos := PosEx('/XRefStm', Tail, TrailerPos);
StartXrefPos := PosEx('startxref', Tail, TrailerPos);
Result := (KeyPos > 0) and
((StartXrefPos = 0) or (KeyPos < StartXrefPos));
end;
Trije izidi se ujemajo s tremi postavitvami. Samo klasična datoteka ima trailer, vendar nima ključa /XRefStm: False. Datoteka, ki se v celoti zanaša na tokove navzkrižnih referenc, sploh nima ključne besede trailer, njeni ključi trailerja se nahajajo v slovarju toka: prav tako False, kar je pravilno, saj je takšna datoteka stisnjena in ne hibridna. Samo dvojno indeksirana postavitev vrne True
Za produkcijsko uporabo sta smiselni dve izboljšavi, ki sta vredni dodatnih vrstic kode. Razčlenite celo število za ključem /XRefStm, skočite na ta odmik in preverite, ali se tam dejansko nahaja objekt toka z /Type /XRef; odrezana datoteka lahko še vedno vsebuje ta ključ, čeprav toka ni več, kar spada v drugačno kategorijo kot zdrava hibridna datoteka. Velikost okna obravnavajte kot parameter: 2 KB pokrijeta običajen izvoz iz Officea, toda nenavadno velik slovar trailerja lahko potisne ključno besedo izven tega dosega, razširitev okna pa je boljša rešitev kot to, da bi datoteko po nesreči razglasili za klasično
Usmerjanje hibridnih datotek skozi Delphi cevovod
Zaznavanje vam omogoča odločitev o usmerjanju. Za datoteke, ki se samo berejo, upodabljajo ali potrjujejo, uporabite nalagalnik, ki razreši oba pogleda, nato pa raje preverite obnašanje kot pa same bajte. Komponenta PDFium med nalaganjem razčleni verigo /XRefStm, tako da je tabela objektov, ki jo vidi vaša koda, združena različica in preverjanja, opisana v našem članku o potrjevanju objektov in tokov navzkrižnih referenc, ostajajo nespremenjena. Če je desinchronizirana hibridna datoteka tako močno poškodovana, da zavrne nalaganje, mehanizem to javi prek svojega nabora napak: FPDF_ERR_SUCCESS, FPDF_ERR_UNKNOWN, FPDF_ERR_FILE, FPDF_ERR_FORMAT, FPDF_ERR_PASSWORD, FPDF_ERR_SECURITY in FPDF_ERR_PAGE, pri čemer strukturna poškodba ustvari FPDF_ERR_FORMAT. Kljub temu se ne zanašajte samo na ta signal: PDFium je načrtno prizanesljiv in tiho obnovi večino nedoslednih datotek, zato uspešno nalaganje dokazuje le, da je bilo datoteko mogoče rešiti, ne pa tudi, da se njena pogleda ujemata. Smiselno preverjanje doslednosti je primerjava tistega, kar najde celoten pregled objektov, z vrednostjo, ki jo deklarira /Size v trailerju
Za datoteke, ki jih vaš cevovod spreminja, je najvarnejša strategija ta, da prenehajo biti hibridne. Nalaganje, ki mu sledi celovito shranjevanje prek HotPDF, ponovno zapiše dokument z eno samo, notranje dosledno navzkrižno referenco v eni obliki: brez /XRefStm in brez drugega pogleda, ki bi lahko postal nesinhroniziran; vsak objekt si lasti natanko en vnos v indeksu. Takšna normalizacija je tisto, kar želite pred vnosom v arhiv, pred strogim nadaljnjim RIP procesiranjem ali storitvijo za podpisovanje, in po vsakem urejanju, uporabljenem na hibridnem vhodu. To deluje zato, ker je nalagalnik na poti navznoter pravilno združil poglede – mehanizem, ki ga podrobno opisuje članek o hibridnih referencah v HotPDF
Edini razred datotek, ki jih pustite pri miru, so digitalno podpisani dokumenti. Celovito ponovno zapisovanje premakne vsak bajt, kar razveljavi vsak podpis, izračunan na podlagi prvotnih obsegov. Sprememba na podpisanem hibridu mora biti izvedena kot ustrezna postopna posodobitev, ki ohrani oba pogleda; datoteka, ki jo je treba le prebrati, bi morala ostati nedotaknjena. Normalizacija je namenjena datotekam, ki so v vaši lasti; podpisanim datotekam pa lahko stvari samo dodajate
Hibridno referencirani PDF-ji niso slabo oblikovani; so oblika lastnega mostu združljivosti formata, Office aplikacije pa jih bodo še naprej ustvarjale, dokler bodo v bazi namestitev preživeli bralniki različice PDF 1.4. Cevovod, ki lahko opazi ključ /XRefStm, preveri združen dokument s komponento PDFium Component in ponovno ustvari čist izhod z enim samim indeksom s pomočjo komponente HotPDF Component, jih obravnava takšne, kot so: kot običajne vnose z enim dodatnim smerokazom v trailerju