Tehnički članak

Rukovanje hibridno-referentnim PDF-ovima iz Office aplikacija u Delphiju

Izvezite dokument iz programa Microsoft Word ili Excel opcijom Spremi kao PDF i datoteka na disku je, češće nego ne, hibridno-referentna datoteka. Ona nosi svoje informacije o unakrsnim referencama dvaput: jednom kao klasičnu tablicu fiksne širine koja je završavala svaki PDF do verzije 1.4, i jednom kao komprimirani tok unakrsnih referenci o kojem zapravo ovisi veći dio dokumenta. Jedinstveni ključ u traileru, /XRefStm, povezuje ova dva prikaza, i hoće li alat vidjeti cijeli dokument svodi se na to slijedi li taj ključ

Ovaj članak promatra hibridne datoteke sa strane potrošnje: kako izgledaju bajtovi na kraju datoteke, kako se dva prikaza razilaze tijekom uređivanja, te kako Delphi cjevovod može otkriti i usmjeriti hibridne unose. Kako učitavač spaja prikaze i zašto redoslijed nije podložan pregovorima predmet je našeg HotPDF članka o učitavanju hibridno-referentnih datoteka; ovaj se bavi prvenstveno prepoznavanjem samog rasporeda

Zašto Office izvozi dvaput pišu indeks

PDF 1.5 uveo je dvije značajke koje su promijenile oblik datoteke: tokove unakrsnih referenci, koji pohranjuju indeks objekata kao komprimirane binarne podatke umjesto kao tekstualnu tablicu, te tokove objekata, koji pakiraju brojne male objekte u jedan Flate-komprimirani spremnik. Pisač koji ih koristi proizvodi manje datoteke, ali čitač PDF 1.4 verzije ne može otvoriti rezultat jer su strukture na koje se oslanja, xref ključna riječ i trailer rječnik, nestale

ISO 32000-1 §7.5.8.4 definira kompromis. Hibridno-referentna datoteka zapisuje oboje: klasičnu tablicu unakrsnih referenci koja adresira objekte do kojih stari čitač mora doći, među njima katalog i stablo stranica, te tok unakrsnih referenci koji indeksira sve ostalo. Objekti složeni u tokove objekata označeni su kao slobodni u klasičnoj tablici, pa ih 1.4 čitač preskače bez žalbi; njihove stvarne lokacije postoje samo u toku. Klasični trailer zatim nosi ključ /XRefStm koji sadrži bajtni pomak tog toka. Stari preglednik nikada ne čita ključ i iscrtava datoteku iz tabličnog prikaza. Moderni preglednik ga slijedi i vidi cijeli dokument. Word i Excel godinama emitiraju upravo ovakav raspored, zbog čega hibridne datoteke nisu egzotični rubni slučaj već veliki udio onoga što poslovni cjevovodi primaju

PDF: rep PDF-a s hibridnom referencom gdje stariji čitač vjeruje klasičnoj xref tablici dok moderni čitač slijedi /XRefStm do kompresiranog prikaza toka
Jedan trailer ključ odlučuje pogled: klasična tablica opslužuje stare čitače, dok /XRefStm moderne čitače predaje streamu koji locira sve ostalo

Kako izgleda kraj hibridne datoteke

Raspored je najlakše razumjeti iz bajtova. Ovdje je kraj male hibridne datoteke sa skraćenim pomacima; u stvarnom Office izvozu vrijednost /XRefStm je tipično veliki pomak blizu kraja datoteke. Redoslijed čitanja je hod od kraja prema početku opisan u našem pregledu strukture PDF datoteke: pronađi %%EOF, pročitaj startxref, skoči na tablicu

% ... tijela objekata, uključujući tokove objekata i na bajtu 116,
% tok unakrsnih referenci (stream objekt s /Type /XRef) ...

xref                    % klasični odjeljak: na što pokazuje startxref
0 4
0000000000 65535 f      % utor 0: glava popisa slobodnih objekata, uvijek prisutna
0000000017 00000 n      % objekt 1: katalog, vidljiv svakom čitaču
0000000000 65535 f      % objekt 2: označen kao slobodan -- nalazi se u toku objekata
0000000000 65535 f      % objekt 3: isto; samo ga prikaz toka može locirati
trailer
<<
  /Size 4
  /Root 1 0 R
  /XRefStm 116          % pomak bajtova toka unakrsnih referenci
>>
startxref
7164                    % pomak bajtova gornje ključne riječi 'xref'
%%EOF

