---
title: "Pages"
description: "List a document's pages, then rotate, reorder, delete, insert, extract and flatten them."
source: "https://www.cloudpdf.com/docs/engine/documents/pages"
---

# Pages

List the pages of a document to learn their size and order, then work on them by `ref`:

```ts
const { pageCount, pages } = await doc.pages.list();

await doc.pages.rotate([pages[0].ref], 90); // turn the first page
await doc.pages.delete([pages[pageCount - 1].ref]); // delete the last one
```

## List pages

`doc.pages.list()` returns every page in display order. It doesn't load any page content, so it's
quick even for a long document.

```ts
const { pages } = await doc.pages.list();

for (const page of pages) {
  console.log(page.index, page.label, page.size.width, page.size.height, page.rotation);
}
```

| Field        | What it is                                                                                                                                                                |
| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `index`      | Position in the document, from 0. Changes when pages move.                                                                                                                |
| `ref`        | The page's address. Stays the same when pages move; pass it to every page call.                                                                                           |
| `label`      | The page's label from the PDF (`'iii'`, `'A-1'`), or `null` when the file has none.                                                                                       |
| `size`       | `{ width, height }` of the visible area (the crop box) in points, before rotation.                                                                                        |
| `rotation`   | `0`, `90`, `180` or `270`: how far the page is turned clockwise when shown.                                                                                               |
| `userUnit`   | How large one point is, in multiples of 1/72 inch. `1` for almost every file.                                                                                             |
| `tabOrder`   | The order its annotations and form fields take keyboard focus in: `'rows'`, `'columns'`, `'structure'`, `'annotations'` or `'widgets'`; `null` when the page doesn't say. |
| `boxes`      | The page's five boxes: `media`, `crop`, `bleed`, `trim` and `art`. See below.                                                                                             |
| `pdfCropBox` | Where the page sits in the PDF file's own numbers, for tools that use those. See below.                                                                                   |

`boxes` follows the PDF standard. A box the file leaves out takes its default: the crop box is the
media box, and the bleed, trim and art boxes are the crop box. Every box stays inside the media
box, so a crop box that reaches past it is cut back. `crop` is what the page shows, and `size` is
its size.

Each box is `{ x, y, width, height }`, measured in points from the top-left corner of what the
page shows, with y going down. So `boxes.crop` is always `{ x: 0, y: 0, width, height }`, and a
media box that reaches past the page's top-left corner has a negative `x` and `y`:

```ts
{
  size: { width: 512, height: 672 },
  boxes: {
    media: { x: -50, y: -60, width: 612, height: 792 },
    crop: { x: 0, y: 0, width: 512, height: 672 },
    trim: { x: 10, y: 10, width: 492, height: 652 },
    // bleed and art
  },
  pdfCropBox: { left: 50, bottom: 60, right: 562, top: 732 },
}
```

`pdfCropBox` is the only value in the PDF file's own numbers: the crop box where the file places
it, as `{ left, bottom, right, top }` with y going up. You need it only beside a tool that reads
those numbers, such as iText, PDFBox or Acrobat JavaScript. `pdfRectOf()` and `pdfPointOf()` turn a
box or point into them, and `pageBoxOf()` and `pagePointOf()` turn them back. `pdfDestinationOf()`
and `pageDestinationOf()` do the same for a destination, with the crop box of the page it goes to:

```ts
import { pdfRectOf } from '@cloudpdf/engine';

const inTheFile = pdfRectOf({ x: 50, y: 82, width: 50, height: 50 }, page.pdfCropBox);
// { left: 100, right: 150, bottom: 600, top: 650 }
```

`size` is the page before it's turned. For a page at `90` or `270`, swap width and height to get
the size it's shown at:

```ts
const { size, rotation } = pages[0];
const turned = rotation === 90 || rotation === 270;
const shown = turned ? { width: size.height, height: size.width } : size;
```

## Refs, not positions

