# Pdfium Impl Multi Page

> Use when merging, concatenating, splitting, extracting, or reordering pages across PDF documents with pdfium-render: combining several PDFs into one, pulling a page subset into a new file, or rearranging page order. Prevents the wrong-method-name mistake (the import_page_from_document family does not exist; the real names are the copy_*_from_document family), the index-base confusion (copy_pages_from_document takes a 1-indexed string while the numeric functions are 0-indexed), and the wrong-receiver mistake (append lives on PdfPages, not PdfDocument). Covers append, copy_page_from_document, copy_page_range_from_document, copy_pages_from_document, pages_mut, and the PdfPageIndex type change. Keywords: pdfium-render merge PDFs, concatenate PDF, combine PDF files, split PDF, extract pages, reorder pages, copy_pages_from_document, copy_page_from_document, copy_page_range_from_document, append, pages_mut, import_page_from_document not found, how do I merge two PDFs, join PDF pages, page index out of bounds, 1-inde

- Skill: `impertio-studio/pdfium-impl-multi-page` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add impertio-studio/pdfium-impl-multi-page`
- Raw SKILL.md: https://api.skillmd.com/api/skills/impertio-studio/pdfium-impl-multi-page/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- License: MIT
- Author: Impertio-Studio (https://skillmd.com/u/impertio-studio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/impertio-studio/pdfium-impl-multi-page

---


# pdfium-impl-multi-page

This skill covers moving whole pages between PDF documents with pdfium-render:
merging several files into one, extracting a subset of pages into a new file,
and reordering pages. Every operation works on the destination document's
mutable page collection, reached with `document.pages_mut()`.

Scope boundary: deleting, rotating, and resizing pages within a single document
is `pdfium-impl-page-manipulation`. Copying individual page objects (text,
images, paths) between pages is `pdfium-impl-page-objects-edit`. This skill
handles whole-page transfer.

Default API surface: pdfium-render 0.9.x. Version notes for 0.8.x are inline
and collected in the version table.

## The four operations

ALWAYS use one of these four verified `PdfPages` methods. The destination is the
collection from `document.pages_mut()`.

| Operation | Method | Page selection |
|-----------|--------|----------------|
| Copy all pages of another document | `append(&source)` | every page |
| Copy one page | `copy_page_from_document(&source, src_idx, dest_idx)` | one 0-indexed page |
| Copy a contiguous range | `copy_page_range_from_document(&source, 0..=n, dest_idx)` | 0-indexed `RangeInclusive` |
| Copy a scattered selection | `copy_pages_from_document(&source, "1,3-4,7", dest_idx)` | 1-indexed string |

All four return `Result<(), PdfiumError>`. Full signatures: `references/methods.md`.

## The wrong-name trap

The methods are named `copy_page_from_document`, `copy_page_range_from_document`,
and `copy_pages_from_document`. There is NO `import_page_from_document`,
`import_page_range_from_document`, or `import_pages_from_document`. Those names
do not exist in any 0.8.x or 0.9.x release; code using them does not compile.
Verified against the docs.rs prelude pages for both 0.8.37 and the current
0.9.x line. ALWAYS use the `copy_*_from_document` names.

## The index-base trap

The page-selection argument changes base between methods. Mixing them up copies
the wrong pages silently or returns `PageIndexOutOfBounds`.

| Method | Argument | Base | Example for the first three pages |
|--------|----------|------|-----------------------------------|
| `copy_page_from_document` | `source_page_index: PdfPageIndex` | 0-indexed | `0`, then `1`, then `2` |
| `copy_page_range_from_document` | `source_page_range: RangeInclusive<PdfPageIndex>` | 0-indexed, inclusive | `0..=2` |
| `copy_pages_from_document` | `pages: &str` | 1-indexed string | `"1-3"` or `"1,2,3"` |

The `destination_page_index` argument on all three is always 0-indexed: it is
the position in the destination collection where the copied pages are inserted.

## Inserting at the end

`append` always adds to the end. For the `copy_*` functions, pass the current
destination length as `destination_page_index` to insert after the last page:

```rust
let dest_len = document.pages().len();
document.pages_mut()
    .copy_page_from_document(&source, 0, dest_len)?;
```

`PdfPages::len()` returns a `PdfPageIndex`, the exact type the destination
argument expects, so no cast is needed.

## Decision tree

```
Moving whole pages between or within documents?
|
+-- Combining entire documents back to back?
|     -> for each source: document.pages_mut().append(&source)
|
+-- Copying a contiguous block (pages 5 through 9)?
|     -> copy_page_range_from_document(&source, 4..=8, dest_idx)   // 0-indexed
|
+-- Copying scattered pages (1, 3, 4, 7)?
|     -> copy_pages_from_document(&source, "1,3-4,7", dest_idx)    // 1-indexed
|
+-- Copying exactly one page?
|     -> copy_page_from_document(&source, src_idx, dest_idx)       // 0-indexed
|
+-- Splitting or extracting into a NEW file?
|     -> pdfium.create_new_pdf(), copy the wanted pages in, save
|
+-- Reordering pages of ONE document?
|     -> create_new_pdf(), copy pages in the target order, save
|
+-- Deleting / rotating pages in place?
      -> not this skill; see pdfium-impl-page-manipulation