Dva detalja u ovom ispisu nose cijeli mehanizam. Prvo, startxref namjerno pokazuje na klasični odjeljak: to je adresa na koju stari čitač mora sletjeti. Tok unakrsnih referenci dostupan je samo putem ključa /XRefStm unutar rječnika trailera, tako da parser koji nikada ne traži taj ključ nikada i ne sazna da tok postoji. Drugo, objekti 2 i 3 su laži benigne vrste. Klasična tablica ih deklarira slobodnima, ali to su stvarni objekti koji sjede unutar komprimiranog spremnika; oznaka slobodnog mjesta je ono što sprječava 1.4 čitača da se spotakne o unose koje ne može koristiti. Potrošač koji vjeruje samo klasičnom prikazu zaključuje da veći dio ovog dokumenta ne postoji

Kako se dva prikaza razilaze

Hibridna datoteka svježe proizašla iz Worda interno je dosljedna: oba prikaza opisuju isti dokument, svaki unutar svog deklariranog opsega. Problemi počinju kada datoteku uređuje alat koji razumije samo jedan od prikaza. Uzmite u obzir uslužni program za pečatiranje koji dodaje inkrementalno ažuriranje u klasičnom stilu: nove objekte, novi xref odjeljak, lanac /Prev do prethodnog odjeljka i novi trailer. Ako taj trailer odbaci ključ /XRefStm, prikaz toka postaje napušten; ako staru vrijednost kopira naprijed, prikaz toka i dalje opisuje dokument onakvim kakav je bio prije uređivanja. U svakom slučaju, dva indeksa se sada ne slažu oko toga što datoteka sadrži

Rezultirajuća datoteka ima osebujan potpis kvara: objekti vidljivi u jednom prikazu nedostaju ili su zastarjeli u drugom. Čitač koji razrješava putem prikaza toka pronalazi staru verziju ažuriranog objekta, ili uopće ne nalazi unos za dodani objekt. Čitač u tabličnom prikazu vidi uređivanje, ali gubi trag o komprimiranim objektima koje locira isključivo tok. U praksi se to pojavljuje kao polja obrasca koja prežive u jednom pregledniku, a nestanu u drugom, kao bilješke za koje se čini da ih je prolaz pečatiranja izbrisao, ili pretrage koje završe na potpuno pogrešnom objektu

Ono što otkrivanje pogrešaka kod ovih datoteka čini skupim jest to što ih Adobe Acrobat obično otvara bez prigovora: kada se indeks ne slaže s bajtovima, on tiho obnavlja podatke unakrsnih referenci skenirajući zaglavlja objekata, pa tko god je proizveo oštećenu datoteku ne vidi ništa sporno. Kvar izlazi na površinu kasnije, kada datoteka stigne do strogog potrošača, preflight validatora, usluge potpisivanja, zadatka arhivskog unosa, koji vjeruje deklariranoj strukturi i prijavljuje nedostajuće objekte ili nepodudaranje unakrsnih referenci. "Otvara se u redu u Acrobatu" tako započinje gotovo svaki ticket vezan za hibridnu desinhronizaciju

Otkrivanje hibridne datoteke u običnom Delphiju

Klasifikacija unosa ne zahtijeva PDF biblioteku. Ključ /XRefStm može se pojaviti samo unutar rječnika klasičnog trailera, a aktivni trailer nalazi se unutar zadnjih nekoliko kilobajta datoteke, jer specifikacija zahtijeva da se %%EOF pojavi blizu fizičkog kraja. Čitanje ograničenog repnog prozora i njegovo pretraživanje dovoljno je za trijažu:

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;

  // Svaka uključena ključna riječ je 7-bitni ASCII, pa je dekodiranje bajt-po-bajt sigurno
  Tail := TEncoding.ANSI.GetString(Buf);

  // Pronađi ZADNJU ključnu riječ 'trailer': kod inkrementalnih ažuriranja,
  // najnoviji trailer je onaj koji upravlja datotekom
  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;  // nema klasičnog trailera: čista xref-stream datoteka, nije hibridna

  // Hibridni trailer nosi /XRefStm između 'trailer' i 'startxref'
  KeyPos := PosEx('/XRefStm', Tail, TrailerPos);
  StartXrefPos := PosEx('startxref', Tail, TrailerPos);
  Result := (KeyPos > 0) and
    ((StartXrefPos = 0) or (KeyPos < StartXrefPos));
end;

Tri ishoda poklapaju se s tri rasporeda. Isključivo klasična datoteka ima trailer, ali nema /XRefStm: False. Datoteka koja se u potpunosti oslanja na tokove unakrsnih referenci uopće nema ključnu riječ trailer, njeni ključevi trailera žive u rječniku toka: također False, što je ispravno jer je takva datoteka komprimirana, a ne hibridna. Samo dvostruko indeksirani raspored vraća True

