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).
Link to a page#
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 } },
});kind | What the reader sees | Fields |
|---|---|---|
'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.
Link to a website#
A uri target opens its address, as you give it:
await page.annotations.update(link.ref, {
target: { kind: 'uri', uri: 'https://example.com/pricing' },
});Read links#
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:
kind | What a click does | You 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.
Change or remove what a link does#
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 clickedA 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.
Copying links#
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).
Your feedback goes directly to the documentation team.