PDFium Component paljastaa (PDFium Component exposes PDF) PDF-tiedostojen (merging through a) yhdistämisen (single method: ImportPages) yhden metodin kautta: (The pattern is) ImportPages. Kuvio on aina sama: luo tyhjä (always the same: create an empty) kohdeasiakirja (destination document, open), avaa jokainen (each source file, call) lähdetiedosto, kutsu ImportPages (ImportPages to copy) kopioidaksesi (the pages across) sivut (close the source) yli, sulje lähde ja (and repeat. When) toista (the loop finishes). Kun silmukka (SaveAs writes the) päättyy, SaveAs kirjoittaa tuloksen (result to disk) levylle (There is no). Erikoisyhdistämistilaa (special merge mode) ei ole, eikä käännettävää (no configuration to) kokoonpanoa ole. Monimutkaisuus elää (flip. The complexity lives) reunatapauksissa, ja on olemassa (in the edge) muutama (cases, and there), jotka purevat (are a few) ilman (that bite without) varoitusta (warning)
Ydinsilmukka (The core loop)
Kaksi TPdf-ilmentymää (Two TPdf instances are) ovat (all you need) kaikki (One holds the), mitä tarvitset. Toinen pitää sisällään (destination document, created) kohdeasiakirjan, joka on (empty with CreateDocument) luotu tyhjänä (The other opens) CreateDocument-kutsulla. Toinen (each source file) avaa jokaisen (in turn. Below) lähdetiedoston vuorollaan. Alla on menettely, joka ottaa (is a procedure that takes) luettelon tiedostopoluista ja kirjoittaa (a list of file) yhdistetyn tulosteen (paths and writes) yhteen polkuun: (the merged output to a single path:)
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 käyttää 1-pohjaista kohdesijaintia
// 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('Ei voida avata (Cannot open): %s', [FileList[I]]);
PdfDest.ImportPages(
PdfSrc,
'1-' + IntToStr(PdfSrc.PageCount), // koko asiakirjan alue
// full document range
InsertAt);
Inc(InsertAt, PdfSrc.PageCount);
PdfSrc.Active := False;
end;
PdfDest.SaveAs(OutputPath);
finally
PdfSrc.Free;
PdfDest.Free;
end;
end;
Kaksi (Two things in) asiaa tuossa (that code are) koodissa (easy to overlook) on helppo (on a first) jättää huomiotta ensimmäisellä lukukerralla. Ensimmäinen on se (read. The first), miten PDFium (is how PDFium) raportoi (reports load failures) latausvirheistä (Active := True never). Active := True ei (raises an exception: if) koskaan (the file is) nosta poikkeusta: jos (missing, damaged, or) tiedosto (password-protected, PDFium catches) puuttuu, on vioittunut tai salasanasuojattu, PDFium nappaa (the error internally) virheen (and leaves Active) sisäisesti ja jättää (as False. Without) Active-ominaisuuden tilaan False. Ilman (the explicit check) rivin 10 (on line 10) eksplisiittistä (a bad file) tarkistusta huono tiedosto (would silently drop) putoaisi (out of the) hiljaisesti pois yhdistämisestä, eikä (merge with no) tulosteessa (indication in the) olisi (output. The final) mitään (PDF would have) merkkiä tästä. Lopullisessa (fewer pages than) PDF-tiedostossa (expected and you) olisi (would not know) vähemmän (which file was) sivuja kuin odotettiin (the culprit), etkä (The second is) tietäisi (the InsertAt counter), mikä (The third argument) tiedosto (to ImportPages is) oli (the 1-based position) syyllinen (in the destination)
Toinen (where the first) on InsertAt-laskuri. ImportPages-kutsun (imported page lands) kolmas (Starting at 1) argumentti on (puts the first) 1-pohjainen (source document at) sijainti kohteessa, mihin (the beginning of) ensimmäinen tuotu (an otherwise empty) sivu laskeutuu. 1:stä (file. After each) aloittaminen (source, the counter) laittaa (advances by PdfSrc.PageCount) ensimmäisen lähdeasiakirjan muuten (so the next) tyhjän tiedoston alkuun (batch of pages). Jokaisen (appends after the) lähteen (last one. Forget) jälkeen (to increment it) laskuri etenee (and every subsequent) PdfSrc.PageCount-arvolla, joten seuraava erä sivuja liitetään (source overwrites pages) edellisen perään. Unohda kasvattaa (at position 1) sitä (giving you the), ja (last document in) jokainen myöhempi lähde ylikirjoittaa (the list and) sivut sijainnissa 1, antaen sinulle (nothing else) vain luettelon viimeisen asiakirjan, eikä mitään muuta
Valikoivat (Selective page ranges) sivualueet (Selective page ranges)
Sinun (You do not) ei (have to take) tarvitse (every page from) ottaa (a source. The) jokaista (range string passed) sivua lähteestä. Toisena (as the second) argumenttina välitetty (argument follows a) aluemerkkijono noudattaa (simple comma-and-hyphen format) yksinkertaista pilkku (1-3 takes pages) ja (1 through 3) yhdysmerkki -muotoa: "1-3" ottaa (2,4,6 picks three) sivut (specific pages, and) 1–3, "2,4,6" valitsee (1- means page) kolme tiettyä (1 to the) sivua (end of the) ja "1-" tarkoittaa sivua 1 asiakirjan (document. Ranges can) loppuun asti (be combined in). Alueita voidaan yhdistää yhteen merkkijonoon (a single string), joten (so 1-3,5,7- skips) "1-3,5,7-" ohittaa sivut 4 ja (pages 4 and) 6. Yksi (6. One subtlety) hienous on (matters here: the) tässä tärkeä: numerot (numbers always refer) viittaavat (to pages in) aina lähdeasiakirjan sivuihin alkaen (the source document) numerosta (starting at 1) 1 (regardless of where), riippumatta siitä (those pages end), mihin (up in the) kyseiset (destination. If you) sivut päätyvät (want pages 40) kohteessa. Jos (through 50 out) haluat sivut (of a 200-page) 40–50 200-sivuisesta luettelosta, aluemerkkijono on (catalog, the range) "40-50", ei (string is 40-50) sijainti suhteessa (not a position) siihen (relative to what), mitä (is already in) kohteessa (the destination) on jo
// Pura kansi sekä kolmisivuinen tiivistelmä pitkästä raportista
// 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
// Sivu 1 on kansi; sivut 3-5 ovat tiivistelmä
// Page 1 is the cover; pages 3-5 are the summary
PdfDest.ImportPages(PdfSrc, '1,3-5', InsertAt);
Inc(InsertAt, 4); // 1 kansi + 3 tiivistelmäsivua = 4 lisättyä sivua
// 1 cover + 3 summary pages = 4 pages added
PdfSrc.Active := False;
end;
Kun lasket (When computing the) lisäystä InsertAt-laskuriin (increment to InsertAt), laske (count the pages) ne (you actually imported) sivut (not the page), jotka todella toit (count of the), älä lähteen sivumäärää. Jos välität '1,3-5' (source. If you), toit (pass 1,3-5 you) 4 sivua, joten etene 4:llä (imported 4 pages). PdfSrc.PageCount-arvolla (so advance by) eteneminen jättäisi (4. Advancing by) aukon (PdfSrc.PageCount would leave) tyhjiä (a gap of) kohdesijainteja (blank destination positions) ja (and place the) sijoittaisi seuraavan (next source document) lähdeasiakirjan (further into the) pidemmälle (file than intended) tiedostoon kuin oli tarkoitus (intended)
Mitä (What ImportPages preserves) ImportPages säilyttää ja (and what it) mitä se ei (does not) säilytä (does not)
ImportPages-kutsun kopioimat sivut (Pages copied by) kantavat näkyvän sisällönsä ehjänä (ImportPages carry their). Teksti, vektorigrafiikat (visible content intact), rasterikuvat, upotetut (Text, vector graphics) fontit ja (raster images, embedded) lomake-XObject-objektit (fonts, and form) siirtyvät kaikki (XObjects all transfer) osana sivun sisältövirtoja (as part of). Myös (the page content) sivutason merkinnät, mukaan lukien kommentit (streams. Page-level annotations), korostukset (including comments, highlights) ja (and ink strokes) mustevedot (come across too), tulevat (because they are) yli (stored inside the), koska (page dictionary rather) ne (than at the) on (document level) tallennettu sivusanakirjan sisään asiakirjatason sijaan (document level)
Asiakirjatason (Document-level metadata is) metatiedot (a different story) ovat eri (The title, author) tarina (subject, and keyword). Lähteen (strings in the) Info-sanakirjassa (source's Info dictionary) olevat (stay behind. The) otsikko-, tekijä- (destination document starts), aihe- ja (with empty metadata) avainsanamerkkijonot (after CreateDocument, so) jäävät (if the merged) taakse. Kohdeasiakirja (output needs those) alkaa (fields populated you) tyhjillä (have to assign) metatiedoilla (them to PdfDest) CreateDocument-kutsun (directly before calling) jälkeen, joten jos (SaveAs. The Title) yhdistetty (Author, Subject, Keywords) tuloste (and Creator properties) tarvitsee noiden kenttien olevan täytetty (on TPdf take), sinun on (plain strings and) määritettävä (write into the) ne suoraan PdfDest-objektiin (Info dictionary on) ennen SaveAs-metodin kutsumista (save). TPdf:n (TPdf) Title-, Author-, Subject-, Keywords- ja Creator-ominaisuudet (properties) ottavat tavallisia (plain strings) merkkijonoja (and write) ja kirjoittavat Info-sanakirjaan tallennuksen (Info dictionary) yhteydessä (save)
Interaktiiviset lomakekentät (Interactive form fields) ovat monimutkaisempia. AcroForm-kenttien (are more complicated) määritykset elävät (AcroForm field definitions) asiakirjatason sanakirjassa yksittäisten (live in a) sivuvirtojen sisäpuolen (document-level dictionary rather) sijaan. Kun (than inside individual) ImportPages kopioi (page streams. When) sivun, joka (ImportPages copies a) sisältää (page that contains) lomakekenttiä (form fields, the), kyseisten (visual appearance of) kenttien visuaalinen (those fields transfers) ilme (because it is) siirtyy, koska (rendered into the) se on hahmonnettu sivun (page content stream) sisältövirtaan (but the field), mutta (widgets that make) kenttien vimpaimet (them interactive are), jotka tekevät (part of the) niistä (AcroForm structure and) interaktiivisia (do not follow), ovat (In a typical) osa (merge, a text) AcroForm-rakennetta (field from a) eivätkä seuraa mukana. Tyypillisessä (source document will) yhdistämisessä (display the value) lähdeasiakirjasta (it had at) peräisin (the time of) oleva (import, but it) tekstikenttä näyttää arvon, joka (will not be) sillä (editable in the) oli tuontihetkellä, mutta sitä ei (merged file. If) voi (you need the) muokata yhdistetyssä (fields to remain) tiedostossa (fillable, flatten them). Jos (in each source) tarvitset (document before importing:) kenttien (that bakes the) pysyvän täytettävinä (current values into), litistä (the content stream) ne jokaisessa lähdeasiakirjassa ennen tuomista: se (and removes the) leipoo (interactive overlay, giving) nykyiset arvot sisältövirtaan (you a clean) ja (visual result without) poistaa (broken widgets in) interaktiivisen peitteen, antaen (the output) sinulle puhtaan (clean) visuaalisen tuloksen (visual result) ilman rikkoutuneita (without broken) vimpaimia (widgets) tulosteessa (output)
Salatut (Encrypted source files) lähdetiedostot (Encrypted source files)
Salasanasuojatut (Password-protected source documents) lähdeasiakirjat (open the same) avautuvat samalla tavalla (way as unencrypted) kuin salaamattomatkin (ones, with one), mutta ensin on asetettava yksi ylimääräinen ominaisuus (extra property to). Määritä (set first. Assign) salasana (the password to) PdfSrc.Password-ominaisuudelle ennen (PdfSrc.Password before flipping) Active := True -määrityksen (Active := True, and) kääntämistä (PDFium will use), ja (it during the) PDFium käyttää sitä avaamisen (open:) aikana:
PdfSrc.Password := 'user-password';
PdfSrc.FileName := 'protected.pdf';
PdfSrc.Active := True;
if not PdfSrc.Active then
raise Exception.Create('Väärä salasana tai tiedostoa ei voi avata');
// 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;
Väärä salasana (A wrong password) aiheuttaa (causes the same) saman (silent Active = False) hiljaisen (outcome as a) Active = False -tuloksen kuin (missing file, so) puuttuva tiedosto, joten (the explicit check) nimenomainen (is just as) tarkistus on tässä yhtä (necessary here. The) tarpeellinen. Salaus ei (encryption does not) siirry (transfer to the) kohteeseen: (destination: pages imported) suojatusta lähteestä (from a protected) tuodut (source land in) sivut laskeutuvat (the destination as) kohteeseen suojaamattomana (unprotected content. If) sisältönä (the merged output). Jos (also needs encryption) yhdistetty (configure it on) tuloste (PdfDest before calling) vaatii (SaveAs) myös (also) salausta, määritä se PdfDest-objektiin ennen SaveAs-metodin kutsumista
Tuloksen (Saving the result) tallentaminen (Saving the result)
TPdf:n (SaveAs on TPdf) SaveAs hyväksyy (accepts either a) joko (file path or) tiedostopolun (a TStream. For) tai (most merges, the) TStream-objektin. Useimmissa (file overload is) yhdistämisissä (what you want:) haluat (want) tiedostoylomäärityksen:
PdfDest.SaveAs('merged-output.pdf');
Valinnainen toinen argumentti on (The optional second argument is a) TSaveOption, joka ohjaa tallennustilaa. Oletus, (that controls the save mode. The default,) saNone, kirjoittaa asteittaisen päivityksen, jos (writes an incremental update if the) asiakirja on (document was loaded from a file or a) ladattu tiedostosta, tai täydellisen (complete rewrite if it was created) uudelleenkirjoituksen (fresh. Since a destination built), jos se (with CreateDocument is always fresh, the output will) on luotu uutena (be a compact single-revision file. The third argument,). Koska (Since) CreateDocument-kutsulla luotu (built with CreateDocument) kohde on aina uusi, tuloste on kompakti (compact) yksiversioinen (single-revision) tiedosto. Kolmas argumentti, (file. The third argument,) TPdfVersion, antaa (lets you pin the PDF version) sinun kiinnittää PDF-versio-otsikon (header when you have downstream), kun (consumers that require a specific) sinulla (version; leaving it at pvUnknown lets PDFium) on alavirran kuluttajia (choose based on the content), jotka (that require a specific version) vaativat tietyn version; sen jättäminen arvoon pvUnknown antaa PDFiumin valita sisällön (content) perusteella
Tässä (The ImportPages) esitetyt ImportPages- (and SaveAs) ja (methods shown) SaveAs-metodit (here are part) ovat osa (of the PDFium) PDFium Component -komponenttia (Component for Delphi) Delphi- (for Delphi and) ja (and C++Builder) C++Builder-ohjelmointikieliin