EmbedPDF

Links

A link is an area of a page that does something when it’s clicked: go to another page of the document, or open a website.

await page.annotations.create({
  subtype: 'link',
  rect: { x: 72, y: 77, width: 128, height: 15 },
  target: { kind: 'uri', uri: 'https://example.com' },
});

A link is an area (rect) and what a click does (target). It’s invisible: it has no color or border, and PDF apps draw nothing where it is. It takes the fields every annotation has, such as contents and the flags. Leave target out for a link that does nothing yet (null).

A goto target names a page and how to show it:

const { pages } = await doc.pages.list();
 
await page.annotations.create({
  subtype: 'link',
  rect,
  target: { kind: 'goto', destination: { kind: 'fit', page: pages[4].ref } },
});
kindWhat the reader seesFields
'fit'The whole page.
'xyz'The point x, y at the top left of the window, at zoom.x, y, zoom
'fitH'The page’s full width, with y at the top of the window.y
'fitV'The page’s full height, with x at the left of the window.x
'fitR'The box x, y, width, height, as large as it fits.x, y, width, height
'fitB'Like 'fit', for the box around the page’s content.
'fitBH'Like 'fitH', for the box around the page’s content.y
'fitBV'Like 'fitV', for the box around the page’s content.x

Positions are in points on the target page, from its top-left corner, with y going down. zoom is a factor: 2 shows the page at 200%. A position or zoom left out (or null) keeps what the reader has, so this link scrolls to a spot without changing the zoom:

await page.annotations.update(link.ref, {
  target: {
    kind: 'goto',
    destination: { kind: 'xyz', page: pages[4].ref, x: null, y: 292, zoom: null },
  },
});

A link to a page the document doesn’t have is refused with NotFound.

A uri target opens its address, as you give it:

await page.annotations.update(link.ref, {
  target: { kind: 'uri', uri: 'https://example.com/pricing' },
});
const { annotations } = await page.annotations.list();
 
for (const annotation of annotations) {
  if (annotation.subtype !== 'link' || !annotation.target) continue;
  const { target } = annotation;
  if (target.kind === 'goto') showPage(target.destination.page);
  if (target.kind === 'uri') openInNewTab(target.uri);
}

target says what a click does:

kindWhat a click doesYou can create it
'goto'Goes to a page of this document (destination).Yes
'uri'Opens a web address (uri).Yes
'named'Runs a viewer command such as NextPage (name).The four below
'goto-remote'Opens another PDF file (file).No
'launch'Opens a file or an app (path).No
'javascript'Runs a script.No
'unsupported'Something the engine doesn’t model.No

target: null is a link that does nothing. A PDF can name a destination instead of spelling it out; the engine looks the name up, so you always get an explicit destination with its page.

A named link you create runs one of the page commands every PDF app has: NextPage, PrevPage, FirstPage or LastPage. Other names, which only some apps know, are read and kept:

await page.annotations.create({
  subtype: 'link',
  rect,
  target: { kind: 'named', name: 'NextPage' },
});

A new link has no border: PDF apps would otherwise draw a black line around it.

await page.annotations.update(link.ref, {
  target: { kind: 'goto', destination: { kind: 'fit', page: pages[0].ref } },
});
 
await page.annotations.update(link.ref, { target: null }); // does nothing when clicked

A new target replaces the old one, and null removes it.

What the engine doesn’t run#

Links that run a script, open another file or run another app’s own command are read and kept in the PDF as they are. The engine never runs or follows them, and you can’t create them: a target of one of these kinds in a create is refused with InvalidArg. Changing another field of such a link (its rect, its contents) keeps what it does, and so does sending back a link you read: its target stays as it was.

Permissions#

Links are annotations: they need the same permissions as any annotation. See Permissions.

Good to know#

The full action#

target is a simple view. The full action is in annotation.actions (read only). A click runs actions.activate; later steps are in next. An action the engine can’t read safely reads as unsupported. Writing target replaces the whole action.

Destinations and actions in the PDF#

A PDF stores a link’s target as a destination (/Dest) or as an action (/A). Both read as a goto target; the engine writes an action. target: null removes both.

Coordinates#

A destination’s positions are measured on the page it goes to, like that page’s annotations: from its top-left corner, with y going down. pageDestinationOf() and pdfDestinationOf() convert to and from the PDF file’s own numbers, with the crop box of the page it goes to; see Pages.

An import maps a link’s destination page the way it maps the link’s own page. A link whose target the engine can’t create is imported without it, and the import reports it in dropped (unsupported-action, field target).

Was this page helpful?

Your feedback goes directly to the documentation team.