PDFium Component izlaže (exposes) spajanje PDF-a kroz jednu jedinu metodu: ImportPages. Obrazac je uvijek isti: stvorite prazan odredišni dokument, otvorite svaku izvornu (source) datoteku, pozovite ImportPages za kopiranje stranica (copy the pages across), zatvorite izvor i ponovite. Kada petlja (loop) završi, SaveAs (Spremi kao) ispisuje (writes) rezultat na disk. Nema posebnog načina spajanja, niti konfiguracije za okretanje (flip). Složenost (complexity) živi u rubnim slučajevima (edge cases), a ima ih nekoliko koji grizu (bite) bez upozorenja (warning)
Glavna petlja
Dva primjerka (instances) TPdf su sve što trebate. Jedan drži odredišni (destination) dokument (document), stvoren prazan (empty) pomoću CreateDocument. Drugi redom otvara svaku (each) izvornu (source) datoteku. Ispod (Below) je postupak (procedure) koji uzima (takes) popis putanja do datoteke (file paths) i ispisuje (writes) spojeni izlaz (merged output) na jedinstvenu (single) putanju:
procedure MergeFiles(const FileList: TStrings; const OutputPath: string);
var
PdfDest, PdfSrc: TPdf;
InsertAt, I: Integer;
begin
PdfDest := TPdf.Create(nil);
PdfSrc := TPdf.Create(nil);
try
PdfDest.CreateDocument;
InsertAt := 1; // ImportPages uses 1-based destination position
for I := 0 to FileList.Count - 1 do
begin
PdfSrc.FileName := FileList[I];
PdfSrc.Active := True;
if not PdfSrc.Active then
raise Exception.CreateFmt('Cannot open: %s', [FileList[I]]);
PdfDest.ImportPages(
PdfSrc,
'1-' + IntToStr(PdfSrc.PageCount), // full document range
InsertAt);
Inc(InsertAt, PdfSrc.PageCount);
PdfSrc.Active := False;
end;
PdfDest.SaveAs(OutputPath);
finally
PdfSrc.Free;
PdfDest.Free;
end;
end;
Dvije su stvari u tom kodu lako (easy to overlook) zanemariti pri prvom čitanju (first read). Prvo je način na koji PDFium izvještava (reports) o greškama pri (load failures) učitavanju. Active := True nikada ne podiže (never raises) iznimku (exception): ako datoteka nedostaje (missing), oštećena je ili zaštićena lozinkom (password-protected), PDFium interno hvata (catches) grešku (error) te ostavlja (leaves) Active kao False. Bez eksplicitne (explicit) provjere na liniji 10, loša bi datoteka tiho ispala (silently drop out) iz spajanja bez naznake (indication) u izlazu (output). Konačni PDF bi imao manje stranica od očekivanog (expected) te ne biste znali koja je datoteka bila krivac (culprit)
Drugi je brojač (counter) InsertAt. Treći argument (argument) za ImportPages je početna 1 pozicija u odredištu (destination) na kojoj slijeće (lands) prva uvezena (imported) stranica. Počevši od (Starting at) 1, stavlja (puts) prvi izvorni (source) dokument na početak inače prazne datoteke. Nakon (After) svakog izvora, brojač (counter) napreduje za (advances by) PdfSrc.PageCount, pa (so) se sljedeća (next) grupa (batch) stranica dodaje (appends) nakon posljednje (last one). Zaboravite li je povećati (increment) tada svaki sljedeći (subsequent) izvor prepisuje (overwrites) stranice na poziciji 1, dajući vam posljednji dokument (last document) na popisu te ništa više
Selektivni rasponi (ranges) stranica
Ne (You do not) morate (have to) uzeti svaku stranicu iz izvora. Niz (string) raspona koji je proslijeđen (passed) kao (as) drugi argument slijedi (follows) jednostavan (simple) format zapisan zarezima (comma) i crticama (hyphen format): "1-3" uzima (takes) stranice (pages) od 1 do 3, dok pak - "2,4,6" odabire (picks) isključivo tri određene (specific) stranice, a (and) opcija uz: "1-" odnosi (means) se na stranicu broj (page) 1 pa skroz do samog kraja na (end of the) dokumentu. Rasponi se tako ovdje itekako mogu (can be) ukombinirati i (combined) u sasvim poseban jedan dodani na opcije sami taj prepoznatljivi niz: (single string,) tako sa primjerom sa "1-3,5,7-" program će automatski kod ispisa i ponude u radu odmah i obrisati iz (skips) postavke iz preskakanja na strani iste (pages) za one na oznaci: 4 i onu po pregledu po - oznaci: 6. Tu itekako (here:) vrijedi spomenuti još jednu bitnu (One subtlety matters) informaciju i suptilnost oko obrade takvih redova iz zahtjeva - a (the numbers always refer to pages in the source document,) to je zasigurno to kako se (refer to) ti brojevi samo i oduvijek (always) primarno samo - u samim takvim - i tim - funkcijama na onom (source) obliku prepoznaju iz isključivo originalnog dokumenta sa (starting at) startom na redu za stranicu: 1, to apsolutno neovisno sa obzirom (regardless of where) po - tome kako te i sve one (those pages) druge takve i stranice za iznos dolaze u ponudi (end up) odredištima s istih točaka s pregledima na to iz rješenja (destination). U tim slučajevima s onom takvom željom za traženja iz iste te stranice od broja - (If you want pages) 40 pa tako skroz na: 50 na (out of) opciji - iz pregleda u obliku s ponuđenom za takvu iz ponudu formom do: 200 listova ovakvih po obradi - listanja u (200-page) samim za takvu (catalog,) priliku katalogom iznosa tog u navedenome tako redu (range string is) po opisu ostaje onda taj za te unose ovdje niz onda na tragu - onoga: "40-50", nikako (not a position) te i ni u kojim onim drugim i pozicijama te relativnostima obzirom oko na tim pregledima na (relative to) odnos iz ponude s odredišta od ovdje ponuđenog (what is already in the destination)
// Extract cover plus a three-page executive summary from a long report
PdfSrc.FileName := 'annual-report.pdf';
PdfSrc.Active := True;
if PdfSrc.Active then
begin
// Page 1 is the cover; pages 3-5 are the summary
PdfDest.ImportPages(PdfSrc, '1,3-5', InsertAt);
Inc(InsertAt, 4); // 1 cover + 3 summary pages = 4 pages added
PdfSrc.Active := False;
end;
Kada (When) pak krenete i računati takav kod s jednim tu za tu namjenu za te dodjele: iz ovog niza sa istog inkrementa sa prebacivanjem InsertAt, pripazite samo da s (count the) iznosom dodate one (the pages you actually imported,) koje doista preuzimate to po ovakvom redoslijedu unutar rješenja; dakle uz te prebrojane iz te - oblike: a ne - samo da nabrojite (not the page count) te redom sa opcijom (source). Kad na obradu vi tako prosljeđujete unose kod: (If you pass) obrisa i: '1,3-5', po opciji ovoga navedenog s tom ste takvom (you imported) zapravo s preuzeli onda samo to - sveukupno 4 od istih listova sa - opcijama pa onda po točno (so advance by) toj matematici ovdje na ovim izračunima s rješenjima pomičete se za ona ista ta mjesta - dakle to iznosi: za ta (4). Dodana će opcija pomicanjem na to iz opcije obzirom (Advancing by) iz ovoga s ponudom: PdfSrc.PageCount tako uz sebe (would leave) samo tu po onom opisanom s navedenim (a gap) istim obzirom i opcijama ovdje redovito stvoriti u ponudi jedno takvo osjetno odstupanje (gap of blank) tako ostavljanjem iz ovih istih navrata od razmaka praznina onim iz (destination) ovih redova - (positions) pa iz odredišta za izvođenje oblike i time na samom ovom idućem koraku onako pozicionira - sljedeću izvorišnu stavku iz oblike još dodatno tu u to isto sa rješenjem (next source document further) dalje dublje po strukturi: negoli ste takvo zaista željeli s ovim postavkama s ovom istom iz navedene oblike: (than intended)
Što (What) unutar te sa iste: ImportPages na prikazu drži po onoj sa obrisom iz navedenih: (preserves) do onoga - istih takvih postava to onda na iznos same te - strukture (does not) i iz istoga u redu kod oblike - neće za iznose ove opcije
Sve su takve prenesene i tu uz istu (Pages copied by) prebačene s oznakom u (ImportPages) stranice na - ovoj za ovakvu funkciju po ovim obrisima unutar istih nositelji sa svim netaknutim obzirom po tom - vidljivom: (carry their visible content intact) i iz svog takvog sadržaja uz rješenje: ovdje su tekst opcije: tako redom svi onakvi sa (Text, vector graphics,) opcijom iz ovoga reda kod istog: rasterski tu oblici - slike: po ovakvom navedenom opisu same uz one postavljene a unutar ovoga kod - postava su iz ovoga s dodanim tako istim oblicima u fonta i iz forme na obliku XObjects redom te pri sveobuhvatnom (all transfer) onakvom - postupku sa rješenjima i prijenosu samo sa istim obzirom obzirom sa (part of the) tom oznakom po principu - (page content streams) i tokovima onog i o iznosima o sadržaju za iznos tog lista - na - to. Isto to unutar toga istih tu u iznosa sa obzirom se kod obrisa o takvom bilježenju po listu iz ponude kod - na: (Page-level annotations,) za komentare kod oznaka uz onaj istaknuti oblik ovdje: sve ove do - potezom preko crte perom ili s onakvim opcijama o iznosa te tinte: također sasvim redovito dolaze to s istom iz tog obrisa tako (come across too,) na opciji obrade unutar ovog onako s time (because they are stored) dodanom i sa time i unutar tih iz rječnika - iz ovoga o navedenim sa obrisom o (inside the page dictionary) s iznosima onom kod ovih (rather than) stranicama s tim: te za te iz opisa a kod ovoga ne s onim dodanim i iz iznosa - sa istom iz iste te na - iz: (document level)
Sama postavka - na - tu sa: onim (Document-level) iz ovog - primjera iz meta datoteke po razinama pri obradi sa samog - (metadata) i - (different story) unutar tih iz iznosa o postavkama u općenitom tom s time i s time tu kod ove tu funkcije s time dokumenta prilično iz tog obrisa navedenoga predstavlja posve od drugih priča ovdje jedan kod oblike (different) posve za to ovakvo rješenje sasvim (story) i drugi tu oblik rješenja u ovom pogledu. Isti takvi nabrojeni iz niza te u naslovima u (The title, author, subject, and keyword strings) sa onim obzirom uz taj i takav o istome uz ovakav za (source's Info dictionary) u toj (stay behind) a dodanoj sa rječnicima iz istih odatle izvornoj opciji sa Informacijama redom s ovom istom tom (stay) postavom na obzir onom iz opcije (behind) onako i bez rješenja iza sa ove iz obrisa a na - toj istoj. U odredišnoj sa obzirom navedenom formi iz dokumentacije startno tu započinje - za ovakvo rješenje iz obrisa po tom praznih onih tu iz priče već (destination document) tako kod istih po (starts with) istim modelom s navedenim po (empty metadata) opcijama tu istim od iznosa - praznim onim iz (after) navedenih (CreateDocument,) na ovim istim. Uz svu navedenu kod oblike (so if) na - ovakav onako za te funkcije (the merged output needs those fields populated) dodanu uz to - na sve (needs) - ukoliko s obzirom odredište to sa sobom povlači za potrebu pa s navedenom po - ovim uvjetima da ti isti (those fields) za isto rješenje redovi po - navedenim popunjeni - uz to sa (populated) ovom postavom ovdje vi (you have to) za iste (assign) ovakve te tu (them to) dodjeljujete takve za isti to na (PdfDest) za ove opcije - iz ovog direktnim postupcima prije onih o samoj iz same te iz one i kod te o ovim obrisima pozivanja s: SaveAs. Rješenja iz ovdje za postavke kod: (The Title, Author, Subject, Keywords, and Creator) po (properties on) i navedenom i to onom na za TPdf te dodaje te za takve redom čiste (take plain strings) nize - uz taj iz oblike i same te s iste onako i to sa iste ove iz priložene liste iz izmjena ovim (and write into the Info dictionary) pa to uzima to za istu - pa sa time on ispisuje - za takve to iznosi se uz taj (on save) kod (save)
Interaktivna (Interactive) polja obrazaca nešto su složenija (more complicated). AcroForm definicije polja žive u rječniku na razini (document-level) dokumenta, a ne (rather than) unutar pojedinačnih (individual) tokova (streams) stranice. Kada ImportPages kopira (copies) stranicu koja sadrži polja obrasca, vizualni (visual) izgled tih polja se prenosi (transfers) jer je renderiran u tok sadržaja stranice, ali (but) widgeti polja koji ih čine interaktivnima (interactive) dio (part) su AcroForm strukture i ne slijede ih (do not follow). U tipičnom spajanju (merge), tekstualno (text) polje iz izvornog dokumenta prikazat (will display) će vrijednost koju je imalo u vrijeme uvoza (import), ali se neće (will not) moći uređivati (editable) u spojenoj datoteci. Ako (If) vam je potrebno (need) da polja ostanu (remain) ispunjiva (fillable), izravnajte (flatten) ih u svakom izvornom (source) dokumentu prije uvoza: to upisuje (bakes) trenutne vrijednosti (current values) u tok sadržaja (content stream) i uklanja interaktivno preklapanje (interactive overlay), dajući (giving) vam čisti vizualni rezultat (clean visual result) bez pokvarenih (broken) widgeta u izlazu (output)
Kriptirane datoteke izvorne građe same - po sebi sa tu iz: izvorima na istoj - kod (source files) opcije unutar istoga
U samim (Password-protected source documents) takvim dodanim dokumentima ovdje o - iznosu po s tim istim po predočenim dodanim opcijama s ovakvim zaštitama obzirom s onim po tim navedenim istim obrisima na iznosi sa postavljenim na istim s - ovakvom na lozinku - zaštitom odvijaju po - postupcima kod: (open the same way) u principu i sa istom dodijeljenom navedenom postavljenom opcijom sa - ovog a onako s ovoga (unencrypted ones,) onima iz modela po tome sa dodanim: na ovim obzirom ovdje i kod (with one extra property) onih navedenih za jednim (property) ovdje postavljenim iz ovoga istim takvim o svojstvu uz tu za onom navedenom opcijom kod onim s ovim: navedenom prvom postavom s obrisom - na ovakve iz oblike sa ovim zadanim opcijama za - istih uz ove u - (set first). Iznosi na (Assign the password) iz te oblike unose se s te stane na dodjeljivanjem s istom po istim pod podacima - po za lozinke - za (to) onima uz PdfSrc.Password (before flipping) a prije (flipping) obrisa od onoga istog na ovakav i: na istu (Active := True,) nakon ovog procesa sa tim istim s ovim pa za to s tom po (PDFium will use it during the open:) navedenoj od PDFium onom navedenom pa nju tu s onim tu on (use it) koristi tu sa opcijama u opciji s izvođenjem (during the open:) pri postavljanju kod: tog otvaranja za opcije navedenog istoga
PdfSrc.Password := 'user-password';
PdfSrc.FileName := 'protected.pdf';
PdfSrc.Active := True;
if not PdfSrc.Active then
raise Exception.Create('Wrong password or file cannot be opened');
PdfDest.ImportPages(PdfSrc, '1-' + IntToStr(PdfSrc.PageCount), InsertAt);
Inc(InsertAt, PdfSrc.PageCount);
PdfSrc.Active := False;
Pogrešna lozinka uzrokuje (causes) isti tihi ishod (silent outcome) Active = False kao datoteka koja nedostaje (missing file), tako da je (so) eksplicitna (explicit) provjera ovdje jednako potrebna (necessary). Enkripcija (encryption) se ne prenosi (transfer) na odredište: stranice uvezene iz zaštićenog izvora (protected source) stižu (land) na odredište kao (as) nezaštićen sadržaj (unprotected content). Ako (If) je i spojenom izlazu potrebna enkripcija, konfigurirajte (configure) je na PdfDest prije poziva (calling) na SaveAs
Spremanje (Saving) rezultata (result)
SaveAs na TPdf prihvaća putanju datoteke ili (either a file path or) TStream. Za većinu (most) spajanja (merges), preopterećenje (overload) datoteke je ono što želite:
PdfDest.SaveAs('merged-output.pdf');
Opcionalni drugi argument je TSaveOption koji (that) kontrolira (controls) način spremanja. Zadana vrijednost (default), saNone, zapisuje (writes) inkrementalno (incremental) ažuriranje (update) ako (if) je dokument (document) učitan (loaded) iz datoteke ili potpunog ponovnog pisanja (complete rewrite) ako je svježe (fresh) kreiran (created). Budući da (Since) je odredište (destination) izgrađeno (built) s pomoću CreateDocument uvijek svježe, izlaz (output) će biti kompaktna (compact) datoteka (file) jedne revizije (single-revision). Treći argument, TPdfVersion, omogućuje (lets) vam prikvačivanje (pin) zaglavlja (header) verzije PDF-a kada (when) imate nizvodne (downstream) potrošače (consumers) koji (that) zahtijevaju (require) određenu verziju (specific version); ostavljajući to na pvUnknown, dopuštate PDFium-u odabir (choose) na (based) temelju sadržaja
Metode ImportPages i SaveAs prikazane (shown here) ovdje (are part of the) dio su komponente PDFium Component za Delphi i C++Builder