```

## Pattern: merge several PDFs into one

```rust
use pdfium_render::prelude::*;

let pdfium = Pdfium::default();
let mut merged = pdfium.create_new_pdf()?;

for path in ["a.pdf", "b.pdf", "c.pdf"] {
    let source = pdfium.load_pdf_from_file(path, None)?;
    merged.pages_mut().append(&source)?;
}

merged.save_to_file("merged.pdf")?;
```

Each source document must stay alive only for the duration of its `append`
call. `append` copies the pages into `merged`, so dropping `source` afterwards
is safe.

## Pattern: extract a page subset into a new document

```rust
use pdfium_render::prelude::*;

let pdfium = Pdfium::default();
let source = pdfium.load_pdf_from_file("report.pdf", None)?;

let mut extract = pdfium.create_new_pdf()?;
// 1-indexed string: pages 1, 3, 4 and 7 of the source.
extract.pages_mut()
    .copy_pages_from_document(&source, "1,3-4,7", 0)?;

extract.save_to_file("extract.pdf")?;
```

## Pattern: reorder the pages of one document

pdfium-render has no in-place page-move method. Reorder by copying pages into a
fresh document in the wanted order.

```rust
use pdfium_render::prelude::*;

let pdfium = Pdfium::default();
let source = pdfium.load_pdf_from_file("input.pdf", None)?;

let mut reordered = pdfium.create_new_pdf()?;
// Original order is 0,1,2; produce 2,0,1.
for src_index in [2, 0, 1] {
    let dest = reordered.pages().len();
    reordered.pages_mut()
        .copy_page_from_document(&source, src_index, dest)?;
}

reordered.save_to_file("reordered.pdf")?;
```

## Pattern: split one document into single-page files

```rust
use pdfium_render::prelude::*;

let pdfium = Pdfium::default();
let source = pdfium.load_pdf_from_file("input.pdf", None)?;
let page_count = source.pages().len();

for index in 0..page_count {
    let mut single = pdfium.create_new_pdf()?;
    single.pages_mut()
        .copy_page_from_document(&source, index, 0)?;
    single.save_to_file(format!("page-{index}.pdf"))?;
}
```

## Version table

| Item | 0.8.x | 0.9.x |
|------|-------|-------|
| `append`, `copy_page_from_document`, `copy_page_range_from_document`, `copy_pages_from_document` | present (verified on 0.8.37) | present |
| Receiver of all four methods | `PdfPages` (via `pages_mut()`) | `PdfPages` |
| `import_*_from_document` names | do not exist | do not exist |
| `PdfPageIndex` underlying type | `u16` | `c_int` since 0.9.0 |
| `pages_mut()` | present since 0.8.0 | present |

The `PdfPageIndex` type changed from `u16` to `c_int` in 0.9.0. Code that wrote
the index type explicitly (`let i: u16 = ...`) breaks on upgrade. ALWAYS let
the index type be inferred, or write `PdfPageIndex`.

## Critical rules

- ALWAYS use the `copy_*_from_document` names. The `import_*_from_document`
  family does not exist in pdfium-render.
- ALWAYS call these methods on `document.pages_mut()`, never on `pages()`.
- ALWAYS treat `copy_pages_from_document`'s string as 1-indexed, and the numeric
  index and range functions as 0-indexed.
- ALWAYS save the destination document with `pdfium-impl-saving` after copying.
  In-memory page changes are lost otherwise.
- NEVER call `append` on a `PdfDocument`. `append` is a `PdfPages` method.
- NEVER pin the index type to `u16`. Use `PdfPageIndex` or inference so the
  code survives the 0.9.0 type change.

## Companion skills

- `pdfium-syntax-document-loading` for loading sources and `create_new_pdf`.
- `pdfium-impl-saving` for persisting the merged or extracted document.
- `pdfium-impl-page-manipulation` for deleting, rotating, resizing pages.
- `pdfium-impl-page-objects-edit` for copying page objects, not whole pages.
- `pdfium-syntax-pages` for `pages()`, `len()`, `iter()`, `get()`.
- `pdfium-errors-runtime` for `PdfiumError` and `PageIndexOutOfBounds`.

## Reference files

- `references/methods.md` : complete API signatures with version annotations.
- `references/examples.md` : working, verified Rust examples.
- `references/anti-patterns.md` : real failures, why they happen, and the fix.

