PDFium VCL ponuja združevanje datotek PDF prek ene same metode: ImportPages. Vzorec je vedno enak: ustvarite prazen ciljni dokument, odprite vsako izvorno datoteko, pokličite ImportPages, da prekopirate strani, zaprite vir in postopek ponovite. Ko se zanka zaključi, metoda SaveAs zapiše rezultat na disk. Ni posebnega načina združevanja in ni nastavitev, ki bi jih bilo treba preklopiti. Zapletenost se skriva v robnih primerih, med katerimi vas lahko nekateri presenetijo brez opozorila.
Glavna zanka
Potrebujete le dva primerka razreda TPdf. Eden predstavlja ciljni dokument, ki ga ustvarite praznega z metodo CreateDocument. Drugi pa zaporedoma odpira posamezne izvorne datoteke. Spodaj je prikazana procedura, ki sprejme seznam poti do datotek in zapiše združeni izhod na eno skupno pot:
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;
Dve stvari v tej kodi je pri prvem branju zlahka spregledati. Prva je način, kako PDFium javlja napake pri nalaganju. Nastavitev Active := True nikoli ne sproži izjeme: če datoteka manjka, je poškodovana ali zaščitena z geslom, PDFium interno ujame napako in pusti lastnost Active na vrednosti False. Brez izrecnega preverjanja v 10. vrstici bi poškodovana datoteka tiho izpadla iz združevanja brez kakršnega koli opozorila v izhodu. Končni PDF bi imel manj strani od pričakovanih, vi pa ne bi vedeli, katera datoteka je povzročila težavo.
Druga stvar je števec InsertAt. Tretji argument metode ImportPages je položaj v ciljnem dokumentu (indeksiran od 1 naprej), kamor se uvrsti prva uvožena stran. Začetek pri 1 postavi prvi izvorni dokument na začetek sicer prazne datoteke. Po vsakem viru se števec poveča za PdfSrc.PageCount, tako da se naslednji paket strani pripne na konec prejšnjega. Če pozabite povečati ta števec, bo vsak naslednji vir prepisal strani na položaju 1, s čimer boste v končnem dokumentu dobili le zadnjo datoteko s seznama.
Izbirni obsegi strani
Iz vira vam ni treba uvoziti vseh strani. Niz obsega, ki ga posredujete kot drugi argument, sledi preprosti obliki z vejicami in vezaji: "1-3" uvozi strani od 1 do 3, "2,4,6" izbere tri določene strani, "1-" pa pomeni od 1. strani do konca dokumenta. Obsege lahko združite v en niz, tako da "1-3,5,7-" preskoči strani 4 in 6. Tukaj je pomembna ena podrobnost: številke se vedno nanašajo na strani v izvornem dokumentu (začevši z 1), ne glede na to, kam se te strani uvrstijo v ciljnem dokumentu. Če želite strani od 40 do 50 iz 200-stranskega kataloga, je niz obsega "40-50" in ne položaj glede na to, kar je že v ciljnem dokumentu.
// 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;
Pri izračunu povečanja za InsertAt preštejte strani, ki ste jih dejansko uvozili, in ne celotnega števila strani v viru. Če posredujete '1,3-5', ste uvozili 4 strani, zato števec povečajte za 4. Povečanje za PdfSrc.PageCount bi pustilo prazne položaje v ciljnem dokumentu in postavilo naslednji izvorni dokument globlje v datoteko, kot je bilo načrtovano.
Kaj ImportPages ohrani in česa ne
Strani, kopirane z metodo ImportPages, v celoti ohranijo svojo vidno vsebino. Besedilo, vektorska grafika, rastrske slike, vgrajene pisave in obrazni predmeti XObject se prenesejo kot del tokov vsebine strani. Prenesejo se tudi komentarji, oznake in risbe na ravni strani (opombe), saj so shranjeni v slovarju strani in ne na ravni dokumenta.
Metapodatki na ravni dokumenta so druga zgodba. Naslov, avtor, zadeva in ključne besede v slovarju Info v viru se ne prenesejo. Ciljni dokument se po klicu CreateDocument začne s praznimi metapodatki, zato morate te lastnosti dodeliti objektu PdfDest neposredno pred klicem metode SaveAs, če želite, da so polja izpolnjena. Lastnosti Title, Author, Subject, Keywords in Creator v razredu TPdf sprejemajo navadne nize in se ob shranjevanju zapišejo v slovar Info.
Interaktivna obrazna polja so bolj zapletena. Definicije polj AcroForm se nahajajo v slovarju na ravni dokumenta in ne v posameznih tokovih strani. Ko metoda ImportPages kopira stran, ki vsebuje obrazna polja, se njihov vizualni izgled prenese (saj je izrisan v tok vsebine strani), vendar se gradniki polj, ki omogočajo njihovo interaktivnost, ne prenesejo, ker so del strukture AcroForm. Pri običajnem združevanju bo besedilno polje iz izvornega dokumenta prikazalo vrednost, ki jo je imelo ob uvozu, vendar v združeni datoteki ne bo več urejevalno. Če želite, da polja ostanejo izpolnljiva, jih pred uvozom sploščite (flatten) v vsakem izvornem dokumentu: to zapeče trenutne vrednosti v tok vsebine in odstrani interaktivni prekrivni sloj, kar zagotavlja čist vizualni rezultat brez nedelujočih gradnikov v izhodu.
Šifrirane izvorne datoteke
Z geslom zaščiteni izvorni dokumenti se odpirajo enako kot nešifrirani, le da je treba najprej nastaviti dodatno lastnost. Geslo dodelite lastnosti PdfSrc.Password pred preklopom Active := True in PDFium ga bo uporabil med odpiranjem:
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;
Napačno geslo povzroči enak tihi neuspeh Active = False kot manjkajoča datoteka, zato je izrecno preverjanje nujno tudi tukaj. Šifriranje se ne prenese v ciljni dokument: strani, uvožene iz zaščitenega vira, se v ciljnem dokumentu pojavijo kot nezaščitena vsebina. Če mora biti tudi združeni izhod šifriran, konfigurirajte šifriranje na objektu PdfDest pred klicem metode SaveAs.
Shranjevanje rezultata
Metoda SaveAs v razredu TPdf sprejme pot do datoteke ali TStream. Za večino združevanj boste uporabili preobremenitev z datoteko:
PdfDest.SaveAs('merged-output.pdf');
Izbirni drugi argument je TSaveOption, ki nadzoruje način shranjevanja. Privzeta vrednost saNone zapiše postopno posodobitev, če je bil dokument naložen iz datoteke, ali popolnoma nov zapis, če je bil ustvarjen na novo. Ker je ciljni dokument, zgrajen s CreateDocument, vedno nov, bo izhod kompaktna datoteka z eno revizijo. Tretji argument, TPdfVersion, omogoča določitev različice glave PDF, če vaši sistemi zahtevajo določeno različico; če jo pustite na pvUnknown, bo PDFium sam izbral različico glede na vsebino.
Tukaj prikazani metodi ImportPages in SaveAs sta del komponente PDFium VCL Component za Delphi in C++Builder.