Organizing pages
The page edit plugin changes the document’s pages: rotate, reorder, delete, duplicate, and insert
new ones, blank or from another PDF. These are edits to the file, kept when it’s downloaded. Register
pageEditPlugin() and use usePageEdit():
Loading live preview…
<script lang="ts" module>
import { DocumentGate, Viewer, type OpenInput } from '@embedpdf/svelte/runtime';
import { pageEditPlugin } from '@embedpdf/svelte/page-edit';
import { renderPlugin } from '@embedpdf/svelte/render';
import { localEngine } from '@embedpdf/engine';
import PageOrganizer from './PageOrganizer.svelte';
import '../organize.css';
const engine = localEngine();
const plugins = [renderPlugin(), pageEditPlugin()];
const ebook = async (): Promise<OpenInput> => {
const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};
</script>
<Viewer {engine} {plugins} initialDocuments={[{ source: ebook }]}>
<DocumentGate>
{#snippet fallback()}
<p class="loading">Loading…</p>
{/snippet}
<PageOrganizer />
</DocumentGate>
</Viewer>await pageEdit.rotateBy([page.ref], 90);
await pageEdit.reorder([page.ref], 'start'); // to the front
await pageEdit.delete([page.ref]);After an edit, everything that shows pages updates by itself: the Stage, thumbnails, page numbers. On a shared document, the others see the change too.
Calls that act on pages take a list of them, so one call works for a single page and for a multi-selection.
Rotate pages#
await pageEdit.rotateBy([page.ref], 90); // a quarter turn clockwise from where it is
await pageEdit.rotateBy([page.ref], -90); // and back
await pageEdit.setRotation([pageA.ref, pageB.ref], 180); // the same rotation for severalLoading live preview…
<script lang="ts" module>
import { DocumentGate, Viewer, type OpenInput } from '@embedpdf/svelte/runtime';
import { pageEditPlugin } from '@embedpdf/svelte/page-edit';
import { RenderLayer, renderPlugin } from '@embedpdf/svelte/render';
import { Stage, stagePlugin } from '@embedpdf/svelte/stage';
import { localEngine } from '@embedpdf/engine';
import RotateToolbar from './RotateToolbar.svelte';
import '../rotate.css';
const engine = localEngine();
const plugins = [stagePlugin(), renderPlugin(), pageEditPlugin()];
const ebook = async (): Promise<OpenInput> => {
const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};
</script>
<Viewer {engine} {plugins} initialDocuments={[{ source: ebook }]}>
<DocumentGate>
{#snippet fallback()}
<p class="loading">Loading…</p>
{/snippet}
<RotateToolbar />
<Stage class="stage">
<RenderLayer />
</Stage>
</DocumentGate>
</Viewer>This rotates the page in the file. To only turn how pages are shown, use the Stage’s view rotation. Keep the two on different buttons.
Reorder pages#
reorder() takes the pages and where they go:
await pageEdit.reorder([page.ref], 'start'); // to the front
await pageEdit.reorder([page.ref], 'end'); // to the back
await pageEdit.reorder([pageA.ref, pageB.ref], { after: pageC.ref }); // after another page
await pageEdit.reorder([page.ref], { before: pageC.ref });Several pages keep their order and land together.
Loading live preview…
<script lang="ts" module>
import { DocumentGate, Viewer, type OpenInput } from '@embedpdf/svelte/runtime';
import { pageEditPlugin } from '@embedpdf/svelte/page-edit';
import { renderPlugin } from '@embedpdf/svelte/render';
import { localEngine } from '@embedpdf/engine';
import PageOrder from './PageOrder.svelte';
import '../reorder.css';
const engine = localEngine();
const plugins = [renderPlugin(), pageEditPlugin()];
const ebook = async (): Promise<OpenInput> => {
const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};
</script>
<Viewer {engine} {plugins} initialDocuments={[{ source: ebook }]}>
<DocumentGate>
{#snippet fallback()}
<p class="loading">Loading…</p>
{/snippet}
<PageOrder />
</DocumentGate>
</Viewer>Delete pages#
await pageEdit.delete([pageA.ref, pageB.ref]);A document always keeps at least one page: deleting every page is refused.
Insert pages#
// A blank page after this one, the size of an A4 sheet
await pageEdit.insertBlank({ placement: { after: page.ref }, size: { width: 595, height: 842 } });
// Pages from another PDF: all of them, or the ones you name
await pageEdit.insertFromBytes(otherPdf, { placement: 'end' });
await pageEdit.insertFromBytes(otherPdf, { pageIndexes: [0, 2], placement: 'start' });
// Pages from another document open in the viewer
await pageEdit.insertFromDocument(otherDocumentId, [pageRef], { placement: 'end' });
// Copies of pages, right after them
await pageEdit.duplicate([page.ref]);Each resolves with { pages }: the new pages, so you can scroll to them or select them. Without a
size, a blank page takes the size of the page it’s placed next to.
Loading live preview…
<script lang="ts" module>
import { DocumentGate, Viewer, type OpenInput } from '@embedpdf/svelte/runtime';
import { pageEditPlugin } from '@embedpdf/svelte/page-edit';
import { RenderLayer, renderPlugin } from '@embedpdf/svelte/render';
import { Stage, stagePlugin } from '@embedpdf/svelte/stage';
import { localEngine } from '@embedpdf/engine';
import InsertToolbar from './InsertToolbar.svelte';
import '../insert.css';
const engine = localEngine();
const plugins = [stagePlugin(), renderPlugin(), pageEditPlugin()];
const ebook = async (): Promise<OpenInput> => {
const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};
</script>
<Viewer {engine} {plugins} initialDocuments={[{ source: ebook }]}>
<DocumentGate>
{#snippet fallback()}
<p class="loading">Loading…</p>
{/snippet}
<InsertToolbar />
<Stage class="stage">
<RenderLayer />
</Stage>
</DocumentGate>
</Viewer>Extract pages#
extract() gives you some pages as a new PDF, without changing this document:
const bytes = await pageEdit.extract([pageA.ref, pageB.ref]);
saveFile(bytes, 'chapter-2.pdf');Loading live preview…
<script lang="ts" module>
import { DocumentGate, Viewer, type OpenInput } from '@embedpdf/svelte/runtime';
import { pageEditPlugin } from '@embedpdf/svelte/page-edit';
import { renderPlugin } from '@embedpdf/svelte/render';
import { localEngine } from '@embedpdf/engine';
import ExtractPages from './ExtractPages.svelte';
import '../extract.css';
const engine = localEngine();
const plugins = [renderPlugin(), pageEditPlugin()];
const ebook = async (): Promise<OpenInput> => {
const response = await fetch('https://snippet.embedpdf.com/ebook.pdf');
return { kind: 'bytes', id: 'ebook', bytes: new Uint8Array(await response.arrayBuffer()) };
};
</script>
<Viewer {engine} {plugins} initialDocuments={[{ source: ebook }]}>
<DocumentGate>
{#snippet fallback()}
<p class="loading">Loading…</p>
{/snippet}
<ExtractPages />
</DocumentGate>
</Viewer>Where a page goes#
| Placement | Means |
|---|---|
{ after: pageRef } | Right after that page |
{ before: pageRef } | Right before that page |
'start' | Before the first page |
'end' | After the last page |
A placement names a page, not a position, so it stays right when pages move while the edit waits its turn. The page can also be an index, as everywhere in this plugin: the page at that index when you call.
Methods#
Everything usePageEdit() gives you.
| Method | What it does |
|---|---|
rotateBy(pages, delta) | Turns each page by 90, -90 or 180 degrees from where it is. Rotate pages |
setRotation(pages, rotation) | Gives every page the same rotation: 0, 90, 180 or 270 |
reorder(pages, placement) | Moves pages to a new place. Several pages keep their order and land together. Reorder pages |
delete(pages) | Deletes pages. Deleting every page is refused. Delete pages |
insertBlank({ placement?, size?, count? }) | Inserts blank pages, one unless you pass a count. Resolves { pages }, the new pages. Insert pages |
insertFromBytes(bytes, { pageIndexes?, placement? }) | Inserts the pages of another PDF, or only the ones at pageIndexes. Resolves { pages }. |
insertFromDocument(documentId, pages, { placement? }) | Inserts copies of pages from another document open in the viewer. Resolves { pages }. |
duplicate(pages, { placement? }) | Copies pages, right after the last of them unless you pass a placement. Resolves { pages }. |
extract(pages) | Copies pages into a new PDF and resolves its bytes. This document doesn’t change. Extract pages |
canEdit(), canExtract() | Whether rotating, reordering, deleting and inserting are allowed, and whether copying pages out is. Permissions |
placement is one of the values in Where a page goes; without one, new pages go
at the end. Every method that returns a promise also takes a signal in its options, to cancel it.
Events#
Page changes are document events, so every part of your app hears them, whichever plugin made the change:
<script lang="ts">
import { useDocumentsEvent } from '@embedpdf/svelte/runtime';
let pageCount = $state(0);
useDocumentsEvent(
(documents) => documents.onPagesChanged,
({ pages }) => (pageCount = pages.length),
);
</script>
<span>{pageCount} pages</span>The page edit plugin has no events of its own. onPagesChanged and what it carries are listed under
Opening documents.
Permissions#
| Permission | What it allows | Check |
|---|---|---|
doc.pages.assemble | Rotate, reorder, delete and insert pages | pageEdit.canEdit() |
doc.download | extract(), duplicate() and insertFromDocument(), which copy pages out | pageEdit.canExtract() |
Good to know#
Pages have a ref, not just a number#
Each page has a ref: its identity in the file. A page’s index changes the moment a page before it
moves or goes; its ref never does. So a call still hits the page the user clicked, even if the
document changed in between. You get refs from usePageList() (page.ref), and anything that
points at a page carries one: a search result’s page, an annotation’s page.
What moves with a page#
Annotations, links and form fields belong to their page, so they move, rotate and go with it. Rotation leaves annotations where they are on the page; they turn with it.
Page edits are saved like every other change; see Saving & downloading.
Your feedback goes directly to the documentation team.