Združevanje (merge) in deljenje (split) sta dve operaciji s stranmi, po katerih vsi najprej posežejo, in pokrivata veliko področij. Vendar ne pokrivata vsega. Obstaja ločena družina opravil, ki prerazporejajo strani, namesto da bi premikale celotne datoteke: postavitev štirih prosojnic na en list za gradivo, vlečenje strani z zadnjega dela dokumenta na začetek ali izločitev strani 3, 7 in 12 v kratek izvleček, ne da bi se dotaknili preostalih. PDFium za to ponuja tri metode in vsaka se obnaša drugače od združevanja in deljenja, ki ju že poznate. Ta članek obravnava, kaj delajo, kje so izhodne točke in eno podrobnost lastništva, ki je v praksi že povzročila sesutje.
Te tri metode so ImportNPagesToOne za postavitev N-up, MovePages za prerazporejanje na mestu in ImportPagesByIndex za ekstrakcijo podnabora. Združevanje zlaganje dokumentov od konca do konca in ohrani število strani enako vsoti vhodov. Deljenje zapiše več izhodnih datotek iz enega vhoda. Tri operacije tukaj se nahajajo vmes: ena izmed njih spremeni, koliko izvornih strani si deli list, ena spremeni vrstni red znotraj enega dokumenta, ena pa kopira izbrano peščico strani v drug dokument. Poznavanje razlik vas reši pred prisilnim plesom združevanja in brisanja, kjer bi zadostoval al že en sam klic.
Kaj postavitev N-up dejansko naredi
Postavitev (imposition) je pripravljalni izraz za urejanje več izvornih strani na en večji list, tako da se natisnjen in zložen rezultat bere v pravilnem vrstnem redu. Vsakodnevna različica je gradivo z 2-up postavitvijo, podpis knjižice s 4-up postavitvijo ali kontaktni list, ki na eno stran prilega ducat sličic. PDFium obravnava geometrijo prek enega klica:
function ImportNPagesToOne(
OutputWidth, OutputHeight: Single;
NumX, NumY : Cardinal): TPdf;
Parametra NumX in NumY opisujeta mrežo. Vrednost 2, 1 postavi dve izvorni strani eno poleg druge; 2, 2 stisne štiri v kvadrantno postavitev; 4, 3 zgradi kontaktni list z dvanajstimi stranmi. PDFium zaporedoma bere izvorne strani, vsako pomanjša, da ustreza svoji celici, in zapolni mrežo od leve proti desni, od zgoraj navzdol, pri čemer začne nov izhodni list, kadar koli je trenutna mreža polna. Izvorne strani se ne spreminjajo. Nazaj prejmete nov dokument, katerega strani so sestavljene (kompoziti).
Velikost izhoda je v točkah, ne slikovnih pikah
Parametra OutputWidth in OutputHeight sta uporabniški enoti PDF, uporabniška enota PDF pa je ena točka, kar je ena dvainsedemdesetina palca. Enota določa fizično velikost izhodnega lista in nima nobene zveze s slikovnimi pikami zaslona ali DPI upodabljanja. To je najpogostejše mesto za napake pri postavitvi, saj razvijalec, navajen bitnih slik, poseže po številu slikovnih pik in konča z listom velikosti poškodbe znamke ali oglasnega panoja.
Številki, ki si ju je vredno zapomniti, sta dve velikosti strani, ki ju boste najpogosteje uporabljali. US Letter je 612 krat 792 točk, saj je 8,5 palca krat 72 enako 612, 11 palcev krat 72 pa je 792. Format A4 meri približno 595 krat 842 točk iz svojih dimenzij 210 krat 297 milimetrov. Lastna glava povezave jasno navaja pravilo, da je ena enota ena dvainsedemdesetina palca, enota pa ponuja konstanto PointsPerInch z vrednostjo 72, če bi raje izračunali velikost iz palcev v kodi, namesto da bi zapisovali literale.
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;
Vrnjena ročica je vaša za sprostitev
Ponovno preberite signaturo. Metoda ImportNPagesToOne vrne TPdf, ne logične vrednosti (Boolean). Ta povratna vrednost je poponoma nova ročica dokumenta, dodeljena ločeno od vira, in klicatelj jo ima v lasti. Izvirni TPdf, na katerem ste poklicali metodo, ostane nedotaknjen in še vedno ima svojo lastno ročico; kompozit pa je drug, neodvisen objekt. Če pustite, da vrnjeni TPdf gre izven dosega brez sprostitve, izgubite celoten PDFium dokument.
Bolj nevarna napaka deluje v obratni smeri. Pod pokrovom metoda zaprosi PDFium za novo ročico FPDF_DOCUMENT prek FPDF_ImportNPagesToOne, nato pa to surovo ročico ovije v vrnjeni TPdf, tako da življenjska doba ovoja upravlja z življenjsko dobo ročice. Od te točke naprej obstaja natanko en lastnik ročice in natanko eno mesto, kjer jo je treba zapreti: ko pokličete Free na vrnjenem objektu. Neprevidna pot napake, ki hkrati sprosti ovoj in pokliče FPDF_CloseDocument na surovem ročaju, zapre isti PDFium dokument dvakrat. To je dvojna sprostitev in gre za specifičnega hrošča, ki je tukaj enkrat prizadel klicatelja. Pravilo, ki to preprečuje, je kratko. Zaprite dokument le na eni poti, s sprostitvijo objekta TPdf, ki vam ga je metoda predala, in nikoli ne segajte mimo ovoja, da bi zaprli ročico, ki jo je ta že prevzel.
Iz tega izhajata dve posledici. Prvič, metoda vrne nil, ko PDFium zavrne argumente, kot je ničla na kateri koli osi mreže ali neuspeh pri dodelitvi, zato preverjanje vrednosti nil sodi pred uporabo rezultata. Drugič, inicializirajte izhodno spremenljivko na nil pred blokom try in jo sprostite v bloku finally, kot to počne zgornji primer, da neuspeh na sredi poti ne povzroči sprostitve nedefinirane reference ali popolnega izpusta sprostitve.
Prerazporejanje strani brez ponovnega zapisovanja
Postavitev gradi nov dokument. Prerazporejanje spreminja en dokument na mestu. Metoda MovePages dvigne niz strani iz njihovih trenutnih položajev in jih spusti na cilj ter premakne vse ostalo okoli premaknjenega bloka, tako da število strani ostane enako:
function MovePages(
const PageIndices: array of Integer;
DestPageIndex : Integer): Boolean;
Indeksi so osnovani na ničli. PageIndices navaja strani, ki jih je treba premakniti v vrstnem redu, v katerem morajo končati, DestPageIndex pa je indeks, na katerem pristane prva premaknjena stran, ko se premik umiri. Ker PDFium premakne strani, namesto da bi kopiral in ponovno stiskal njihovo vsebino, je operacija poceni in brez izgube: objekti strani ohranijo svoje tokove, svoje vire in svojo zvestobo. To je klic za ploščo strani z vlečenjem za prerazporeditev, kjer uporabnik povleče sličico v novo režo, vi pa potrdite nov vrstni red z enim samim premikom. Vrne False, ko je indeks izven obsega, zato preverite rezultat, namesto da predvidevate, da je prerazporeditev 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;
Ekstrakcija podnabora po indeksu
Tretja operacija kopira ekspliciten niz strani iz enega dokumenta v drugega. Metoda ImportPagesByIndex sprejme izvorni dokument in z ničlo indeksirano polje ter vstavi te strani v cilj na izbrano mesto:
function ImportPagesByIndex(
Source : TPdf;
const PageIndices: array of Integer;
InsertAt : Integer= 0): Boolean;
Pokličete jo na ciljnem dokumentu in predate vir kot prvi argument. PageIndices poimenuje izvorne strani, ki jih želite potegniti, v vrstnem redu, ki ga želite; InsertAt je na ničli temelječe mesto v cilju, kamor gre prva uvožena stran, tako da 0 postavi te strani pred obstoječo prvo stran, trenutno število strani cilja pa se poveča. Prazno polje uvozi vsako stran, kar naredi klic popolno kopijo, ko jo potrebujete. Vrne False, če je kateri koli indeks izven obsega v viru.
Tukaj je pomembna razlika z deljenjem. Deljenje zapiše ločene datoteke, pri čemer ena operacija ustvari veliko izhodov na disku. Metoda ImportPagesByIndex opravi nasprotno delo: zbere izbran niz strani v en sam ciljni dokument v pomnilniku, ki ga ko nato enkrat shranite. Ko je naloga "daj mi strani 3, 7 in 12 kot en kratek PDF", je to neposredna pot, ki pod pokrovom ovija FPDF_ImportPagesByIndex.
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;
Učinkovito povezovanje delov
Celotna oblika je enaka pri vseh treh: odprete vir z nastavitvijo FileName in preklopom Active na True, izvedete operacijo, shranite z SaveAs in sprostite tisto, kar imate v lasti. Ena veja, ki potrebuje posebno skrb, je tista, ki določa, kateri klici dodelijo nov dokument. MovePages spreminja dokument, ki ga že imate, zato morate sprostiti en objekt. ImportPagesByIndex piše v cilj, ki ste ga ustvarili sami, zato sprostite vir in odprti cilj. Metoda ImportNPagesToOne izstopa, saj je novi dokument povratna vrednost metode in ne nekaj, kar ste sami zgradili. Če pozabite, da gre za ločeno ročico v lasti klicatelja, pride do iztekanja pomnilnika in dvojne sprostitve. Inicializirajte rezultat na nil, ga preverite po klicu in ga sprostite na eni sami poti.
Če je vaše dejansko delo združevanje celotnih datotek in ne prerazporejanje strani, si oglejte združevanje več datotek PDF v en dokument. Če gre za nasprotno operacijo, razčlenitev enega dokumenta na več datotek, si oglejte deljenje dokumentov PDF na več datotek. Metode postavitve in prerazporejanja, opisane tukaj, se dostavljajo kot del komponente PDFium Component za Delphi in C++Builder, skupaj z vmesniki API za nalaganje, upodabljanje in urejanje, ki so obravnavani drugje na tem blogu.