Otvorite PDF koji je proizveo Microsoft Word ili Excel, prelistajte ga i ništa ne izgleda neobično. Učitajte ga u Delphi program, pročitajte broj stranica i taj broj je tačan. Zatim ga ponovo sačuvajte sa uključenom enkripcijom i posao pada sa greškom EListError, ili se izlaz otvara uz upozorenje o oštećenoj unakrsnoj referenci (damaged cross-reference warning). Datoteka nikada nije bila oštećena. To je datoteka sa hibridnom referencom (hybrid-reference file), a sama struktura koja omogućava petnaest godina starom programu za pregled da je otvori jeste ona ista struktura koja pobeđuje program za učitavanje (loader) koji prerano prestane sa čitanjem
Ovo je jedan od najčešćih načina na koji procesiranje PDF-a (PDF pipeline) koje je prošlo svaki interni test nailazi na datoteku koju ne može da obradi ukrug (round-trip). Svi ulazni podaci su generisani interno (in-house), tako da nikada nisu bili hibridni. Prva hibridna datoteka stiže onog dana kada kupac prosledi fakturu izvezenu iz tabele
Šta Word i Excel zapravo pišu
ISO 32000-1 opisuje raspored hibridne reference (hybrid-reference layout) u §7.5.8.4. Aplikacija koja želi svojstva (features) iz PDF-a 1.5 poput tokova objekata (object streams), a da pritom omogući čitaču PDF-a 1.4 otvaranje datoteke, piše informacije o unakrsnoj referenci (cross-reference) dva puta. Postoji klasična tabela unakrsne reference, ASCII redovi fiksne širine koji su završavali svaki PDF do verzije 1.4, i postoji tok unakrsne reference koji indeksira ostatak. Prikolica (trailer) klasičnog odeljka nosi unos /XRefStm čija je vrednost odstupanje u bajtovima (byte offset) tog toka
Ova podela rada je namerna. Objekti do kojih stari čitač mora da dosegne, katalog i stablo stranica (page tree) među njima, adresabilni su iz klasične tabele. Objekti koji su savijeni (folded) u kompresovane tokove objekata označeni su kao slobodni u klasičnoj tabeli, sa unosom tipa f, tako da ih čitač 1.4 preskače i nikada se ne sapliće na strukturu koju ne može da analizira (parse). Njihove prave lokacije žive samo u toku unakrsne reference (cross-reference stream). Potpis takve datoteke je njen rep (tail): kratak klasični odeljak, često ništa više od reči xref praćene zaglavljem pododeljka (subsection header) 0 0, čija prikolica ukazuje na /XRefStm gde se nalaze stvarni podaci za oporavak (recovery data)
Zašto ispravan broj stranica ne dokazuje ništa
Zato što su katalog i stablo stranica sa razlogom dostupni iz klasične tabele, pa program za učitavanje (loader) koji čita samo tu tabelu pronalazi /Root, prolazi kroz stablo stranica i prijavljuje pravi broj stranica. Sve što je starom čitaču potrebno je prisutno, tako da datoteka deluje zdravo. Objekti koji nedostaju jesu oni spakovani u tokove objekata (object streams): rečnici AcroForm polja, elementi strukture tagged-PDF, dugačak rep malih rečnika koji nikada nisu ni morali da budu vidljivi starom (legacy) programu za pregled
Vi ni ne primećujete tu prazninu dok nešto ne dotakne te objekte, a puno ponovno čuvanje dodiruje sve njih. Prolazak kroz dokument da bi se ponovo enkriptovao ili prepisao jeste upravo operacija koja traži svaki broj objekta redom, što je razlog zašto se simptom javlja prilikom čuvanja radije nego pri učitavanju, i to daleko od svog uzroka
Zamka je detektor koji vidi xref i zaustavlja se
Jeftin način da se odluči o tome kako je datoteka indeksirana je pratiti startxref i pregledati prve bajtove na koje to ukazuje. Ključna reč xref znači klasična tabela; strim (stream) objekat znači tok unakrsne reference (cross-reference stream). Taj test je tačan za svaku datoteku koja se posveti jednoj shemi. On je pogrešan za hibridnu datoteku (hybrid file), čiji startxref cilja ka klasičnom odeljku u isključivu svrhu zadovoljavanja starih čitača, dok je /XRefStm u prikolici (trailer) tog odeljka mesto gde je najveći deo dokumenta zapravo indeksiran. Detektor koji vraća "klasično" na prvom susretu sa rečju xref nikada ne čita /XRefStm, i tako svaki objekat koji živi isključivo u toku (stream) postaje nevidljiv
var
Pdf: THotPDF;
PageCount: Integer;
begin
Pdf := THotPDF.Create(nil);
try
PageCount := Pdf.LoadFromFile('Invoice_XLS.pdf'); // count is correct
// inspect or edit the loaded document here
Pdf.SaveLoadedDocument('Invoice_secured.pdf'); // walks every object
finally
Pdf.Free;
end;
end;
Sa instaliranim detektorom ranog izlaska (early-exit), učitavanje deluje sasvim u redu, a ponovno čuvanje (resave) je momenat kada odsutni objekti objavljuju svoje prisustvo. Popravka se ne krije u čitanju većeg broja bajtova na startu; popravka je u tome da se prepozna hibridna prikolica (hybrid trailer) i prati /XRefStm pre konačne odluke o tome da je fajl kompletiran
Redosled spajanja ne podleže pregovaranju
Jednom kada se i jedan i drugi indeks očitaju, moguće ih je kombinovati isključivo samo u jednom jedinom pravcu. Taj se uloženi dodatak s parametrima (cross-reference stream) ujedno prvi prebaci ka centru, pa se unosi koje nazivamo klasičnim onda popune sa strana oko tog bloka (around it). Osnova iza koje sledi odgovor za ovaj pristup tiče se male prevare iz srca tog oblika fajla (deception at the heart of the format). Takva, tj hibridna datoteka komprimovane objekte redom naziva slobodnim po pravilu za standardnu klasičnu tabelu zarad starijih pretraživača (old readers) i njihovog načina obilaženja (ignore them). Aplikacija s učitavanjem kod koje prvenstvo sledi pravilu po sistemu – da prvo što je i učitano biva zapamćeno kao zvanično (first-seen-wins policy) pa na takav način unosi prvu klasičnu tabelu po starom, sačuvaće te objekte baš sa oznakom kao da su slobodni (free), po čemu bi ih trajno ignorisala pri bilo kakvom unosu s parametrima koji ih inače precizno pronalaze na sistemu (locate them) i to jer su za te objekte onda lokacije prosto već zaposele na samoj strukturi fajla (slots are already taken). Ako im na silu zamenite red (Reverse the order) biće mesta da tip 2 po sistemu (type 2) izbije tok – svaki s brojem s tokom po redu kao (object-stream number) te uz pripadajući mu indeks ponese s polja zapravo njemu prave namenske slajdere i poziciju (slots), gde posle biva omogućeno klasičnim jedinicama s unosom tek iza njih (settle around them)
Ista navika pazi na dešavanja vezana za starija izdanja fajla te sprečava povratak prethodno ukinutih stvari (deleted object). Osvežena inkrementalna uvezivanja redom povlače zadnja izdanja oslanjajući se za prolaz povratno putem (backward through) /Prev dok besplatan unos koji dobije (type 0) bude zapravo taj takozvani sentinel za nadzor na koga novija izdanja bace redni onaj ukinut objekat iz opsega zadatih serijskih (object number). Ne smete dopustiti unutra takvog nekog zadatog lančarskog sistema (chain) ikad za kasniji al i mnogo bajatiji (later, older) pododeljak te tabele presnimavanje (overwrite) bilo kakvim tu praistorijskim i bajatim obeležjem na taj uneti (stale location) trag kontrole (sentinel). Po pravilu i osnovici držati prvenstvo po prvom čitanju kao autoritativni dokaz onog stanja besplatno ustupljenih markera znači to da brisan dokument stalno biće onakav koga zovu ugašen; u radu postupajte tome samo u nemaru, dobićete to iz koga će sam taj prethodni život te datoteke (file's own history) odmah uspešno vratiti vama sadržaj pred kojim najnovije pak izdanje u procesu tog fajla briše (latest revision removed)
Šta to znači u HotPDF-u
Motor (engine) raščlanjuje ugrađenu referentnu postavku za hibridne datoteke u Vašu korist umesto vas, on po tom principu ide i vrši to kroz nepregledne prolaze svaki put tamo gde god po redu analize tih ukrštenih mernih struktura biva uslovljena staza provere sa tom tabelom s podatkom unakrsnih relacija (parse the cross-reference data). Počnite učitavanjem preko opcije u fajlu tj pomoću onoga što ide ko LoadFromFile ili se okrenite sistemu u radu direkt sa toka LoadFromStream. Onda završite s ubačenim onim izmenama kakvim želite, okončajte pa potvrdite u onom na kraju navedenom zapisu s naredbom SaveLoadedDocument, ili pustite odmah gotovu funkciju jedne putanje (one-shot operation) kakvu pruža naredba EncryptFile uz nju tu iščitava Vaš ubačeni početni materijal s rezultatom kog izlaga po prenosu za sam prenos takvog posla. Na svaki taj korak put oporavka vrši se očitavanjem tog ugrađenog /XRefStm stapa tu istu tu tabelu u strujnom (stream section) smislu pred onim starim unosom klasika po izradi (classic entries) tako na licu mesta tu i sam pronalazi izlaz po kom žive uskladišteni zapisi toka (objects that live in streams) – baš pred momenat kojim taj akt upisivanja krene uz tu numeraciju. Pravila same te enkripcije kod AES-256 biće baš to tačno onoliko prvo to obličje (first showed itself) pod kojim sam Vam pojasnio onaj opseg navedenog primera tog kvara iz navedenog prostog razloga pa po uvođenju te i takve enkripcije kroz prelaz samog sadržaja takav postupak radi presnimavanja svakog redom tu uvezanog objekta zbog onog celokupnog iziskanog nivoa pri kome je redosled za sve te ugrađene i prisutne elemente iz te obrade na celini pronađen do sitnica pre prvog sledećeg preuređivanja
// One-shot: read the hybrid input, write an AES-256 encrypted copy
Pdf.EncryptFile('Letter_DOC.pdf', 'Letter_secured.pdf',
'owner-secret', '', aes256, [prPrint, prFillAnnotations]);
Taj sitni al itekako vredni primer onoga iz čega tu najviše crpete (carrying away) smešten bi negde sa vrha pred ulaz u takozvane API mehanizme sistema. Sve priložene datoteke što se vama dopreme na radnu tačku pa koje poreklo nose još iz programa poput tog kao Word te onoga uz Excel il tog još navedenog po nazivu PowerPoint (PowerPoint) ili na kraju pod jednom podužom lepezom unosa kod kojeg vrše sistematičan upis ko PDF sa imenom (Save as PDF) idu u onaj podatak iz radnog okruženja redom samo te s priznakom rutinskog i hibridnog stila izvedbe (routinely hybrid), i tako vama taj prozor unosa koga recimo testirate baš samo po unutrašnjim Vašim ličnim (only against your own generator) alatkama a sve na polju testiranja pa baš vi tako nikad ni za korak se na njih ni tu zapravo i ne sapletete unutar faze na testiranje tog uočljivog dela procesa posla. Vaše probe pa s time tako probajte da nasadite (Seed your fixtures) obavezno sa materijalom a taj isti koji stiže Vama samo pod oblikom uvezbe koja napusti pravi radni sto te i originalni prostor neke aplikacije vezane tu uz onaj zvanični paket za uređivanje pod ofis formatom sistema a ne oslonjeni tek onda tamo sa jedinim onim obličjem te iste fajla verzije nastale tek ličnim Vama stvorenim (your own code) okvirom koda poslatog uz sistem
Proveravanje fajla u koji sumnjate
Na to tu vršene samo po sistemu na bazi sa dvokrilnog tog prolaza takva provera izuzetno je dovoljna. Na tom polju proveravajte datoteke u prvom onom navedenom sistemu pogledu uz (hex view), a te potom podaci u okviru rednog opisa pod oznakama bitova nakon finalnog onog ulaza za komande koje počnu baš s navedenom tom linijom što se očitava ko s oznake (startxref); tad obavezno uvidite po ishodu ako hibridna pokazuje (hybrid file shows) kraći taj uobičajeni onaj klasičan tip odeljka pri kojem na samom začelju po navici ima unet (trailer dictionary contains) sam izraz oblika po kom tu sledi i sam izraz za čitanje iz upita sa /XRefStm. Imamo opet s navedenog onaj lakši slučaj (Or compare) pa broj iz preuređivanja tih samih ubrajanih pod onaj kôd celokupnog toka gde to i ta izrada provere stoji i stiče taj jedan broj objekata s kojim ona ukrsti u paraleli s time uz onu na početku izradu toka (reports against the highest object) kojoj taj /Size u navodima objavi uz dno samog fajla – s čime taj broj uz tu tabelu (trailer) stigne unutar samog tog dna datoteke. I najmanje vidljivo ili te kakvo malo predimenzionirano veliko iskakanje pa taj manjak iz istog primera iz brojanja – znači jedno te isto po uverenjima da su tu negde zabačeni te preskočeni iz pogleda objekti pa još isti unutar toka onoga toka koga ta sama operacija iz unosa još ni ne (loader has not opened) poznaje; takav tajisti baš manjak pa ta sama takva usputna nepopunjena merna (shortfall) tačkica unese po prebacivanju u red za takozvanog kasnijeg oblika pri procesu rada kod kog on propusti pri pokušaju zapisa tog daljnjeg onoga što i ne stvori – il takozvani ugrađeni (save-time failure) neuspeh po samom njegovom naopakom sistemu
Rep tipičnog Excel izvoza čini prvu proveru konkretnom. Sve posle završne ključne reči xref je običan ASCII format (plain ASCII), tako da je sam potpis čitljiv pravo iz datog alata za heksadekadni prikaz (hex view) – pritom ilustrovana pomeranja i zabeleške su Vama sad prosto radi prikaza same obrade (offsets illustrative, annotations added)
xref
0 0 % empty classic subsection: no rows at all
trailer
<< /Size 216 % one past the highest object number in use
/Root 1 0 R
/Info 15 0 R
/ID [<5C9A...> <5C9A...>]
/XRefStm 87325 % byte offset of the cross-reference stream
>>
startxref
88710 % points at the classic section above
%%EOF
Na tu osnovu navedena grupa sekcije sa (subsection) tim vrednostima od samog početnog mesta po liniji u izrazima za oznake poput one 0 0 odaje to (is the tell): sama klasična tabela na celom tom spisku poseduje s podacima tu ravno izbrojano onih brojnosti u okvirima koji na izrazima ostaju i to pod redom nula ulaza, što baš samim navodom i te iste namene samo se postavlja sa idejom te nosi prikolicu (only to carry the trailer), po čemu se postavlja baš sa jedinim smislom u tu primarnu namenu (exists mainly) sa ciljem izgovora na taj deo tj unosa uz ispis samo kodne vrste po onome /XRefStm 87325. Naveden na rad, detektor kome stop tačka leži u ključnoj reči xref je na primeru tog polaznog nivoa video pred kraj taj popis al kod koga po redu on sam stoji prazan indeks ne postojećih stvari, ma to i nije ništa. S obzirom ako ste raspoloženiji (rather script the check) te se tako kod takvih stvari ne posvećujete vizuelnim oslanjanjem iz onih prostih čisto optičkih procena o samoj stvari, to markiranje pak (marker) na osnovu datoteka tog tipa stoji negde pri začelju u tim tamo zadnjim merama kilobajta pa takvo nazadovanje i čitanje nazad na ograničenu (bounded backward read) veličinu jeste i onaj i biće sasvim jedan adekvatan rad
// Returns the /XRefStm offset from the file's tail, or -1 if the
// marker is absent (the file is not hybrid, or not a PDF at all)
function FindXRefStm(const FileName: string): Int64;
var
FS: TFileStream;
Tail: AnsiString;
Len, P: Integer;
begin
Result := -1;
FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Len := 2048; // the trailer lives in the tail
if FS.Size < Len then
Len := Integer(FS.Size);
FS.Position := FS.Size - Len; // bounded backward read: 2 KB max
SetLength(Tail, Len);
FS.ReadBuffer(Tail[1], Len);
finally
FS.Free;
end;
P := Pos(AnsiString('/XRefStm'), Tail);
if P = 0 then
Exit; // no hybrid marker in the tail
Inc(P, Length('/XRefStm'));
while (P <= Len) and (Tail[P] in [' ', #9, #13, #10]) do
Inc(P); // skip whitespace after the key
Result := 0;
while (P <= Len) and (Tail[P] in ['0'..'9']) do
begin
Result := Result * 10 + Ord(Tail[P]) - Ord('0');
Inc(P);
end;
end;
// Usage: a non-negative result names the byte where the stream starts
if FindXRefStm('Invoice_XLS.pdf') >= 0 then
Writeln('hybrid-reference file: resave will need the /XRefStm section');
Priđite tom sondiranju (probe) kroz onakav sistem u maniru izviđanja radi selekcije onih prioriteta za posao s time kakve one zahtevaju (triage) mere predaha naspram kakvog nekog strogog postupanja iz alata kao recimo (parser): sam uvid će reći tek toliko da po pitanjima onih određenih dokumenata za red prvenstva u obradama po kojima ti isti za tim zaslužuju i svaku rednu vrstu brige pre puštanja sledećeg operativnog i te onakvog unosa ponovnog oblika presnimavanja (resave) na izraz u takvom jednom opsegu (nothing more) da izvan istog toga ništa ni ne preuzme s baze po navikama za provere u radu. Kada dođemo do rešenja šta učitavač s dobijenim navodom pa sve posle i od toga započetom samom u takvom jednom poslu za to pomeranje sa offset strane (offset it finds), sve ono sa sledećim radom tj praćenjem lanca delova iz fajl paketa (section chain) uz samo ono ugrađivanje elemenata (merging) za rešene unosima kroz sekcije (stream entries ahead) na ta klasična polja preko očuvavanja (respecting) tih takozvanih zaštitnika (free-entry sentinels), onda bi taj primer bio veoma jasno po detaljima i sve sa takozvanim prebrojanim koracima (walked through step by step) prikazan i detaljno prostudiran negde na srodnom i pratećem tekstu koga obrađuje članak povodom načina postupanja sa onim dokumentima (hybrid-reference) u hibridno uvezanom tipu iz onih datoteka rađenih (Office applications) za sisteme kancelarijskog programerskog aparata (our companion article)
Ta autorska spisateljska tačka iz ove vizure sa opisima povodom ove (writer's side of this story) ubačene priče, oko tih svih postupaka uz koje taj prenos unesen po vrsti vezan uz elemente (object streams) s kompresovanim relacijama koje sprovede sami sistemi i iz same (first place) onakve tu osnovne postave (compressed cross-references), tu ona bude negde kompletna obrađena a uz navedeni opis je to iz same prateće priče objašnjeno baš pod delom i sadržinom koju tu pruža članak o tim operacijama kod unosa elemenata toka s navođenjem u koracima kod ažuriranja i po uobičajenim onim primerima (object streams and incremental updates) po toj nekoj temi pređašnje rađene osnove na poslu. Slučaj pod kakvim hibridni model takvog fajla ima i bude iz tog posla nekad tu po izradi po onoj opštoj u razmeri pri testiranju pod jednom tako veoma velikom merom stvoren na taj baš sistem iz posla (very large) iz toga baš navodimo sve one takozvane mogućnosti (loading techniques) s raznim vrstama po polju tog primera tj iz alata sa podacima obrade na direktnom delovanju ka navedenim API tehnologijama kroz PDF putokaze velikog zahvata i u operacijama preuzimanja celina iz samog toka fajla baš onim postupcima da oni pruže opcije samom Vama pa dozvole u rad da vidite fajl po svakom poslu posve to ni pre tog ulaska celokupne izrade na sam memorijski (reading the whole thing into memory) modul sistema radnih okruženja poslovanja preuzmete radni pregled tog paketa na istom tom samom materijalu celokupne priče. Kod obe takve priče se sasvim lako u paru iz tih poslova nadoveže (Both pair naturally) svako ovo u izvedbi stvoreno saznanje kod ovog takvog i obrađenog tu na polju iz celokupnog izlečenja kvara s primerom (recovery described here) a taj na celom programu uz tu pak celokupnu podlogu ide sa obzirom (ships as part of) na izdatu baš u tom smeru tu po nazivu za taj zadati pravac za rad komponentu od imena HotPDF Component radi onih delovanja ka poslovnom Delphi a ujedno radi C++Builder dela programerskih zadataka (alongside the loading, editing) na učitavanju i sa doradom, a tu dođe usput sa sistemima bezbednosti i kriptovanjem a u paketu idu operacije kod poslova s onim potpisnim procesima (signing APIs) što dolaze onako na uobičajeni sistem pretražen i razrađen drugde na temama preko ovog (covered elsewhere on this blog) istog na njemu sakupljenog al prepoznatog programerskog okvira za informativni blok teksta na ovim tu sažetim ulogama sajta ovog samog operativnog predmeta iz celog izlaganja