Spajanje i dijeljenje su dvije operacije sa stranicama koje svatko prvo isproba, i one pokrivaju puno toga. Međutim, one ne pokrivaju sve. Postoji zasebna obitelj poslova koja preslaguje stranice umjesto premještanja cijelih datoteka: postavite četiri slajda na jedan list za priručnik, povucite stranicu sa začelja dokumenta na početak, ili izuvcite stranice 3, 7 i 12 u kratki izvadak bez dodirivanja ostatka. PDFium izlaže tri metode upravo za to, a svaka se ponaša drugačije od spajanja i dijeljenja koje već poznajete. Ovaj članak prolazi kroz to što one rade, gdje se nalaze izlazne točke i jedan detalj vlasništva koji je uzrokovao rušenje u praksi
Tri metode su ImportNPagesToOne za N-up nametanje, MovePages za promjenu redoslijeda na licu mjesta i ImportPagesByIndex za ekstrakciju podskupa. Spajanje slaže dokumente jedan na drugi i ostavlja broj stranica jednakim zbroju ulaza. Dijeljenje zapisuje nekoliko izlaznih datoteka iz jednog ulaza. Tri operacije ovdje nalaze se između: jedna od njih mijenja koliko izvornih stranica dijeli list, jedna od njih mijenja redoslijed unutar jednog dokumenta, a jedna kopira odabranu šačicu stranica u drugi dokument. Znanje o tome koja je koja spašava vas od forsiranja plesa spajanja i brisanja tamo gdje bi bio dovoljan jedan poziv
Što zapravo radi N-up nametanje
Nametanje je grafički pojam za raspoređivanje nekoliko izvornih stranica na jedan veći list tako da se tiskani i presavijeni rezultat čita u ispravnom redoslijedu. Svakodnevna verzija je priručnik s 2 stranice na jednom listu, potpis knjižice s 4 stranice ili kontaktni list koji smješta desetak sličica na stranicu. PDFium upravlja geometrijom kroz jedan poziv
function ImportNPagesToOne(
OutputWidth, OutputHeight: Single;
NumX, NumY : Cardinal): TPdf;
NumX i NumY opisuju mrežu. Vrijednost 2, 1 postavlja dvije izvorne stranice jednu pored druge; 2, 2 pakira četiri u raspored kvadranta; 4, 3 gradi kontaktni list s dvanaest stranica. PDFium čita izvorne stranice redom, smanjuje svaku kako bi stala u svoju ćeliju i popunjava mrežu s lijeva na desno, odozgo prema dolje, započinjući novi izlazni list kad god se trenutna mreža popuni. Izvorne stranice se ne mijenjaju. Ono što dobivate natrag je novi dokument čije su stranice kompoziti
Veličina izlaza je u točkama, a ne u pikselima
OutputWidth i OutputHeight su korisničke jedinice PDF-a, a korisnička jedinica PDF-a je jedna točka, što je jedna sedamdeset i druga inča. Jedinica deklarira fizičku veličinu izlaznog lista i nema nikakve veze s pikselima zaslona ili DPI-jem prikaza. Ovo je najčešće mjesto na kojem se griješi u nametanju, jer programer naviknut na bitmape poseže za brojem piksela i završi s listom veličine poštanske marke ili jumbo plakata
Brojevi koje vrijedi zapamtiti su dvije veličine stranica koje ćete najviše koristiti. US Letter je 612 puta 792 točke, jer je 8,5 inča puta 72 jednako 612, a 11 inča puta 72 je 792. A4 je otprilike 595 puta 842 točke, iz svojih dimenzija 210 puta 297 milimetara. Zaglavlje samog vezanja jasno navodi pravilo da je jedna jedinica jedna sedamdeset i druga inča, a jedinica isporučuje konstantu PointsPerInch jednaku 72 ako biste radije izračunali veličinu iz inča u kodu nego pisali literal
const
LetterW = 612.0; // 8.5 in * 72
LetterH = 792.0; // 11 in * 72
var
Source, Composite: TPdf;
begin
Source := TPdf.Create(nil);
Composite := nil;
try
Source.FileName := 'slides.pdf';
Source.Active := True;
// Četiri izvorne stranice po listu Letter, mreža 2 x 2
Composite := Source.ImportNPagesToOne(LetterW, LetterH, 2, 2);
if Composite = nil then
raise Exception.Create('PDFium rejected the imposition arguments');
Composite.SaveAs('slides-4up.pdf');
finally
Composite.Free; // vidi sljedeći odjeljak: ovo je obavezno
Source.Free;
end;
end;
Vraćena ručka je vaša za oslobađanje
Pročitajte ponovno potpis. ImportNPagesToOne vraća TPdf, a ne Boolean. Ta povratna vrijednost je potpuno nova ručka dokumenta, dodijeljena odvojeno od izvora, i pozivatelj je posjeduje. Izvor TPdf na kojem ste pozvali metodu je netaknut i još uvijek posjeduje vlastitu ručku; kompozit je drugi, neovisni objekt. Ako dopustite da vraćeni TPdf izađe iz dosega bez oslobađanja, procurit ćete cijeli PDFium dokument
Opasnija pogreška ide u drugom smjeru. Ispod haube, metoda traži od PDFium-a svježi FPDF_DOCUMENT kroz FPDF_ImportNPagesToOne, a zatim omotava tu sirovu ručku unutar vraćenog TPdf-a tako da životni vijek omotača upravlja ručkom. Od tog trenutka postoji točno jedan vlasnik ručke i točno jedno mjesto na kojem bi trebala biti zatvorena: kada oslobodite (Free) vraćeni objekt. Nemarna putanja pogreške koja oslobađa omotač i također poziva FPDF_CloseDocument na sirovoj ručki koju je uhvatila zatvara isti PDFium dokument dvaput. To je dvostruko oslobađanje i to je specifičan bug koji je ovdje jednom pogodio pozivatelja. Pravilo koje to sprječava je kratko. Zatvorite dokument samo na jednoj putanji, oslobađanjem TPdf-a koji vam je metoda predala, i nikada ne posežite izvan omotača kako biste zatvorili ručku koju je već usvojio
Iz ovoga proizlaze dvije posljedice. Prvo, metoda vraća nil kada PDFium odbije argumente, kao što je nula na bilo kojoj osi mreže ili neuspjeh dodjele memorije, pa provjera na nil pripada prije nego što dotaknete rezultat. Drugo, inicijalizirajte izlaznu varijablu na nil prije bloka try i oslobodite je u bloku finally, kao što to čini gornji primjer, tako da vas neuspjeh na pola puta ne ostavi s oslobađanjem nedefinirane reference ili preskakanjem oslobađanja u potpunosti
Promjena redoslijeda stranica bez ponovnog pisanja
Nametanje gradi novi dokument. Promjena redoslijeda mijenja jedan dokument na licu mjesta. MovePages podiže skup stranica iz njihovih trenutnih pozicija i ispušta ih na odredište, pomičući sve ostalo oko premještenog bloka tako da broj stranica ostaje isti
function MovePages(
const PageIndices: array of Integer;
DestPageIndex : Integer): Boolean;
Indeksi su bazirani na nuli. PageIndices navodi stranice koje treba premjestiti, u redoslijedu u kojem bi trebale završiti, a DestPageIndex je indeks na koji slijeće prva premještena stranica nakon što se premještanje završi. Budući da PDFium premješta stranice umjesto kopiranja i ponovnog komprimiranja njihovog sadržaja, operacija je jeftina i bez gubitaka: objekti stranica zadržavaju svoje tokove, resurse i vjernost. Ovo je poziv iza ploče s povlačenjem za promjenu redoslijeda stranica, gdje korisnik povlači sličicu u novi utor, a vi potvrđujete novi redoslijed jednim pokretom. Vraća False kada je indeks izvan raspona, pa potvrdite rezultat umjesto da pretpostavite da je preslagivanje uspjelo
var
Doc: TPdf;
begin
Doc := TPdf.Create(nil);
try
Doc.FileName := 'report.pdf';
Doc.Active := True;
// Pomakni zadnju stranicu (indeks 4 u datoteci od 5 stranica) sasvim na početak
if not Doc.MovePages([4], 0) then
raise Exception.Create('MovePages rejected the index');
Doc.SaveAs('report-reordered.pdf');
finally
Doc.Free;
end;
end;
Izvlačenje podskupa po indeksu
Treća operacija kopira eksplicitan skup stranica iz jednog dokumenta u drugi. ImportPagesByIndex uzima izvorni dokument i niz indeksa baziran na nuli, te umeće te stranice u ciljni dokument na odabranu poziciju
function ImportPagesByIndex(
Source : TPdf;
const PageIndices: array of Integer;
InsertAt : Integer= 0): Boolean;
Pozivate ga na ciljnom dokumentu i prosljeđujete izvor kao prvi argument. PageIndices imenuje izvorne stranice koje treba povući, u redoslijedu u kojem ih želite; InsertAt je utor baziran na nuli u ciljnom dokumentu gdje ide prva uvezena stranica, tako da ih 0 postavlja ispred postojeće prve stranice, a trenutni broj stranica cilja se pribraja. Prazan niz uvozi svaku stranicu, što čini poziv potpunom kopijom kada vam je potrebna. Vraća False ako je bilo koji indeks izvan raspona u izvoru
Ovdje je važna razlika u odnosu na dijeljenje. Dijeljenje zapisuje zasebne datoteke, pri čemu jedna operacija proizvodi mnogo izlaza na disku. ImportPagesByIndex radi suprotan oblik posla: sakuplja odabrani skup stranica u jedan ciljni dokument u memoriji, koji zatim jednom spremate. Kada je zadatak "daj mi stranice 3, 7 i 12 kao jedan kratki PDF", ovo je izravan put i on obavija FPDF_ImportPagesByIndex ispod haube
var
Source, Excerpt: TPdf;
begin
Source := TPdf.Create(nil);
Excerpt := TPdf.Create(nil);
try
Source.FileName := 'manual.pdf';
Source.Active := True;
Excerpt.CreateDocument; // započni prazan cilj
// Uvuci stranice 3, 7 i 12 (indeksi od 0: 2, 6, 11) u izvadak
if not Excerpt.ImportPagesByIndex(Source, [2, 6, 11], 0) then
raise Exception.Create('A requested page index is out of range');
Excerpt.SaveAs('manual-excerpt.pdf');
finally
Excerpt.Free;
Source.Free;
end;
end;
Uredno spajanje svega zajedno
Oblik od početka do kraja isti je za sve tri operacije: otvorite izvor postavljanjem FileName i prebacivanjem Active na True, izvedite operaciju, spremite s SaveAs i oslobodite ono što posjedujete. Jedina grana koja zahtijeva oprez je koja od njih dodjeljuje novi dokument. MovePages mijenja dokument koji već držite, pa postoji jedan objekt za oslobađanje. ImportPagesByIndex zapisuje u cilj koji ste sami stvorili, pa oslobađate izvor i cilj koji ste otvorili. ImportNPagesToOne je iznimka, jer je novi dokument povratna vrijednost metode, a ne nešto što ste sami konstruirali, a zaboravljanje da se radi o zasebnoj ručki u vlasništvu pozivatelja način je na koji se događaju i curenje i dvostruko oslobađanje. Inicijalizirajte rezultat na nil, provjerite ga nakon poziva i oslobodite ga na jednoj putanji
Ako je posao koji zapravo imate spajanje cijelih datoteka, a ne preslagivanje stranica, pogledajte spajanje više PDF datoteka u jedan dokument. Ako je obrnuto, razbijanje jednog dokumenta na više datoteka, pogledajte dijeljenje PDF dokumenata na više datoteka. Metode nametanja i promjene redoslijeda ovdje opisane isporučuju se kao dio softvera PDFium Component za Delphi i C++Builder, zajedno s API-jima za učitavanje, prikazivanje i uređivanje koji su obrađeni drugdje na ovom blogu