HotPDF v Delphiju primerja dva dokumenta PDF prek THPDFDocComparison, ki prehodi graf objektov obeh datotek od kataloga navzven in, kadar je zahtevano, tudi upodobi vsak par strani ter izmeri slikovne pike, ki se razlikujejo. Rezultat je poročilo JSON, ki poimenuje vsako najdeno razliko, porabljeni proračun in ali se je primerjava izvedla do konca. Oba prehoda sta pomembna, ker strukturna in vizualna primerjava odgovarjata na različni vprašanji
Vprašanje, ki stoji za to funkcijo, je običajno vprašanje izdaje. Mehanizem za poročila dobi spremembo, izhod se znova ustvari, nekdo pa mora presoditi, ali se je karkoli premaknilo. Odpiranje obeh datotek druge ob drugi se obnese do približno treh strani, preden pozornost popusti. Primerjava surovih bajtov spodleti takoj, saj dva teka istega generatorja dasta različne bajte iz razlogov, ki nimajo nobene zveze s tem, kaj bralec vidi
Zakaj so lahko datoteke PDF bajtno različne, a vizualno enake?
Dva neodvisno ustvarjena PDF-ja, ki se natisneta enako, se v bajtih redno razlikujeta, razlogi za to pa so strukturni in ne kozmetični. Številke objektov so dodeljene v vrstnem redu, v katerem so objekti pač zapisani. Podnabori pisav dodeljujejo CID-je v vrstnem redu, v katerem so znaki prvič naleteni, zato podnabor, zgrajen med rahlo drugačnim prehodom, da drugačne bajte vsebinskega toka za isto vidno besedilo. Odmiki v tabeli navzkrižnih sklicev se premaknejo, kadarkoli se kje pred njimi spremeni dolžina
Zato številk objektov ni mogoče uporabiti kot identiteto med dokumenti. HotPDF namesto tega vsak posnetek zgradi tako, da prehodi od kataloga navzven, slovarje razširi po bajtnem vrstnem redu njihovih ključev, polja pa po indeksu, tako da je vsak objekt poimenovan po poti, ki do njega vodi. Objekti, do katerih prehod od korena ne more priti, se prevežejo na sintetično pot $Unreachable[...], ki nosi številko objekta in generacijo, kar osiroteno vsebino ohrani vidno v poročilu, namesto da tiho izgine
Tokovi se ne primerjajo s kopiranjem. Vsak tok prispeva postopen podpis SHA-256, izračunan tako, da se pozneje obnovi izvirni položaj v toku, zato primerjava dveh dvestomegabajtnih datotek ne pomeni materializacije dvesto megabajtov dvakrat
Poravnava strani, kadar ima en dokument vstavek
Primerjava strani 1 s stranjo 1, strani 2 s stranjo 2 in tako naprej je pravilna samo, kadar ni bilo nič vstavljeno. Vstavite naslovnico in naivna primerjava javi, da se je spremenila vsaka stran, kar je tehnično res, operativno pa neuporabno
HotPDF strani poravna, preden jih primerja. Za vsako stran zgradi podpis iz izvlečljivega besedila, za strani brez besedila pa se prevesi na strukturni podpis, nato pa izračuna najdaljše naraščajoče podzaporedje nad ujemajočimi se ciljnimi indeksi. Strani znotraj tega podzaporedja so tiste, ki so se le premaknile; strani izven njega so prave spremembe. Prav to razlikovanje naredi primerjavo 400-stranskega priročnika berljivo, saj poročilo pove, da je bila vstavljena ena stran, namesto da se je spremenilo štiristo strani
Izvajanje strukturne primerjave
Najpreprostejši klic sprejme dva naložena dokumenta in način. cmStructural izvede prehod grafa objektov, cmRenderedImage izvede primerjavo slikovnih pik, cmFull naredi oboje, lažji načini cmPageCount, cmPageText in cmObjectCount pa obstajajo za poceni osnovne preverbe:
uses
HPDFDoc, HPDFDocCompare;
var
DocA, DocB: THotPDF;
Report: AnsiString;
begin
DocA := THotPDF.Create(nil);
DocB := THotPDF.Create(nil);
try
if (DocA.LoadFromFile('baseline.pdf') <= 0) or
(DocB.LoadFromFile('candidate.pdf') <= 0) then
Exit;
Report := THPDFDocComparison.Compare(DocA, DocB, cmStructural);
with TFileStream.Create('diff.json', fmCreate) do
try
WriteBuffer(Report[1], Length(Report));
finally
Free;
end;
finally
DocB.Free;
DocA.Free;
end;
end;
Poročilo razlikuje tri stanja, ki jih logična vrednost ne more. identical pove, ali se je karkoli razlikovalo, comparisonComplete pove, ali se je prehod zaključil, comparisonBudget pa poimenuje omejitev, ki ga je ustavila, če ga je katera. Primerjava, ki izčrpa proračun, hkrati javi comparisonComplete=false in identical=false, ker prekinjen prehod nima nobene podlage za trditev o enakosti. Vsaka avtomatizacija, ki bere samo identical, bo sčasoma zaustavitev zaradi proračuna obravnavala kot pravo razliko, zato berite vse tri
Katere omejitve prehod držijo v mejah?
Privzete vrednosti v THPDFStructuralCompareLimits.Default so dimenzionirane za prave dokumente in ne za sovražne, vsak pomensko relevanten proračun pa ima svojo lastno zgornjo mejo: 250.000 objektov, 2.000.000 povezav, globina 128, 10.000 javljenih razlik, 64 MB na tok in skupno 512 MB bajtov tokov, 1 MB na vrednost in 4.096 bajtov na pot. Zvišajte jih namensko, kadar poznate svoj korpus, in znižajte, kadar primerjate datoteke, ki so prišle od zunaj:
var
Limits: THPDFStructuralCompareLimits;
Options: THPDFRenderedCompareOptions;
begin
Limits := THPDFStructuralCompareLimits.Default;
Limits.MaxDifferences := 200; // hiter neuspeh v CI
Limits.MaxTotalStreamBytes := 128 * 1024 * 1024;
Options := THPDFRenderedCompareOptions.Default;
Options.DPI := 150; // privzeto je 72
Options.ColorTolerance := 2; // prezri šum zaokroževanja na ravni 1-2
Options.MinimumSimilarity := 0.9995;
Options.MaxChangedPixelRatio := 0.0005;
Options.GenerateHeatmaps := True; // zapiši prekrivne slike za pregled
Report := THPDFDocComparison.CompareWithOptions(DocA, DocB, cmFull,
Limits, Options);
end;
Upodobitveni prehod pred dodelitvijo katere koli bitne slike oceni število slikovnih pik iz dimenzij strani in zahtevane ločljivosti DPI, nato pa pozneje ponovno preveri dejansko bitno sliko, tako da popačena geometrija strani ne more zaobiti proračuna z lažno navedbo svoje velikosti. Zvišanje DPI kvadratno zvišuje zvestobo in ceno: 150 DPI je štirikrat toliko slikovnih pik kot 72, zgornje meje na stran in skupaj pa obstajajo prav zato, ker bi si sicer paketno opravilo pri 300 DPI z dodeljevanjem samo prikorakalo v težave
Kako podobno je dovolj podobno?
Dve strani veljata za podobni samo, kadar sta izpolnjena oba pogoja: delež spremenjenih slikovnih pik je enak ali pod MaxChangedPixelRatio, podobnost pa je enaka ali nad MinimumSimilarity. Dva praga namesto enega, ker je peščica katastrofalno napačnih slikovnih pik nekaj drugega kot širok nanos drobnih barvnih premikov, in vsak od njiju je lahko v enem poteku dela sprejemljiv, v drugem pa diskvalifikacijski. Preizkusi pragov uporabljajo nezaokrožene vrednosti; šest decimalnih mest v JSON obstaja zato, da so poročila stabilna in primerljiva, ne da bi definirala primerjavo
Spremenjene slikovne pike so razvrščene v območja z uporabo ploščic fiksne velikosti kot vozlišč s štirismerno sosednostjo, in ne z zapolnjevanjem posamezne slikovne pike za slikovno piko. To pomni v mejah, seznam območij pa ostaja stabilen med teki. Skrajšanje ohranjenih podrobnosti območij vpliva samo na seznam, ne pa na javljeno število območij, zato stran z več spremenjenimi območji, kot jih dovoljuje MaxChangedRegions, še vedno javi, koliko jih je bilo
Eno vedenje je vredno jasno povedati, ker obrne običajen instinkt. Napake upodabljalnika, napake dodeljevanja in napake prekrivanja nikoli niso pogoltnjene. Vse takšno je zabeleženo kot renderError ali renderBudget in vsili renderComparisonComplete=false, ker je stran, ki se ni uspela upodobiti, stran, ki je nihče ni primerjal, javljanje, da je enaka, pa je slabše kot javljanje ničesar
Kam v cevovodu sodi posamezen način
Strukturna primerjava odgovori, kaj se je spremenilo, in je pravilna privzeta izbira za regresijske pakete: poimenuje pot, indeks strani in vpletene številke objektov, tako da neuspeh kaže na kodo, ki ga je povzročila. Upodobljena primerjava odgovori, ali bo kdo to opazil, kar je vprašanje za odobritve in za preverjanje, da je bil optimizacijski prehod res brez izgub
Dobro se dopolnjujeta. cmStructural poganjajte pri vsaki izgradnji in naj glasno spodleti ob nepričakovanih spremembah na ravni objektov; cmFull s toplotnimi zemljevidi poganjajte pred izdajo, ko je na voljo človek, ki si lahko ogleda prekrivanja. Za cevovode, ki iz drugih razlogov že izpisujejo oznako strani, besedilni izhod, opisan v izvozu PDF-strani v SVG, ponuja tretji, človeku berljiv pogled, samodejne preverbe v avtomatizaciji poročil pred tiskom pa pokrivajo vprašanja skladnosti, na katera noben od načinov primerjave ni namenjen odgovoru
Primerjava, preverjanje pred tiskom in upodabljanje si delijo isti objektni model naloženega dokumenta, zato lahko en sam prehod čez datoteko napaja vse tri. Celoten seznam funkcij za Delphi in C++Builder je na strani komponente HotPDF PDF za Delphi