PDFium Component expune îmbinarea de fișiere PDF printr-o singură metodă: ImportPages. Modelul este mereu același: creați un document destinație gol, deschideți fiecare fișier sursă, apelați ImportPages pentru a copia paginile, închideți sursa și repetați. Când bucla se termină, SaveAs scrie rezultatul pe disc. Nu există niciun mod special de îmbinare, nicio configurare de comutat. Complexitatea trăiește în cazurile limită, iar câteva dintre ele mușcă fără avertisment
Bucla principală
Două instanțe TPdf sunt tot ce vă trebuie. Una ține documentul destinație, creat gol cu CreateDocument. Cealaltă deschide pe rând fiecare fișier sursă. Mai jos este o procedură care primește o listă de căi de fișiere și scrie rezultatul îmbinat într-o singură cale:
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 folosește o poziție destinație bazată pe 1
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), // intervalul întregului document
InsertAt);
Inc(InsertAt, PdfSrc.PageCount);
PdfSrc.Active := False;
end;
PdfDest.SaveAs(OutputPath);
finally
PdfSrc.Free;
PdfDest.Free;
end;
end;
Două lucruri din acel cod sunt ușor de trecut cu vederea la prima citire. Primul este modul în care PDFium raportează eșecurile de încărcare. Active := True nu ridică niciodată o excepție: dacă fișierul lipsește, este deteriorat sau protejat prin parolă, PDFium prinde intern eroarea și lasă Active pe False. Fără verificarea explicită de la linia 10, un fișier defect ar cădea tacit din îmbinare, fără nicio indicație în rezultat. PDF-ul final ar avea mai puține pagini decât cele așteptate și nu ați ști care fișier a fost vinovatul
Al doilea este contorul InsertAt. Al treilea argument al lui ImportPages este poziția bazată pe 1 din destinație unde ajunge prima pagină importată. Pornirea de la 1 pune primul document sursă la începutul unui fișier altfel gol. După fiecare sursă, contorul avansează cu PdfSrc.PageCount, așa că următorul lot de pagini se adaugă după ultima. Uitați să îl incrementați și fiecare sursă ulterioară suprascrie paginile de la poziția 1, oferindu-vă doar ultimul document din listă și nimic altceva
Intervale selective de pagini
Nu trebuie să luați fiecare pagină dintr-o sursă. Șirul de interval transmis ca al doilea argument urmează un format simplu, cu virgulă și cratimă: "1-3" ia paginile 1 până la 3, "2,4,6" alege trei pagini specifice, iar "1-" înseamnă de la pagina 1 până la sfârșitul documentului. Intervalele pot fi combinate într-un singur șir, așa că "1-3,5,7-" omite paginile 4 și 6. O subtilitate contează aici: numerele se referă întotdeauna la paginile din documentul sursă, începând de la 1, indiferent unde ajung acele pagini în destinație. Dacă vreți paginile 40 până la 50 dintr-un catalog de 200 de pagini, șirul de interval este "40-50", nu o poziție relativă la ce se află deja în destinație
// Extrage coperta plus un sumar executiv de trei pagini dintr-un raport lung
PdfSrc.FileName := 'annual-report.pdf';
PdfSrc.Active := True;
if PdfSrc.Active then
begin
// Pagina 1 este coperta; paginile 3-5 sunt sumarul
PdfDest.ImportPages(PdfSrc, '1,3-5', InsertAt);
Inc(InsertAt, 4); // 1 copertă + 3 pagini de sumar = 4 pagini adăugate
PdfSrc.Active := False;
end;
Când calculați incrementul pentru InsertAt, numărați paginile pe care le-ați importat efectiv, nu numărul de pagini al sursei. Dacă transmiteți '1,3-5' ați importat 4 pagini, așa că avansați cu 4. Avansarea cu PdfSrc.PageCount ar lăsa un gol de poziții de destinație goale și ar plasa următorul document sursă mai departe în fișier decât s-a intenționat
Ce păstrează ImportPages, și ce nu
Paginile copiate de ImportPages poartă conținutul lor vizibil intact. Textul, graficele vectoriale, imaginile raster, fonturile înglobate și obiectele XObjects de formular se transferă toate ca parte a fluxurilor de conținut de pagină. Adnotările la nivel de pagină, inclusiv comentariile, evidențierile și trasările de cerneală, trec și ele, pentru că sunt stocate în interiorul dicționarului de pagină, nu la nivel de document
Metadatele la nivel de document sunt o altă poveste. Șirurile de titlu, autor, subiect și cuvinte cheie din dicționarul Info al sursei rămân în urmă. Documentul destinație pornește cu metadate goale după CreateDocument, așa că dacă rezultatul îmbinat are nevoie ca acele câmpuri să fie populate, trebuie să le atribuiți direct lui PdfDest înainte de a apela SaveAs. Proprietățile Title, Author, Subject, Keywords și Creator de pe TPdf preiau șiruri simple și scriu în dicționarul Info la salvare
Câmpurile de formular interactive sunt mai complicate. Definițiile de câmpuri AcroForm trăiesc într-un dicționar la nivel de document, nu în interiorul fluxurilor de pagină individuale. Când ImportPages copiază o pagină care conține câmpuri de formular, aspectul vizual al acelor câmpuri se transferă pentru că este randat în fluxul de conținut al paginii, dar widget-urile de câmp care le fac interactive fac parte din structura AcroForm și nu urmează. Într-o îmbinare tipică, un câmp de text dintr-un document sursă va afișa valoarea pe care o avea la momentul importului, dar nu va fi editabil în fișierul îmbinat. Dacă aveți nevoie ca acele câmpuri să rămână completabile, aplatizați-le în fiecare document sursă înainte de import: asta încorporează valorile curente în fluxul de conținut și elimină stratul interactiv, oferindu-vă un rezultat vizual curat, fără widget-uri stricate în rezultat
Fișiere sursă criptate
Documentele sursă protejate prin parolă se deschid la fel ca cele necriptate, cu o proprietate suplimentară de setat mai întâi. Atribuiți parola lui PdfSrc.Password înainte de a comuta Active := True, iar PDFium o va folosi în timpul deschiderii:
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;
O parolă greșită cauzează același rezultat tăcut Active = False ca un fișier lipsă, așa că verificarea explicită este la fel de necesară și aici. Criptarea nu se transferă în destinație: paginile importate dintr-o sursă protejată ajung în destinație ca și conținut neprotejat. Dacă rezultatul îmbinat are nevoie și de criptare, configurați-o pe PdfDest înainte de a apela SaveAs
Salvarea rezultatului
SaveAs de pe TPdf acceptă fie o cale de fișier, fie un TStream. Pentru majoritatea îmbinărilor, supraîncărcarea pentru fișier este cea de care aveți nevoie:
PdfDest.SaveAs('merged-output.pdf');
Al doilea argument opțional este un TSaveOption care controlează modul de salvare. Valoarea implicită, saNone, scrie o actualizare incrementală dacă documentul a fost încărcat dintr-un fișier, sau o rescriere completă dacă a fost creat proaspăt. Din moment ce o destinație construită cu CreateDocument este întotdeauna proaspătă, rezultatul va fi un fișier compact cu o singură revizie. Al treilea argument, TPdfVersion, vă permite să fixați antetul versiunii PDF atunci când aveți consumatori din aval care necesită o versiune specifică; lăsându-l la pvUnknown permiteți PDFium să aleagă în funcție de conținut
Metodele ImportPages și SaveAs prezentate aici fac parte din PDFium Component pentru Delphi și C++Builder