Spajanje i deljenje su dve operacije nad stranicama za koje svi prvo posežu, i one pokrivaju mnogo toga. Ipak, one ne pokrivaju sve. Postoji posebna grupa zadataka koja menja raspored stranica umesto da pomera čitave datoteke: postavljanje četiri slajda na jedan list za priručnik, prevlačenje stranice sa kraja dokumenta na početak, ili izvlačenje stranica 3, 7 i 12 u kratak izvod bez diranja ostatka. PDFium izlaže tri metode upravo za to, i svaka se ponaša drugačije od spajanja i deljenja koje već poznajete. Ovaj članak prolazi kroz ono što one rade, gde se nalaze izlazne tačke i jedan detalj o vlasništvu koji je izazvao rušenje na terenu.
Ove tri metode su ImportNPagesToOne za N-up nametanje (imposition), MovePages za reorganizaciju u mestu, i ImportPagesByIndex za ekstrakciju podskupa. Spajanje slaže dokumente sa kraja na kraj i ostavlja broj stranica jednakim zbiru ulaza. Deljenje upisuje nekoliko izlaznih datoteka iz jednog ulaza. Tri operacije ovde se nalaze između: jedna od njih menja koliko izvornih stranica deli jedan list, jedna menja redosled unutar jednog dokumenta, a jedna kopira izabrani skup stranica u drugi dokument. Znanje o tome koja je koja štedi vas od forsiranja plesa spajanja i brisanja tamo gde bi jedan poziv obavio posao.
Šta N-up nametanje zapravo radi
Nametanje (imposition) je grafički izraz za raspoređivanje nekoliko izvornih stranica na jedan veći list tako da odštampani i presavijeni rezultat ima ispravan redosled čitanja. Svakodnevna verzija je 2-up priručnik, 4-up knjižica ili kontaktna stranica koja uklapa desetak sličica na stranicu. PDFium rešava geometriju kroz jedan poziv:
function ImportNPagesToOne(
OutputWidth, OutputHeight: Single;
NumX, NumY : Cardinal): TPdf;
NumX i NumY opisuju mrežu. Vrednost 2, 1 postavlja dve izvorne stranice jednu pored druge; 2, 2 pakuje četiri u kvadratni raspored; 4, 3 gradi kontaktni list sa dvanaest stranica. PDFium čita izvorne stranice po redu, skalira svaku nadole da stane u svoju ćeliju, i popunjava mrežu sleva nadesno, odozgo nadole, započinjući nov izlazni list kad god je trenutna mreža puna. Izvorne stranice se ne menjaju. Ono što dobijate nazad je nov dokument čije su stranice kompozitne.
Izlazna veličina je u tačkama, a ne u pikselima
OutputWidth i OutputHeight su PDF korisničke jedinice, a PDF korisnička jedinica je jedna tačka (point), što je jedna sedamdeset-druga inča. Jedinica deklariše fizičku veličinu izlaznog lista i nema nikakve veze sa pikselima ekrana ili DPI-jem renderovanja. Ovo je najčešće mesto gde se nametanje pogreši, jer programer navikao na bitmape poseže za brojem piksela i završi sa listom veličine poštanske marke ili bilborda.
Brojevi koje vredi zapamtiti su dve veličine stranica koje ćete najviše koristiti. US Letter je 612 sa 792 tačke, jer je 8,5 inča puta 72 elektronski iznos od 612, a 11 inča puta 72 je 792. A4 je otprilike 595 sa 842 tačke, na osnovu svojih dimenzija od 210 sa 297 milimetara. Zaglavlje samog povezivanja jasno navodi pravilo da je jedna jedinica jedna sedamdeset-druga inča, a jedinica isporučuje konstantu PointsPerInch jednaku 72 ukoliko 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;
// Four source pages per Letter sheet, 2 by 2 grid.
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; // see the next section: this is mandatory
Source.Free;
end;
end;
Vraćena ručka je vaša za oslobađanje
Pročitajte potpis ponovo. ImportNPagesToOne vraća TPdf, a ne Boolean. Ta povratna vrednost je potpuno nova ručka dokumenta, alocirana odvojeno od izvora, i pozivalac je poseduje. Izvorni TPdf nad kojim ste pozvali metod je netaknut i dalje poseduje sopstvenu ručku; kompozit je drugi, nezavisni objekat. Ako dozvolite da vraćeni TPdf izađe iz opsega bez oslobađanja, curenjem gubite ceo PDFium dokument.
Opasnija greška ide u drugom smeru. Ispod haube, metod traži od PDFium-a novu FPDF_DOCUMENT strukturu preko FPDF_ImportNPagesToOne, a zatim obmotava tu sirovu ručku unutar vraćenog TPdf-a tako da životni vek omotača upravlja životnim vekom ručke. Od tog trenutka postoji tačno jedan vlasnik ručke i tačno jedno mesto gde je treba zatvoriti: kada pozovete Free na vraćenom objektu. Nemarna putanja greške koja oslobađa i omotač i poziva FPDF_CloseDocument na sirovoj ručki koju je uhvatila zatvoriće isti PDFium dokument dvaput. To je dvostruko oslobađanje (double-free), i to je konkretan bag koji je jednom pogodio korisnika. Pravilo koje to sprečava je jednostavno. Zatvorite dokument samo na jednoj putanji, oslobađanjem TPdf objekta koji vam je metod predao, i nikada ne posežite iza omotača da zatvorite ručku koju je on već usvojio.
Iz ovoga proizilaze dve posledice. Prvo, metod vraća nil kada PDFium odbaci argumente, kao što je nula na bilo kojoj osi mreže ili neuspeh alokacije, tako da provera na nil pripada mestu pre nego što dodirnete rezultat. Drugo, inicijalizujte svoju izlaznu promenljivu na nil pre try bloka i oslobodite je u finally bloku, kao što to radi gornji primer, tako da neuspeh na pola puta ne može da vas ostavi sa oslobađanjem nedefinisane reference ili preskakanjem oslobađanja u potpunosti.
Promena redosleda stranica bez njihovog ponovnog upisivanja
Nametanje gradi nov dokument. Reorganizacija menja jedan dokument u mestu. MovePages podiže skup stranica sa njihovih trenutnih pozicija i ispušta ih na odredište, pomerajući sve ostalo oko pomerenog bloka tako da broj stranica ostaje isti:
function MovePages(
const PageIndices: array of Integer;
DestPageIndex : Integer): Boolean;
Indeksi su zasnovani na nuli. PageIndices navodi stranice koje treba pomeriti, u redosledu u kom treba da završe, a DestPageIndex je indeks na koji prva pomerena stranica sleće nakon što se pomeranje završi. Pošto PDFium premešta stranice umesto da kopira i ponovo kompresuje njihov sadržaj, operacija je jeftina i bez gubitaka: objekti stranica zadržavaju svoje tokove, svoje resurse i vernost. Ovo je poziv iza panela sa stranicama za prevlačenje radi promene redosleda, gde korisnik povlači sličicu u novo mesto, a vi potvrđujete novi redosled jednim potezom. Vraća False kada je indeks van opsega, pa validirajte rezultat umesto da pretpostavljate da je promena rasporeda uspela.
var
Doc: TPdf;
begin
Doc := TPdf.Create(nil);
try
Doc.FileName := 'report.pdf';
Doc.Active := True;
// Move the last page (index 4 in a 5-page file) to the very front.
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 pomoću indeksa
Treća operacija kopira eksplicitan skup stranica iz jednog dokumenta u drugi. ImportPagesByIndex uzima izvorni dokument i niz indeksa zasnovan na nuli, i umeće te stranice u ciljni dokument na izabranoj poziciji:
function ImportPagesByIndex(
Source : TPdf;
const PageIndices: array of Integer;
InsertAt : Integer= 0): Boolean;
Pozivate ga na ciljnom dokumentu i prenosite izvor kao prvi argument. PageIndices imenuje izvorne stranice koje treba izvući, u redosledu u kom ih želite; InsertAt je mesto zasnovano na nuli u ciljnom dokumentu gde ide prva uvezena stranica, tako da ih 0 postavlja pre postojeće prve stranice i dodaje ih na trenutni broj stranica cilja. Prazan niz uvozi svaku stranicu, što poziv čini kompletnim kopiranjem kada vam je to potrebno. Vraća False ako je bilo koji indeks van opsega u izvoru.
var
Source, Excerpt: TPdf;
begin
Source := TPdf.Create(nil);
Excerpt := TPdf.Create(nil);
try
Source.FileName := 'manual.pdf';
Source.Active := True;
Excerpt.CreateDocument; // start an empty target
// Pull pages 3, 7 and 12 (zero-based 2, 6, 11) into the excerpt.
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;
Čisto sklapanje celine
Oblik od početka do kraja je isti za sve tri operacije: otvorite izvor podešavanjem FileName i prebacivanjem Active na True, izvršite operaciju, sačuvate pomoću SaveAs i oslobodite ono što posedujete. Jedina grana koja zahteva pažnju jeste to koji pozivi alociraju nov dokument. MovePages menja dokument koji već držite, pa postoji jedan objekat za oslobađanje. ImportPagesByIndex upisuje u cilj koji ste sami kreirali, pa oslobađate izvor i otvarate cilj koji ste otvorili. ImportNPagesToOne je izuzetak, jer je novi dokument povratna vrednost metode, a ne nešto što ste sami konstruisali, a zaboravljanje da je to posebna ručka u vlasništvu pozivaoca je način na koji se dešavaju i curenje i dvostruko oslobađanje. Inicijalizujte rezultat na nil, proverite ga nakon poziva i oslobodite ga na samo jednoj putanji.
Ako je posao koji zapravo imate kombinovanje celih datoteka umesto reorganizacije stranica, pogledajte spajanje više PDF datoteka u jedan dokument. Ako je obrnuto, razbijanje jednog dokumenta na više datoteka, pogledajte splitting PDF documents into multiple files. Metode nametanja i reorganizacije opisane ovde isporučuju se kao deo PDFium komponente za Delphi i C++Builder, zajedno sa API-jima za učitavanje, renderovanje i uređivanje koji su pokriveni na drugim mestima na ovom blogu.