Class PdfMerger


  • public final class PdfMerger
    extends java.lang.Object
    Public entry point of the PD4ML PDF merge API. Combines page ranges selected independently from two (or more) existing PDF files into one output document, built entirely on the pd4ml com.pd4ml.pdf.cos object model/parser (no PDFBox, no other PDF library) -- the same foundation com.pd4ml.pdf.sign is built on.

    Every selected page is deep-cloned into a fresh object-numbering space (see COSObjectImporter), so the two sources' indirect object numbers never collide, regardless of how much they overlap in the originals. Each selected page's accessibility structure (tagging) is reconciled into a single merged /StructTreeRoot on a best-effort basis (see StructureTreeMerger): pages from a tagged source keep their structure, pages from an untagged source are simply included without any, and the merged document is tagged overall only if at least one selected page contributed structure.

    Encrypted source PDFs are supported: pass the source's user or owner password to the password-taking addSource overload (an empty or omitted password is tried too, covering the common case of a document that only restricts permissions with an owner password). The pd4ml COS reader decrypts transparently while parsing, so every page cloned from an encrypted source is handled exactly like one from a plain PDF; a source is only rejected if the given password fails to open it.

    The merged output itself can also be encrypted -- see encryptOutput(String, String) / encryptOutput(PdfEncryptor.Options).

    With exactly one source added, calling selectPages(...) is optional: with no selection, the result is that source's own pages, in their original order (decrypted, if the source was encrypted, unless encryptOutput(java.lang.String, java.lang.String) was also called) -- i.e. a filter/decrypt/re-encrypt pass rather than a true merge. With two or more sources, every source still requires an explicit selectPages(...) call.

    Not supported (v1): merging /AcroForm field hierarchies, named destinations, or outlines (their absence doesn't corrupt the output -- widget annotations still come along fine as ordinary page /Annots -- only the document-level field tree/outline tree is not reconciled).

    Thread-safety: a single PdfMerger instance may safely be shared across threads -- addSource(...)/encryptOutput(...) may be called concurrently (e.g. to add several sources in parallel), and mergeToBytes()/merge(...) always reads a consistent snapshot of whatever sources had been fully added by the time it runs. A source is only guaranteed to be included in a merge call that starts after its addSource(...) call has returned; racing addSource(...) against an in-flight mergeToBytes() call on the same instance may or may not pick it up, but never corrupts either call's result. Each PdfMergeSource returned by addSource(...) is likewise safe to hand off to another thread before that thread calls selectPages(...) on it.

    Example

    
     PdfMerger merger = new PdfMerger();
     merger.addSource(pdfABytes).selectPages("2-5,odd");
     merger.addSource(pdfBBytes, "sourceBPassword").selectPages("1,3,6+");
     merger.encryptOutput("openPassword", "ownerPassword");
     byte[] merged = merger.mergeToBytes();