EmbedPDF

Engine & handles

The engine exposes three layers of objects: the engine itself, a document handle for each open document, and a page handle for per-page work. Each layer narrows the scope of what you operate on.

The engine#

localEngine() returns an object implementing the Engine interface:

interface Engine {
  open(input: OpenInput, options?: OpenOptions): AbortablePromise<DocumentHandle>;
  destroy(): AbortablePromise<void>;
}

open() is the only entry point.

The local engine accepts { kind: 'bytes' } (and { kind: 'layerBytes' } for layer documents) — you hand it the PDF bytes and a stable id (see Getting started).

Call destroy() when your app is shutting the engine down; existing handles should be close()d first.

One engine can hold many open documents at once. When you open by token, each handle carries its own per-document bearer, so a single engine can serve several independently-authorized documents concurrently.

The document handle#

open() resolves to a DocumentHandle — your entry point to everything about one open document:

interface DocumentHandle {
  readonly id: string;
  readonly capabilities: DocumentCapabilities;
  readonly security: DocumentSecurityService;
  readonly metadata: MetadataService;
  readonly annotations: DocumentAnnotationsService;
  readonly pages: DocumentPagesService;
  readonly render: DocumentRenderService;
 
  page(pageObjectNumber: PageObjectNumber): PageHandle;
  download(opts?: { mode?: PdfSaveMode }): AbortablePromise<Uint8Array>;
  close(): AbortablePromise<void>;
}
  • metadata, annotations, pages, and security are document-scoped services (covered in their own pages).
  • render carries the document’s render policy — not pixels; per-page rendering stays on page(pon).render. See Render policy.
  • page(pon) returns a page handle (below).
  • download() returns the full PDF bytes — see Downloading.
  • close() releases the handle. It’s idempotent and safe to call more than once.

Capabilities#

capabilities advertises engine-specific behavior so portable code can adapt:

interface DocumentCapabilities {
  readonly weakAnnotationEditSessions: 'not-needed' | 'required';
  readonly pageEditSessions: 'unsupported' | 'supported';
}

On the cloud engine, weakAnnotationEditSessions is 'required' (index-based annotation edits need an active edit session) and pageEditSessions is 'unsupported'.

The page handle#

doc.page(pon) returns a PageHandle keyed on the page’s indirect object number, never its display index — so the handle stays valid across page reorders.

interface PageHandle {
  readonly pageObjectNumber: PageObjectNumber;
  readonly pageIndex: number; // advisory display index at mint time
  readonly annotations: PageAnnotationsService;
  readonly text: PageTextService;
  readonly geometry: PageGeometryService;
  readonly render: PageRenderService;
}

page() is synchronous — page records are already known to the handle. To discover page object numbers, call doc.pages.list():

const { pageCount, pages } = await doc.pages.list();
const firstPon = pages[0].pageObjectNumber;
const page = doc.page(firstPon);

Treat pageIndex as advisory metadata. It reflects display order at the time the handle was minted; the durable key is always pageObjectNumber.

Lifecycle at a glance#

const engine = localEngine();
 
const doc = await engine.open({ kind: 'bytes', id: 'doc_123', bytes }); // open
const page = doc.page((await doc.pages.list()).pages[0].pageObjectNumber); // work
await page.render.image();
 
await doc.close();    // release the document
await engine.destroy(); // tear down the engine
Was this page helpful?

Your feedback goes directly to the documentation team.