Združevanje ali deljenje dvogigabajtnega PDF po očitni poti vas stane dvoje hkrati: čas na uri in naslovni prostor. Očitna pot je naložiti vsak vhod, opraviti delo, zapisati izhod. Nalaganje je tisto, kar se zlomi. Arhiv skenov, ki se s 300 premakne na 600 DPI, podvoji linearno ločljivost in se na disku približno početveri, zato isto sestavljalno opravilo, ki je vse leto obvladovalo datoteke po 400 MB, začne mlatiti prazno v trenutku, ko vhod prekorači gigabajt, pogosto medtem ko ne počne nič drugega kot šteje strani. Opravilo ni postalo nič težje. Odpri, preštej, izberi obsege, zlepi — to je vse. Nalaganje celotnega drevesa preprosto ni več smiselna privzeta izbira pri tej velikosti. PDF Library for Delphi, knjižnica losLab za PDF v Delphiju in C++Builderju, na to odgovarja s svojo plastjo neposrednega dostopa: družino funkcij s predpono DA, ki jo podpira pretočni bralnik in ki tabelo navzkrižnih sklicev prehodi na mestu, namesto da bi celoten dokument gradil v pomnilniku
Kam gre pomnilnik pri polnem nalaganju
Nalaganje PDF "na običajen način" pomeni razčlenitev xref, razrešitev vsakega posrednega objekta v drevo v pomnilniku, dekodiranje objektnih tokov ter povezavo drevesa strani, pisav in pripisov v objekte, s katerimi lahko upravljate. Za urejevalne poteke dela je to prava kupčija. Za združevanje, deljenje in pregledovanje je večinoma zapravljanje. Arhiv skenov s 30.000 stranmi lahko drži milijone posrednih objektov, opravilo deljenja pa jih mora prebrati nekaj sto: vozlišča strani v zahtevanem obsegu in tisto, na kar se ta vozlišča sklicujejo
Plast neposrednega dostopa model obrne. DAOpenFile in DAOpenFileReadOnly razčlenita prikolico in xref, torej nekaj kilobajtov na repu datoteke, ter vrneta ročico datoteke. Objekti se pridobijo leno, ko jih klic potrebuje. Praktična posledica je, da odpiranje večgigabajtne datoteke traja približno toliko kot odpiranje majhne, poraba pomnilnika pa sledi temu, česa se dotaknete, ne pa temu, kaj datoteka vsebuje
Sondiranje ogromne datoteke brez nalaganja
Spodnji vzorec izhaja iz knjižničinega merila zmogljivosti za velike datoteke: odpri samo za branje, postavi vprašanja, zapri. Drevo dokumenta nikoli ne nastane
var
Lib: TPDFlib;
Handle, Pages: Integer;
begin
Lib := TPDFlib.Create;
try
Handle := Lib.DAOpenFileReadOnly('archive-2025.pdf', '');
if Handle = 0 then
raise Exception.Create('Direct access open failed');
Pages := Lib.DAGetPageCount(Handle);
Writeln('pages : ', Pages);
Writeln('title : ', Lib.DAGetInformation(Handle, 'Title'));
Lib.DACloseFile(Handle);
finally
Lib.Free;
end;
end;
Način samo za branje je vredno imeti raje, kadar koli je mogoče: omogoča, da faza zajema teče, medtem ko datoteko držijo drugi procesi, in dokumentira namen. Faza sondiranja, ki po nesreči pokliče spreminjajočo funkcijo, odpove hitro, namesto da bi pokvarila arhiv
PageRef je ročica objekta, ne številka strani
Najpogostejša napaka pri API-ju DA je podajanje številke strani tam, kjer funkcija pričakuje PageRef. Skoraj vsak klic DA za posamezno stran vzame sklicno ročico objekta strani in ne številke strani: DAExtractPageText, DARenderPageToFile, DARotatePage in DACapturePage vsi pričakujejo sklic. Dobite ga tako, da človeku namenjeno številko prevedete prek DAFindPage:
PageRef := Lib.DAFindPage(Handle, 250); // številka strani -> ročica objekta
if PageRef <> 0 then
begin
Text := Lib.DAExtractPageText(Handle, PageRef, 0);
Lib.DARenderPageToFile(Handle, PageRef, 5, 150, 'page250.png');
end;
Podajanje surove številke 250 namesto tega ne sproži napake. Naslovi objekt, ki pač stoji za to vrednostjo ročice, kar na dober dan odpove vidno, na slab dan pa izvleče besedilo z napačne strani v dokument, namenjen stranki. Če plast DA ovijete v lastno storitveno kodo, poskrbite, da prevoda ne bo mogoče preskočiti: na meji sprejmite številke strani, takoj pokličite DAFindPage in interno podajajte samo sklice
Združevanje stotin datotek s poimenovanim seznamom
Za dve datoteki zadošča MergeFiles(First, Second, Output). Paketno sestavljanje se bolje razteza prek seznamov datotek: vhode registrirajte pod imenom seznama, nato seznam združite v enem prehodu
Lib.AddToFileList('Statements', 'jan.pdf');
Lib.AddToFileList('Statements', 'feb.pdf');
Lib.AddToFileList('Statements', 'mar.pdf');
Lib.MergeFileList('Statements', 'q1-statements.pdf');
// Izid preverite po poceni poti: spet neposredni dostop
Handle := Lib.DAOpenFileReadOnly('q1-statements.pdf', '');
Writeln('merged pages: ', Lib.DAGetPageCount(Handle));
Lib.DACloseFile(Handle);
Družina za združevanje ima tri različice in razlika ni le v hitrosti. MergeFileListFast preskoči ohranitev strukturnega drevesa; MergeFileListStrict uveljavi strogi način; različica brez pripone je uravnotežena privzeta izbira. Iz tega izpade operativno pravilo: če je kateri koli vhod označen PDF, čigar zgradba dostopnosti mora preživeti, pri čemer je očiten primer vse, kar nastaja za PDF/UA, sezite po privzeti ali strogi različici, saj Fast strukturno drevo tiho zavrže. Za navadne arhive skenov brez označevanja je Fast zastonj zmogljivost. Odločite se na ravni cevovoda in ne po razpoloženju razvijalca, uporabljeno različico pa zabeležite v dnevnik opravila
Deljenje brez nalaganja: izvleček obsegov
Deljenje sledi isti filozofiji brez nalaganja. ExtractFilePages(InputFileName, Password, OutputFileName, RangeList) potegne obseg strani naravnost iz datoteke v datoteko, s seznamom obsegov, kot je '1-500', '501-1000', ali z izbirami, ločenimi z vejicami, vir pa nikoli ne postane drevo dokumenta. Kadar je dokument iz drugih razlogov že naložen, ExtractPageRanges iz trenutnega ustvari nov dokument v pomnilniku, CopyPageRanges pa obsege prenese iz drugega naloženega dokumenta po ID. Za deljenje združenih tiskalniških tokov po posameznih izpiskih je oblika iz datoteke v datoteko tista, ki 4 GB vhodu prepreči, da bi se sploh kdaj napihnil v pomnilnik
Datoteke, ki lažejo o svoji geometriji
Cevovodi za velike datoteke srečujejo poškodovane datoteke pogosteje, kot jih cevovodi za majhne datoteke sploh kdaj vidijo, preprosto zato, ker vhodi prehajajo skozi več sistemov. Dve obliki odpovedi si zaslužita izrecno obravnavo
Prvič, premaknjene glave. Poštni prehodi in tiskalniške vrste PDF včasih spredaj dodajo bajte, zato oznaka %PDF ne stoji več na odmiku 0, vsak odmik xref v datoteki pa je napačen za isto vrednost. Pretočni bralnik to zazna in razkrije (DAShiftedHeader na ploski ravni, ShiftedHeader na TSmartPDFReader), nato pa to med branjem izravna. Domača aritmetika odmikov tega običajno ne počne, in prav zato je "deluje na vsaki datoteki, ki jo ustvarimo sami, odpove pri datotekah stranke X" klasičen simptom
Drugič, pokvarjene tabele navzkrižnih sklicev. DACopyFile(InputFileName, OutputFileName, PageCount) celotno datoteko pretoči v nov izvod in med tem znova zgradi xref, kot stranski izdelek pa vrne število strani. Če ga poganjate kot fazo normalizacije pred izbirčnim porabnikom nižje po toku, razred občasnih napak pri razčlenjevanju pretvorite v en predvidljiv popravljalni korak. Kadar je treba shraniti vaše lastne spremembe, jih DAAppendFile zapiše kot inkrementalno posodobitev, torej doda novo revizijo, namesto da bi prepisoval gigabajte, kar strošek shranjevanja ohrani sorazmeren s spremembo in ne z datoteko
Podrobnosti dostave: linearizacija in sestavljanje
Cevovod za velike datoteke zaokrožita dve sosednji zmožnosti. Kadar se sestavljen izhod streže po HTTP za ogled v brskalniku, ga LinearizeFile preuredi za pretakanje po obsegih bajtov, tako da se prva stran prikaže, preden se ostanek 500 MB paketa prenese do konca. Poganjajte ga kot zadnjo fazo, po vsem združevanju, saj vsaka poznejša sprememba datoteko spet delinearizira. Kadar pa paketi potrebujejo sestavljanje in ne golega lepljenja, denimo naslovnico, odtisnjeno za vsakim izpiskom, ali dve izvorni strani, uprizorjeni na enem izhodnem listu, DACapturePage poljubno stran spremeni v predlogo za večkratno rabo, ki jo DADrawCapturedPage postavi na ciljno stran v poljuben pravokotnik, še vedno brez polnega nalaganja večgigabajtnega vira
Omejitve in kaj ostane samo za branje
Zapisu samemu zmanjka prostora dosti prej kot neposrednemu dostopu. Odmiki so skozi celotno plast DA Int64, zato sta resnična stropa razpoložljivi disk in desetmestno polje za odmik xref v klasičnih (netokovnih) tabelah navzkrižnih sklicev. Večgigabajtni arhivi skenov v praksi niso nič posebnega, poraba pomnilnika pa ostaja omejena ne glede na velikost datoteke, ker se objekti berejo šele, ko jih klic zahteva
Dve vprašanji se pojavljata dovolj pogosto, da nanju odgovorimo naravnost. Združevanje po privzeti poti prenese zgradbo dokumenta, zato zaznamki in povezave preživijo; različica Fast je tista, ki strukturno drevo zamenja za hitrost, in prav to je razlog, da jo prihranite za neoznačene vhode. Varna navada je, da združen izhod odprete, prehodite njegov oris in pred odpremo preverite nekaj notranjih povezav. Glede urejanja: med sondiranjem samo za branje in polnim nalaganjem obstaja koristna vmesna pot. Operacije na ravni strani delujejo neposredno na ročici, med njimi DARotatePage, DAMovePage in DAHidePage, poleg branja polj obrazcev, DAAppendFile pa te spremembe ohrani kot inkrementalno revizijo. Urejanje na ravni vsebine, torej vse, kar prepisuje označevalne operatorje znotraj strani, še vedno sodi v plast celotnega dokumenta
Sorodni članki
Če mora vaš združeni izhod ostati dostopen, je ozadje o strukturnem drevesu obravnavano v članku o dostopnosti označenega PDF, ki natančno pojasni, kaj bi različica Fast zavrgla. Za izvlečenje vsebine iz obsegov, ki jih razdelite, glejte vodnik po izvlečku besedila, slik in pisav
Celoten seznam funkcij neposrednega dostopa je priložen knjižnici; izdaje in preizkusni prenosi so na strani izdelka PDF Library for Delphi