Every call that works on pages takes refs, never indexes. A ref stays valid when pages move,
rotate, or when pages before it are inserted or deleted, so you can keep it for as long as the
page exists.

```ts
const page = doc.page(ref); // a handle for one page: render, text, annotations
```

`doc.page(ref)` makes no request. When you stored a page's object number yourself,
`toPageRef(12)` turns it back into a ref. See [Refs & identity](https://www.cloudpdf.com/docs/engine/concepts/identity).

## Rotate

`rotate()` sets how pages are turned: `0`, `90`, `180` or `270` degrees clockwise. It sets the
rotation, it doesn't add to it, so one value works for several pages at once:

```ts
await doc.pages.rotate([pageA.ref, pageB.ref], 90);
```

To turn a page a further quarter, add 90 to its current `rotation` from `list()`. After 270,
start again at 0.

Rotating changes how the page is shown, not its content, so annotations and text keep their
positions. Renders don't turn by themselves: pass the page's `rotation` to show it turned (see
[Turn the page](https://www.cloudpdf.com/docs/engine/documents/rendering#turn-the-page)).

## Reorder

`reorder(refs, position)` takes the pages out and puts them back together, in the order you pass
them, at `position`: right after or before another page, or at the start or the end.

```ts
const { pages } = await doc.pages.list();
const last = pages[pages.length - 1].ref;

await doc.pages.reorder([last], 'start'); // the last page to the front
await doc.pages.reorder([pages[0].ref], { after: pages[1].ref }); // swap the first two
```

A position names a page, never a number, so it means the same when other pages come or go
before the call lands. The page you name can't be one of the pages you move (`InvalidArg`);
one the document doesn't have fails with `NotFound`.

## Delete

```ts
await doc.pages.delete([page.ref]);
```

A page's annotations go with it. A document keeps at least one page: deleting every page fails
with `InvalidArg`.

## Insert another PDF

`insert(bytes, position?)` copies every page of another PDF into this one, with their
annotations. Without a position, the pages go at the end:

```ts
const response = await fetch('/appendix.pdf');
const { insertedPages } = await doc.pages.insert(await response.arrayBuffer());
```

`insertedPages` holds the refs of the new pages, in order. To merge several files, insert them one
after the other.

## Insert blank pages

`insertBlank()` adds empty pages of the size you give, in points:

```ts
// Two US Letter pages after the first page
const { pages } = await doc.pages.list();
const letter = { width: 612, height: 792 };
await doc.pages.insertBlank({ size: letter, count: 2 }, { after: pages[0].ref });
```

A4 is `{ width: 595, height: 842 }`. `count` is 1 by default and at most 100 per call.

## Extract pages to a new PDF

`extract(refs)` returns a new PDF with copies of those pages, in the order you pass them. The
document itself doesn't change.

```ts
const bytes = await doc.pages.extract([pages[0].ref, pages[1].ref]);
```

That makes splitting a document a loop, one file per page:

```ts
for (const { ref, index } of pages) {
  const bytes = await doc.pages.extract([ref]);
  await save(`page-${index + 1}.pdf`, bytes);
}
```

And taking pages from one document into another is `extract()` then `insert()`:

```ts
const bytes = await source.pages.extract([ref]);
await target.pages.insert(bytes, 'start');
```

### A copy as the pages print

With `flatten`, the copy is the pages as they look: the annotations and form fields painted into
the pages, and nothing left to click, fill in or edit. It takes the same options as
[`flatten()`](#flatten-a-pages-annotations), so `{ usage: 'print' }` makes a copy for paper: only
what prints is on it, and layers are in their print state. The document itself doesn't change.

```ts
const printable = await doc.pages.extract(refs, { flatten: { usage: 'print' } });
```

`includeAnnotations` and `includeFormFields` choose what's painted in. Each defaults to what the
caller may read, as in a [page picture](https://www.cloudpdf.com/docs/engine/documents/rendering), and they only go with
`flatten`:

```ts
await doc.pages.extract(refs, { flatten: { usage: 'print' }, includeAnnotations: false });
```

The copy keeps the document's title, so a print queue or a saved file has a name.

## Flatten a page's annotations

Flattening draws a page's annotations into the page itself and removes them, so they can't be
changed or deleted anymore. Filled-in form fields become plain content the same way.

```ts
const { results } = await doc.pages.flatten([page.ref]);
// results: [{ page, status: 'applied' }]
```

Each page's `status` is `'applied'`, or `'unchanged'` when it had nothing to flatten. Flattening is
all or nothing: when a page fails, or the call is cancelled, no page changes and the call rejects
with the error. Flattening paints what the page shows: an annotation without a stored appearance is
painted as the engine draws it. Hidden annotations and popups stay as they are, and so do the few
annotations the engine draws only from a stored appearance, such as a stamp without one.
Flattening can't be undone, and it ends undo for every change made before it; see
[Changes & undo](https://www.cloudpdf.com/docs/engine/documents/changes#what-cant-be-undone).

By default the page is flattened as it's shown on screen, which leaves out annotations shown only
when printed. Pass `{ usage: 'print' }` to flatten it as it prints, which leaves out annotations that
don't print:

```ts
await doc.pages.flatten([page.ref], { usage: 'print' });
```

New annotations print unless you create them with `print: false`; see
[Drawing annotations yourself](https://www.cloudpdf.com/docs/engine/annotations/appearances#what-prints).

To flatten only some annotations, see
[Drawing annotations yourself](https://www.cloudpdf.com/docs/engine/annotations/appearances).

## Permissions

| Call                                                             | Needs                                        |
| :--------------------------------------------------------------- | :------------------------------------------- |
| `list()`                                                         | `doc.open`                                   |
| `rotate()`, `reorder()`, `delete()`, `insert()`, `insertBlank()` | `doc.pages.assemble`                         |
| `extract()`                                                      | `doc.download`: the pages leave the document |
| `extract()` with `flatten`                                       | `doc.download` or `doc.download.flattened`   |
| `flatten()`                                                      | `doc.pages.modify` and `doc.annotate.modify` |

See [Permissions](https://www.cloudpdf.com/docs/engine/concepts/permissions).

## Good to know

### What a change returns

`rotate()`, `reorder()`, `delete()`, `insert()`, `insertBlank()`, `setName()` and `removeName()`
return the new page list as `layout`, the same shape `list()` returns, so you can replace the list
you hold without asking again:

```ts
const { layout } = await doc.pages.reorder([ref], 'start');
showThumbnails(layout.pages);
```

Other code that shows pages can listen for `pages.reordered`, `pages.rotated`, `pages.deleted`,
`pages.inserted` and `pages.flattened` instead. See [Events](https://www.cloudpdf.com/docs/engine/concepts/events).

### Inserting

A password-protected PDF can't be inserted: `insert()` fails with `DocPasswordRequired`. Open it,
unlock it, and `extract()` its pages instead. Pages you insert get new refs; the pages already in
the document keep theirs.

### Named pages

A PDF can give pages names. Acrobat's stamp files work this way: each page is one stamp, named
`identifier=label`. The names come with the page list:

```ts
const { namedPages } = await doc.pages.list();
// [{ name: 'Approved=Approved', target: { kind: 'page', page: ref } }, …]
```

`target` is `{ kind: 'page', page }` for a page in the document. A name can also point at a hidden
page template (`{ kind: 'template' }`), which isn't a page you can show, or at nothing
(`{ kind: 'dangling' }`) in a file another app made.

Add, change or remove a name with `setName()` and `removeName()`. The page itself stays:

```ts
await doc.pages.setName({ name: 'Draft=Draft', page: page.ref });
await doc.pages.setName({ name: 'Draft=Concept', page: page.ref, replace: 'Draft=Draft' }); // rename
await doc.pages.removeName({ name: 'Draft=Concept' });
```

Both need `doc.pages.assemble`. Deleting a page removes its names.
