Merging and Splitting PDFs Locally: File Structure, What Survives, What Breaks
PDF Merger & Page Combiner
Drag in multiple PDFs, order them, and export a single document. Parsing and assembly happen in browser memory, so contracts and statements are never uploaded.
Why PDF Editing Behaves Strangely
Most file formats are a linear stream you read from start to end. PDF is not. It is an object graph — a set of numbered objects that reference each other — plus an index telling a reader where each object lives in the file. That design is why a 400-page PDF opens instantly on page 300, and also why “deleting” a page does not necessarily delete anything.
The format is standardised as ISO 32000-1 (PDF 1.7) and ISO 32000-2:2020 (PDF 2.0). Understanding four structures explains every merge and split behaviour you will encounter.
What you get from this guide: a working mental model of the file, a checklist of what survives each operation, and the specific verification step that prevents accidentally shipping a “removed” confidential page.
The Four Structures That Matter
1. Objects
Everything is an object with a number and generation: dictionaries, arrays, numbers, strings, names, and streams (compressed binary blobs holding page content, fonts, and images).
3 0 obj
<< /Type /Page
/Parent 2 0 R
/MediaBox [0 0 595.28 841.89]
/Resources << /Font << /F1 7 0 R >> >>
/Contents 4 0 R >>
endobj
7 0 R is an indirect reference: “object 7, generation 0”. A page does not contain its font; it points at one. That indirection is the entire reason merging is cheap and also the reason resources get duplicated.
2. The cross-reference table
At the end of the file, the xref table (or, in modern files, a compressed xref stream) maps each object number to a byte offset. The trailer points to the catalog and to the xref itself:
trailer
<< /Size 12 /Root 1 0 R >>
startxref
14127
%%EOF
A reader opens a PDF by seeking to the end, reading startxref, jumping to the table, and only then loading the objects it needs.
3. The page tree
Pages are not a flat list. The catalog points to a /Pages node, which contains /Kids — pages or further nodes — and a /Count:
1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj
2 0 obj << /Type /Pages /Kids [3 0 R 5 0 R 9 0 R] /Count 3 >> endobj
This is what merging and splitting actually manipulate. A merge creates a new /Pages node whose /Kids array lists page objects copied from several sources, renumbering objects to avoid collisions and rewriting every reference. A split creates a catalog whose /Kids names only the pages you kept. Page content streams are copied verbatim, which is why merging is lossless for what is drawn on the page.
4. Incremental updates
PDF allows a writer to append: leave the original bytes untouched, write changed objects at the end, and add a new xref section that supersedes the old entries. Readers follow the newest table, so the document appears edited.
The consequence is blunt: the old objects are still in the file. A page “deleted” by an incremental update can be recovered by a parser that reads the previous xref generation, and its text can sometimes be pulled out by a naive extractor that scans all streams. Redacting by drawing a black rectangle over text is the same failure in a different costume — the text object remains underneath.
What Survives Merge and Split
| Element | Merge | Split | Notes |
|---|---|---|---|
| Page content (text, vectors) | ✅ | ✅ | Content streams copied byte-for-byte |
| Embedded fonts | ✅ | ✅ | Copied per source; often duplicated |
| Images / XObjects | ✅ | ✅ | Duplicated unless the library deduplicates by hash |
| Page size and rotation | ✅ | ✅ | /MediaBox and /Rotate are page-level |
| Bookmarks / outlines | ⚠️ | ⚠️ | Document-scope; frequently dropped or dangling |
| Internal links / named destinations | ⚠️ | ❌ | Break when the target page leaves the document |
| AcroForm fields | ⚠️ | ⚠️ | Field name collisions on merge; hierarchy loss on split |
| Digital signatures | ❌ | ❌ | Invalidated — the signed byte range no longer exists |
| Tags / accessibility structure | ⚠️ | ⚠️ | The /StructTreeRoot is document-scope; often lost |
| XMP / document metadata | ⚠️ | ⚠️ | Usually taken from the first input or regenerated |
| JavaScript actions | ❌ | ❌ | Commonly discarded |
Three practical rules follow:
- Sign last. Any signature applied before a merge or split is void afterwards. Assemble the document, then sign.
- Flatten forms before merging. Two source files with a field named
signaturecollide. Flattening converts fields to static page content and removes the ambiguity — at the cost of no longer being fillable. - Treat accessibility as a re-do. If the source PDFs were tagged for screen readers, verify the tag tree in the output; a merge that silently drops
/StructTreeRootproduces a document that fails an accessibility audit even though it looks identical.
Doing It in the Browser
Client-side PDF assembly is genuinely practical: the page tree is a data structure, and rewriting it needs no rendering. A library such as pdf-lib parses the objects, copies pages, and serialises a new file entirely in browser memory.
import { PDFDocument } from "pdf-lib";
// MERGE: copy every page from each input into one output document.
async function mergePdfs(fileBuffers) {
const out = await PDFDocument.create();
for (const buf of fileBuffers) {
const src = await PDFDocument.load(buf);
const pages = await out.copyPages(src, src.getPageIndices());
pages.forEach((p) => out.addPage(p));
}
return out.save(); // Uint8Array of the new PDF
}
// SPLIT: extract a 1-based inclusive page range.
async function extractRange(buf, from, to) {
const src = await PDFDocument.load(buf);
const out = await PDFDocument.create();
const indices = Array.from({ length: to - from + 1 }, (_, k) => from - 1 + k);
const pages = await out.copyPages(src, indices);
pages.forEach((p) => out.addPage(p));
return out.save();
}
copyPages performs the important work: it deep-copies each page’s referenced resources into the destination document and renumbers objects. Note what it does not do — carry over the outline tree, the form dictionary, or the structure tree. That omission is the source of most “my bookmarks vanished” reports, and it is a property of the operation rather than a bug in a particular tool.
Why local processing is the right default
A PDF you need to merge is usually a PDF you should not upload: a signed contract, a bank statement, a medical form, an ID scan. Uploading routes the document through a third party’s storage and logs, creating a retention question you cannot answer. Local assembly removes the question — with two honest limits:
- Memory, not bandwidth, is the ceiling. A desktop browser handles a few hundred megabytes; a phone will fail much earlier on the same file.
- Encrypted PDFs must be decrypted first. The page tree cannot be read through the encryption layer, so remove the open password in your reader before assembling.
Verifying the Output
Do not trust the page count alone. Three checks catch the failures that matter:
# 1. Structure: page count, encryption, version
pdfinfo output.pdf
# 2. Content: confirm removed text is actually gone
pdftotext output.pdf - | grep -i "confidential-term"
# 3. Size sanity: a split that removed 90% of pages should shrink accordingly
ls -l input.pdf output.pdf
Check 2 is the one people skip. If a page was removed because it held sensitive content, extracting text from the output is the only evidence that the content left with it. A result that is nearly the same size as the input, after dropping most of its pages, is a signal that objects were orphaned rather than removed.
Step-by-Step: Assembling a Document with Toolbox
- Open the tool: visit the Toolbox PDF Merger. For extracting pages, use the PDF Splitter; for reordering or rotating in place, the PDF Organizer.
- Remove passwords first if any input is encrypted — a protected file cannot have its page tree read.
- Flatten fillable forms before merging documents that both contain form fields, to avoid field-name collisions.
- Order the documents deliberately; metadata and, where preserved, the outline are typically inherited from the first input.
- Export and verify: open the result, confirm the page count and page order, and if any page was dropped for confidentiality, extract the text and search for the term that mattered.
- Sign or protect last, since signatures applied before assembly are invalidated by it.
Outcome: a correctly assembled document, with a deliberate decision recorded about bookmarks, forms, and signatures — and no confidential file handed to a third-party server on the way.
Related guides: EXIF metadata and GPS stripping · Compressing images to an exact size · Base64 and data URIs
Frequently Asked Questions
Does deleting a page from a PDF actually remove its content from the file? ▼
Not necessarily. PDF supports incremental updates, where an editor appends a new cross-reference section instead of rewriting the file, so the removed page's objects can remain in the byte stream and be recoverable. Only a full rewrite drops orphaned objects, so verify that the output shrank as expected and re-extract text from the result before distributing it.
Why is my merged PDF larger than the sum of its inputs? ▼
Resources are duplicated rather than shared. Each source carries its own embedded font subsets, colour profiles, and image XObjects, and a naive merge copies all of them, so two files embedding the same font subset produce a doubled result. Some libraries deduplicate identical streams by hash; many do not. Recompressing or re-subsetting fonts recovers most of the size.
What is lost when a PDF is split into separate pages? ▼
Anything defined at document scope. Bookmarks pointing outside the extracted range become dangling and are usually dropped, internal links break, AcroForm hierarchies can lose shared fields, JavaScript and named destinations are often discarded, and digital signatures are invalidated because the signed byte range no longer exists. Page content, embedded fonts, and vector graphics survive.
Can a browser-based tool handle a large or encrypted PDF? ▼
Size is limited by available memory rather than an upload cap, so hundreds of megabytes usually work on a desktop while mobile browsers fail earlier. Encryption is a hard limit: the page tree cannot be read through the encryption layer, so remove the password in your PDF reader before merging or splitting.