Headless text annotations
The instance.annotations.text namespace exposes the text annotation editor as a programmatic surface. Apply bold, italic, and underline to the active annotation; change the font family, size, color, and background; and subscribe to editor state changes, all from your own UI.
Looking for the visual side of this? See the supported slots reference for replacing the inline text-formatting toolbar that appears when a text annotation is being edited. This page covers the programmatic surface you’ll call from inside that custom UI.
When to use this
Reach for the headless text annotation API when you’re building:
- A host-app text-formatting toolbar that lives outside the viewer container.
- A keyboard shortcut palette your application defines for bold, italic, font selection, and so on.
- A custom font picker fed by
getFonts()instead of the default font list. - A multipane editing experience where the formatting controls are in a separate panel from the canvas.
API reference
The text annotation namespace exposes three groups of methods: applying style and formatting to the active editor, controlling editor focus and selection, and reading editor state and the font list.
Style and formatting
Each setter accepts an optional second argument identifying the annotation to apply the change to. Omit the argument and the SDK targets the annotation currently in the editor.
| Method | Notes |
|---|---|
instance.annotations.text.setTextStyle(options, annotationOrId?) | Applies one or more text styles. options is { bold?: boolean, italic?: boolean, underline?: boolean }. Omit a field to leave it unchanged. |
instance.annotations.text.setFontFamily(family, annotationOrId?) | Sets the font family. family should be one of the values returned by getFonts(). |
instance.annotations.text.setFontSize(size, annotationOrId?) | Sets the font size in points. |
instance.annotations.text.setFontColor(color, annotationOrId?) | Sets the font color. color is a NutrientViewer.Color. |
instance.annotations.text.setBackgroundColor(color, annotationOrId?) | Sets the background fill color. Pass null to clear. |
instance.annotations.text.restoreSelection() | Restores the most recent selection inside the inline editor. Useful after a toolbar interaction has stolen focus. |
Editor focus and selection
These methods drive the live editor while it’s in EDITING mode, which is useful for keyboard-shortcut palettes and external toolbars that need to drive the caret directly.
| Method | Notes |
|---|---|
instance.annotations.text.focusEditor() | Programmatically focuses the live text editor. Requires the editor to be in EDITING mode. |
instance.annotations.text.blurEditor() | Programmatically blurs the live text editor. Requires EDITING mode. |
instance.annotations.text.setSelection(start, length = 0) | Sets the editor selection by character offsets. length = 0 places the caret. Requires EDITING mode; validates non-negative integers. |
Editor state and fonts
| Method | Notes |
|---|---|
instance.annotations.text.getEditorState() | Returns a snapshot of the editor state, or null if no text annotation is active. |
instance.annotations.text.getFonts() | Returns { supportedFonts, customFonts, unsupportedCurrentFont }. The last field is set when the active annotation uses a font the SDK didn’t load. Render it in your picker so the current font is always selectable. |
instance.annotations.text.getColorPresets() | Returns the resolved color presets for font, background, and border colors. |
Events
| Event | When it fires |
|---|---|
textAnnotationEditorState.change | Whenever the editor state changes, selection, formatting, focus, and so on. |
The event payload is { current, previous }, where each side has the same shape as getEditorState()’s return value (or null when no text annotation is active). Read current to keep your custom toolbar in sync with the editor without polling; previous is useful for diffing transitions.
Example: An external text-formatting toolbar
This example builds a host-app toolbar with bold, italic, font family, font size, and color controls. The toolbar listens for textAnnotationEditorState.change and reflects the current editor state in its button states:
const instance = await NutrientViewer.load({ container: "#viewer", document: "contract.pdf"});
const toolbar = document.getElementById("text-toolbar");const boldButton = toolbar.querySelector("#bold");const italicButton = toolbar.querySelector("#italic");const fontSelect = toolbar.querySelector("#font");const sizeInput = toolbar.querySelector("#size");const colorInput = toolbar.querySelector("#color");
const fonts = instance.annotations.text.getFonts();const families = [...fonts.supportedFonts, ...fonts.customFonts];// `unsupportedCurrentFont` is the font the active annotation uses but the// SDK didn't load. Prepend it so the dropdown always reflects the current font.if (fonts.unsupportedCurrentFont && !families.includes(fonts.unsupportedCurrentFont)) { families.unshift(fonts.unsupportedCurrentFont);}families.forEach((family) => { const option = document.createElement("option"); option.value = family; option.textContent = family; fontSelect.appendChild(option);});
instance.addEventListener("textAnnotationEditorState.change", ({ current }) => { if (!current) { toolbar.style.display = "none"; return; }
toolbar.style.display = "flex"; boldButton.classList.toggle("is-active", current.marks.bold === "on"); italicButton.classList.toggle("is-active", current.marks.italic === "on"); fontSelect.value = current.fontFamily.value ?? ""; sizeInput.value = current.fontSize.value ?? "";});
boldButton.onclick = () => { instance.annotations.text.restoreSelection(); const state = instance.annotations.text.getEditorState(); const isBold = state?.marks.bold === "on"; instance.annotations.text.setTextStyle({ bold: !isBold });};
italicButton.onclick = () => { instance.annotations.text.restoreSelection(); const state = instance.annotations.text.getEditorState(); const isItalic = state?.marks.italic === "on"; instance.annotations.text.setTextStyle({ italic: !isItalic });};
fontSelect.onchange = () => { instance.annotations.text.restoreSelection(); instance.annotations.text.setFontFamily(fontSelect.value);};
sizeInput.oninput = () => { instance.annotations.text.restoreSelection(); instance.annotations.text.setFontSize(Number(sizeInput.value));};
colorInput.oninput = () => { instance.annotations.text.restoreSelection(); instance.annotations.text.setFontColor( NutrientViewer.Color.fromHex(colorInput.value) );};The restoreSelection() call before each formatting action is important; clicking a toolbar button moves focus out of the editor and clears the selection, so we restore it before applying the change.
Example: Combining several styles in one call
setTextStyle accepts multiple flags at once, so a “make this bold and italic” command is a single call:
instance.annotations.text.restoreSelection();instance.annotations.text.setTextStyle({ bold: true, italic: true });Omitting a field leaves that style unchanged; only the fields you set are applied.
Related
- Color presets — The cross-namespace pattern for reading preset colors.
- Text annotation reference — The underlying
TextAnnotationAPI. - Callout annotations —
TextAnnotationvariants with leader lines.