Skip to content
Inkoxide

Search covers every page and heading of the docs. Use ↑ ↓ and ↵.

Links, bookmarks and metadata

Edit this page

<Document title="Annual report" author="ACME" lang="en-US">
  <Page>
    <Text href="#results" className="text-blue-700 underline">Jump to results</Text>
    <Text href="https://example.com">Website</Text>
    <Text>
      Read the <Link href="https://example.com/terms" className="underline">terms</Link>.
    </Text>
    <Text id="results" bookmark="Results" className="text-xl font-bold">Results</Text>
    <Text bookmark={{ title: 'Revenue', level: 2 }}>Revenue</Text>
  </Page>
</Document>

href on a Text or View makes the whole element a link, and <Link> does the same as a component: inside a Text it is a linked run, elsewhere a box like a View. A link takes the style it is given and adds no colour or underline of its own. href="#id" jumps to the element with that id.

Bookmarks become the PDF outline in the viewer's sidebar.

Document writes title, author, subject, keywords, creator (the application that made the file) and producer (the software that wrote the PDF, by default Inkoxide <version>) into the PDF's properties. creationDate (an RFC 3339 timestamp, "2026-10-12T09:30:00Z") dates it, as the creationDate option does; without either, the same input gives byte-identical output.

pageMode and pageLayout say how the viewer opens the PDF:

<Document pageMode="useThumbs" pageLayout="twoPageRight">…</Document>

pageMode is 'useNone' (no side panel), 'useOutlines' (the bookmarks), 'useThumbs' (page thumbnails) or 'fullScreen'. The default shows the bookmarks when there are any. pageLayout is 'singlePage', 'oneColumn' (continuous scrolling), 'twoColumnLeft' or 'twoColumnRight' (two scrolling columns, odd pages on the left or right), or 'twoPageLeft' or 'twoPageRight' (two pages at a time); without it, the viewer uses its own setting.

fallbackFonts lists the fonts tried, in order, for characters a run's font lacks. The default is ['NotoSans-Regular']. The entries are font ids: the bundled Inter-Regular, Inter-Italic, Inter-Bold, InterDisplay-Bold, NotoSans-Regular and RobotoMono-Regular, or a face added with Font.register, whose id is its family with spaces as hyphens, its weight, and -italic for an italic face:

Font.register({ family: 'Noto Sans Arabic', src: './fonts/NotoSansArabic-Regular.ttf' });

<Document fallbackFonts={['NotoSans-Regular', 'Noto-Sans-Arabic-400']}>…</Document>

An id that is not registered is skipped, and fallbackFonts={[]} turns the fallback off.

PDF/A

conformance makes the file PDF/A, for archiving, and so does the pdfA render option, which wins over it:

<Document title="Invoice 2026-0815" conformance="PDF/A-2b">…</Document>
conformancepdfAWrites
'PDF/A-2b' or 'PDF/A-2'true or '2b'PDF/A-2b
'PDF/A-3b' or 'PDF/A-3''3b'PDF/A-3b, as 2b; the level allows embedded files, but Inkoxide cannot attach any yet
'PDF/A-1b' or 'PDF/A-1''1b'PDF/A-1b, the oldest level, for archives that require it

PDF/A-1b has no transparency, so a render fails with ENGINE_REJECTED (engineCode PDFA_CONFLICT) and the component at fault for a colour with alpha below 1 (a transparent background is fine: it draws nothing), an opacity between 0 and 1, an image with an alpha channel such as most PNG logos, an SVG drawn with opacity, a mask or a blend mode, or debug. PDF/A-2b and 3b allow all of these. The a and u levels ('PDF/A-2a', 'PDF/A-3u') need tagged PDF, which is not supported yet: they fail with TEMPLATE_ERROR.

conformance is not part of the document tree: to render a stored result.document again at the same level, pass pdfA to renderDocument.

Passwords and permissions

userPassword encrypts the PDF (AES-256, which Acrobat X and later, pdf.js, Chrome and Preview open): the file opens only with that password. ownerPassword lifts permissions, what a reader without it may do:

<Document
  userPassword="open-sesame"
  ownerPassword={process.env.PDF_OWNER_PASSWORD}
  permissions={{ printing: 'highResolution', copying: false }}
>
  …
</Document>
PermissionDefaultAllows
printingnot allowed'lowResolution' or 'highResolution' printing
modifyingfalsechanges other than annotations and form fields
copyingfalsecopying text and images
annotatingfalseadding annotations and filling in fields
fillingFormsfalsefilling in fields
contentAccessibilitytrueextracting text for screen readers
documentAssemblyfalseinserting, rotating and deleting pages
  • Passwords are printable ASCII, at most 127 bytes. With only ownerPassword, anyone opens the file and the permissions apply; with only userPassword, it is also the owner password, so permissions need an ownerPassword other than userPassword.
  • The encryption render option ({ userPassword, ownerPassword, permissions }) replaces the three props as a whole, for passwords that come from the request rather than the template.
  • Encryption and PDF/A do not go together: PDF/A forbids encryption.
  • A bad value fails with ENGINE_REJECTED at its path (options.encryption.userPassword, …). No error shows a password, and the passwords are not part of result.document or of the tree's JSON.
  • Readers enforce the permissions; the file cannot. Encryption hides the text and images, not the page count or sizes. Field values are hidden, but not which checkboxes are checked, nor the positions of the chosen radio button and list box options.
  • The same document and passwords give the same bytes, as unencrypted output does.

Synced from packages/render/GUIDE.md in the inkoxide repository.