Delphi dijagram odluke koji pretražuje rep datoteke za posljednji trailer i /XRefStm pa usmjerava hibridne PDF-ove prema provjeri, normalizaciji ili rukovanju samo-dodavanjem
Ograničeno pretraživanje repa daje tri presude, i samo dvostruko-indeksirani slučaj nastavlja kao pravi hibrid

Za produkcijsku upotrebu, dva ojačanja vrijedna su dodatnih linija koda. Raščlanite cijeli broj iza /XRefStm, skočite na taj pomak i potvrdite da tamo doista stoji objekt toka s /Type /XRef; skraćena datoteka može nositi ključ iako toka više nema, što spada u drugačiju kategoriju od zdravog hibrida. Veličinu prozora pak tretirajte kao parametar: 2 KB pokriva običan Office izlaz, ali neuobičajeno veliki rječnik trailera može izbaciti ključnu riječ izvan dosega, a proširenje prozora je bolje rješenje od slučajnog proglašavanja datoteke klasičnom

Usmjeravanje hibridnih datoteka kroz Delphi cjevovod

Otkrivanje vam kupuje odluku o usmjeravanju. Za datoteke koje se samo čitaju, iscrtavaju ili provjeravaju, upotrijebite učitavač koji razrješava oba prikaza, a zatim provjerite ponašanje, a ne bajtove. PDFium komponenta parsira /XRefStm lanac tijekom učitavanja, pa je tablica objekata koju vaš kod vidi spojena tablica, a provjere opisane u našem članku o provjeri valjanosti objektnih i unakrsno-referentnih tokova primjenjuju se nepromijenjene. Ako je desinhronizirani hibrid dovoljno oštećen da odbije učitavanje, engine to prijavljuje kroz svoj skup pogrešaka, FPDF_ERR_SUCCESS, FPDF_ERR_UNKNOWN, FPDF_ERR_FILE, FPDF_ERR_FORMAT, FPDF_ERR_PASSWORD, FPDF_ERR_SECURITY i FPDF_ERR_PAGE, pri čemu je FPDF_ERR_FORMAT onaj koji nastaje zbog strukturnih oštećenja. Ipak, nemojte se oslanjati na taj signal: PDFium je popustljiv po dizajnu i tiho obnavlja većinu nedosljednih datoteka, pa uspješno učitavanje dokazuje tek da je datoteka bila popravljiva, a ne i da se njena dva prikaza slažu. Smislena provjera dosljednosti je usporedba onoga što pronalazi cjeloviti prolaz kroz objekte u odnosu na ono što deklarira /Size u traileru

Za datoteke koje vaš cjevovod mijenja, najsigurnija politika je spriječiti ih da uopće ostanu hibridne. Učitavanje praćeno potpunim spremanjem putem HotPDF-a prepisuje dokument s jedinstvenom, interno dosljednom unakrsnom referencom u jednom obliku: bez /XRefStm, bez drugog prikaza koji može ispasti iz sinhronizacije, svaki objekt posjeduje točno jedan indeksni unos. Takva normalizacija je ono što želite prije arhivskog unosa, prije strogog nizvodnog RIP-a ili usluge potpisivanja, i nakon svakog uređivanja primijenjenog na hibridni unos. Funkcionira jer je učitavač ispravno spojio prikaze prilikom ulaza, mehanizam koji HotPDF članak o hibridnim referencama detaljno prolazi

Jedina klasa datoteka koju treba ostaviti na miru su digitalno potpisani dokumenti. Potpuno prepisivanje premješta svaki bajt, što poništava bilo koji potpis izračunat nad izvornim rasponima. Promjena na potpisanom hibridu mora ući kao ispravno inkrementalno ažuriranje koje održava oba prikaza; datoteka koju treba samo čitati trebala bi proći netaknuta. Normalizacija je za datoteke koje posjedujete; potpisanim datotekama samo se dodaje na kraj

Hibridno-referentni PDF-ovi nisu loše oblikovani; oni su vlastiti most kompatibilnosti samog formata i Office aplikacije će ih nastaviti proizvoditi sve dok PDF 1.4 čitači opstaju u instalacijskoj bazi. Cjevovod koji može uočiti /XRefStm ključ, potvrditi valjanost spojenog dokumenta pomoću PDFium komponente i regenerirati čist jedinstveno indeksirani izlaz pomoću HotPDF Delphi komponente tretira ih kao ono što jesu: obični unosi s jednim dodatnim putokazom u traileru