Web SDKv1.0.0

AnnotationsApi

The Highlight, Note, Link and shape tools and the selection bar's text marks, headless: create() writes highlights, underlines, squiggly underlines, strikeouts, notes, links, squares, circles, lines and ink drawings; list() also reports stamps and free text. What list() reports — name, flags, colors, quads, strokes, line ends, end caps, stroke width, link target, dates — create() takes back, so text marks, notes, links and shapes copy between documents (under the extract feature: without it the words come back '', and a web link's uri '', which create() refuses); a line's cap fill and a dashed border do not come along. Stamps and text boxes are not created here: their picture is their appearance, which only their tools draw.

License. Creating, changing, deleting and flattening, replying and setting review status need the annotations feature; a form field's widget changed, removed or flattened needs forms too. Listing annotations and reading comments need no feature, their contents and author blanked to '' without extract. On a certified document, a write the certification forbids rejects with INVALID_STATE unless the call passes invalidateSignatures: true, the same escape as flattening a signed document: the certification is then broken for good and nothing is asked again.

Ids, not positions. An annotation is addressed by its id: its name when the file names it, which other readers keep across a round trip, else an id the SDK derives from its object in the file (obj:<number>), which a save keeps too. What the tools and create() write is named with a UUID. A file that names two annotations alike addresses the first in page order, everywhere. The tools honor an annotation's flags; the API does not. Erase and Note leave a Locked or ReadOnly annotation alone and do not show a Hidden one; doc.annotations is your code and acts on any. color is the color, opacity how opaque, each its own member: null means the annotation carries no color entry, and a reader uses its default.

Every annotation the SDK writes is a proper comment in other readers: it carries the print flag, a creation date and a modification date, and a subject naming what it is, in English from the API. A link is navigation and is written without them. A note also keeps its size and orientation when the page is zoomed or turned (noZoom, noRotate), as Acrobat writes notes. Form fields appear in list() as type: 'widget'; their value lives in the form environment, so read and fill those through FormsApi.

Every rect is a Rect in page space (PDF points, origin bottom-left, y upwards) and a text mark's lines are Quads, their corners named as Acrobat writes them. Colors go in as any CSS color string a PDF can hold (hex, rgb(), hsl(), named colors; oklch(), lab(), color() and color-mix() are refused with INVALID_ARGUMENT) and come out as lower-case #rrggbb with opacity beside them.

namespacedoc.annotations

Usage

TypeScript
const comments = (await doc.annotations.list(0)).filter((a) => a.contents !== '');

await doc.annotations.create(0, {
  type: 'note',
  rect: { x: 72, y: 700, width: 20, height: 20 },
  contents: 'Check this total',
  author: 'Accounts',
});

Annotations

listmethodsince 1.0.0
list(pageIndex: number): Promise<AnnotationInfo[]>
Every annotation on a page as AnnotationInfo[]. An id is the annotation's name — a UUID for one the SDK created — or obj:<number> for one the file carries without a name. Without the extract feature contents, author and a web link's uri are ''. A link's uri is the address the viewer would follow (http:, https:, mailto:, tel:, as create() takes it — a bare host in the file reads with https:// in front); a link with any other scheme has uri and targetPage both null. created and modified are Dates, null where a file from another producer says nothing or writes something that is not a PDF date. Needs no feature.
ParameterTypeDescription
pageIndexnumberZero-based page index.
Returns Promise<AnnotationInfo[]> Each with pageIndex, id, type, rect, contents, author, color, opacity, fillColor, created, modified, quads, inkList, vertices, line, lineEndings, strokeWidth, uri, targetPage, flags, inReplyTo and status.
  • INVALID_ARGUMENT — a page index out of range.
  • DOCUMENT_CLOSED — the document has been closed.
TypeScript
const links = (await doc.annotations.list(0)).filter((a) => a.type === 'link');
getmethodsince 1.0.0
get(id: string): Promise<AnnotationInfo | null>
The annotation with that id, wherever it sits now, as AnnotationInfo, or null — the way to keep hold of one across saves, deletions and other readers. A miss reads every page. Words blanked as list() blanks them without extract. Needs no feature.
ParameterTypeDescription
idstringThe annotation's id, as list() or create() gave it.
Returns Promise<AnnotationInfo | null>
  • INVALID_ARGUMENT — an id that is not a non-empty string.
  • DOCUMENT_CLOSED — the document has been closed.
TypeScript
const note = await doc.annotations.get(id);
if (note) console.log(note.pageIndex, note.contents);
createmethodsince 1.0.0
create(pageIndex: number, annotation: NewAnnotation, options?: SignedDocumentOptions): Promise<string>

Adds one. A text mark (highlight, underline, squiggly, strikeout) covers quads (omitted or empty: its rect, which becomes their bounds); a line needs line, an ink needs inkList, a polygon needs vertices (three or more corners), a polyline too (two or more). A square, circle, line, drawing, polygon or polyline is written as its tool writes it — the same properties and appearance, so lineEndings: { end: 'openArrow' } is the Arrow tool's arrow: a square's or circle's rect is the box given; a line's, drawing's, polygon's or polyline's is worked out from its geometry, and the one given is not used.

A shape with no color is drawn in #18181B, the default palette's first color; strokeWidth is up to 14 400 points, one point when omitted or null, and 0 gives a square, circle or polygon no outline (a line, drawing or polyline is drawn at one point for 0); lineEndings default to 'none' and are ignored on anything but a line; borderStyle is solid when omitted, dashed or dotted is a pattern sized to the stroke, and cloudy goes round a square, circle or polygon only (an open shape asked for it is drawn solid). An id of your own is written as its name — saved in the file for every reader, so no secrets — and must be one no annotation in the document carries, at most 256 characters, no control characters; omitted, it gets a UUID. flags (a Partial<AnnotationFlags>: a flag left out is off, and it prints unless noPrint) are written as given. A link goes to uri — http:, https:, mailto: or tel:, a bare address gets https://, stored percent-encoded — or to targetPage. Needs the annotations feature.

ParameterTypeDescription
pageIndexnumberZero-based page index.
annotationNewAnnotationtype (one of highlight, underline, squiggly, strikeout, note, link, square, circle, line, ink, polygon, polyline) and rect are required; contents, author, color, opacity, fillColor, quads, line, inkList, vertices, strokeWidth, lineEndings, borderStyle, uri, targetPage, id, flags, created and modified are optional.
options?SignedDocumentOptions{ invalidateSignatures? }.
Returns Promise<string> The new annotation's id.
  • INVALID_ARGUMENT — a stamp, a free text, a type it does not make (the message names the ones it does: 'rectangle' is 'square'), a missing line, strokes or corners, a shape point that is not a number or lies past ±14 400 points, a color string the API does not take, an opacity outside 0–1, a stroke width outside 0 to 14 400, an end cap not in LineEnding, a border style not in BorderStyle, another link scheme, a targetPage that is not a page of the document, a pageIndex that is not a page of the document, an id that is empty, taken, longer than 256 characters, holds a control character or is of the obj:… form the SDK derives, a web link whose uri is '', or text-mark quads with a coordinate that is not a number or lies past ±14 400 points, or that run along more than 1 440 000 points of line in all.
  • PAGE_ERROR — the annotation could not be added.
  • FEATURE_NOT_LICENSED — the license lacks annotations.
  • DOCUMENT_CLOSED — the document has been closed.
  • INVALID_STATE — a certified document whose certification forbids it, unless options.invalidateSignatures; or a progressive document still downloading.
TypeScript
const id = await doc.annotations.create(0, {
  type: 'line',
  rect: { x: 0, y: 0, width: 0, height: 0 }, // worked out from the geometry
  line: { start: { x: 72, y: 600 }, end: { x: 300, y: 600 } },
  lineEndings: { end: 'openArrow' }, // the Arrow tool's arrow
  color: '#D92D20',
});
updatemethodsince 1.0.0
update(id: string, changes: AnnotationUpdate, options?: SignedDocumentOptions): Promise<void>

Changes rect, contents, author, color, fillColor (a square's, circle's or polygon's; refused on anything else) or opacity of the annotation with that id. A color with no alpha changes the color alone; one with an alpha, or an opacity beside it, sets the opacity too; null is refused, a color entry cannot be removed. A new rect moves and scales what the annotation draws — quads, strokes, line ends, a polygon's or polyline's corners — with it; a stamp's or text box's own picture is fitted to it.

update({ color }) rewrites the entry and rebuilds the appearance, so every reader draws the new color. A text box is refused with INVALID_ARGUMENT, since its color is its words'. A stamp accepts the call, but its picture is its own and keeps its colors: only the color entry and the opacity change. update() stamps the modification date when it changes something; it also acts on a reply or a status entry as on any annotation. Needs the annotations feature, and forms too for a form field's widget.

ParameterTypeDescription
idstringThe annotation's id.
changesAnnotationUpdate{ rect?, contents?, author?, color?, fillColor?, opacity? }.
options?SignedDocumentOptions{ invalidateSignatures? }.
Returns Promise<void>
  • INVALID_ARGUMENT — an id no annotation has, a text box's color, fillColor on anything but a square, circle or polygon, a null color, or a value the API does not take.
  • PAGE_ERROR — the change could not be written (the color cannot be written).
  • FEATURE_NOT_LICENSED — the license lacks annotations (or forms, for a form field's widget).
  • DOCUMENT_CLOSED — the document has been closed.
  • INVALID_STATE — a certified document whose certification forbids it, unless options.invalidateSignatures; or a progressive document still downloading.
TypeScript
await doc.annotations.update(id, { contents: 'Checked — fine', color: '#12B76A' });
deletemethodsince 1.0.0
delete(id: string, options?: SignedDocumentOptions): Promise<void>
Removes the annotation with that id, with its pop-up (listed as other) and every reply and review status that answers it, which may sit anywhere on the page; their ids are gone with them. Needs the annotations feature, and forms too for a form field's widget.
ParameterTypeDescription
idstringThe annotation's id.
options?SignedDocumentOptions{ invalidateSignatures? }.
Returns Promise<void>
  • INVALID_ARGUMENT — an id no annotation has.
  • PAGE_ERROR — the annotation could not be removed.
  • FEATURE_NOT_LICENSED — the license lacks annotations (or forms, for a form field's widget).
  • DOCUMENT_CLOSED — the document has been closed.
  • INVALID_STATE — a certified document whose certification forbids it, unless options.invalidateSignatures; or a progressive document still downloading.
TypeScript
await doc.annotations.delete(id);
flattenmethodsince 1.0.0
flatten(pageIndex?: number, options?: SignedDocumentOptions): Promise<void>
Burns annotations into page content as they show on screen (Acrobat's default), dropping hidden ones. Omit the index for the whole document. Not reversible. Flattening on export keeps the page as it prints instead: what is marked not to print is dropped. Flattening a signed document changes what the signature covers, so it would no longer verify: refused as Acrobat refuses. Needs the annotations feature, and forms too where a form field's widget is flattened.
ParameterTypeDescription
pageIndex?numberZero-based page index. Omit for the whole document.
options?SignedDocumentOptions{ invalidateSignatures? }.
Returns Promise<void>
  • INVALID_ARGUMENT — a page index out of range.
  • FEATURE_NOT_LICENSED — the license lacks annotations (or forms, when a page to flatten carries form fields).
  • INVALID_STATE — a signed document, unless options.invalidateSignatures; or a progressive document still downloading.
  • PAGE_ERROR — a page could not be flattened.
  • DOCUMENT_CLOSED — the document has been closed.
TypeScript
await doc.annotations.flatten(); // the whole document

Comments

commentsmethodsince 1.0.0
comments(pageIndex?: number): Promise<CommentThread[]>
The page's comments as CommentThread[] (every page's, in page order, without pageIndex): each markup annotation with its replies, its reviewers' latest statuses and the latest status. Links, form fields, pop-ups and hidden comments are left out. Contents and authors are blank as list() blanks them without extract. Needs no feature.
ParameterTypeDescription
pageIndex?numberZero-based page index. Omit for every page's, in page order.
Returns Promise<CommentThread[]> Each { comment, replies, statuses, status }: the annotation commented on, its replies (replies to replies included, oldest first), each reviewer's latest status entry (oldest first) and the latest status any reviewer set, or null.
  • INVALID_ARGUMENT — a page index out of range.
  • DOCUMENT_CLOSED — the document has been closed.
TypeScript
for (const thread of await doc.annotations.comments()) {
  console.log(thread.comment.author, thread.replies.length, 'replies', thread.status);
}
replymethodsince 1.0.0
reply(id: string, reply: NewReply, options?: SignedDocumentOptions): Promise<string>
Writes a reply to the comment with that id. Edit a reply with update(…, { contents }), remove it with delete(). Needs the annotations feature.
ParameterTypeDescription
idstringThe comment's id.
replyNewReply{ contents, author? }; author omitted is ''.
options?SignedDocumentOptions{ invalidateSignatures? }.
Returns Promise<string> The reply's id.
  • INVALID_ARGUMENT — an id no annotation has, the annotation is not a comment (a link, a form field, a pop-up, a status entry), or contents is empty.
  • PAGE_ERROR — the reply could not be written.
  • FEATURE_NOT_LICENSED — the license lacks annotations.
  • DOCUMENT_CLOSED — the document has been closed.
  • INVALID_STATE — a certified document whose certification forbids it, unless options.invalidateSignatures; or a progressive document still downloading.
TypeScript
const replyId = await doc.annotations.reply(id, { contents: 'Agreed', author: 'Ann' });
setStatusmethodsince 1.0.0
setStatus(id: string, status: ReviewStatus, options?: StatusOptions): Promise<string>
Sets a reviewer's (options.author) status on the comment with that id. The entry's text is Acrobat's "Accepted set by Ann", or the status alone without an author. update() and delete() act on a status entry as on any annotation, rewriting that reviewer's history. Needs the annotations feature.
ParameterTypeDescription
idstringThe comment's id.
statusReviewStatus'accepted', 'rejected', 'cancelled', 'completed', or 'none' to clear theirs.
options?StatusOptionsauthor (the reviewer; omitted, '') and invalidateSignatures.
Returns Promise<string> The status entry's id.
  • INVALID_ARGUMENT — an id no annotation has, a status not in ReviewStatus, or the annotation is not a comment.
  • PAGE_ERROR — the status could not be written.
  • FEATURE_NOT_LICENSED — the license lacks annotations.
  • DOCUMENT_CLOSED — the document has been closed.
  • INVALID_STATE — a certified document whose certification forbids it, unless options.invalidateSignatures beside options.author; or a progressive document still downloading.
TypeScript
await doc.annotations.setStatus(id, 'accepted', { author: 'Ann' });

XFDF

exportXFDFmethodsince 1.0.0
exportXFDF(options?: XFDFExportOptions): Promise<string>

The annotations and the form field values as XFDF — the interchange format Acrobat, Nutrient and Apryse read. Every text mark, note, shape, drawing, polygon, free text and stamp with its name, author, subject, dates, color, opacity, flags, contents, pop-up and geometry; replies (inreplyto) and review statuses (state); the fields' values (<fields>, nested by the dots in their names) as doc.forms.getValues() reads them. A stamp goes out by its name and box alone — its picture stays in the document; a member of an annotation group goes out as an annotation of its own. Dates go out as PDF date strings, coordinates are the page's own, colors #RRGGBB.

Names are the hinge: the SDK writes every annotation's own name as its name, matches by it on import, and reads Acrobat's own names back, so a round trip through a review backend neither duplicates nor loses a comment. Needs the extract feature (the file carries the document's words) and, for the annotations, annotations.

ParameterTypeDescription
options?XFDFExportOptionsannotations (default true), fields (default true), links (default false: a link has no name in most files and would be duplicated by a re-import), pages (zero-based; omitted, every page), href (the <f href> file name Acrobat shows).
Returns Promise<string> A UTF-8 XML string.
  • INVALID_ARGUMENT — pages is not an array of pages of the document.
  • FEATURE_NOT_LICENSED — the license lacks extract, or annotations for the annotations.
  • DOCUMENT_CLOSED — the document has been closed.
TypeScript
const xfdf = await doc.annotations.exportXFDF({ href: 'contract.pdf' });
await fetch('/reviews/' + id, { method: 'PUT', body: xfdf });
importXFDFmethodsince 1.0.0
importXFDF(xml: string, options?: XFDFImportOptions): Promise<XFDFImportResult>

Applies an XFDF file: its annotations are written as create(), reply() and setStatus() write them, with the file's names, authors, subjects and dates (a freetext as the Text Box tool writes it, from its <defaultappearance>), and its <fields> go through doc.forms.setValues().

options.mode: 'merge' (default) — an entry whose name the document already has replaces that annotation in place, keeping the replies in the document the file does not bring back (unless the file moves it to another page, where a thread cannot follow: the old one goes with its thread), and every other entry is added; 'replace' — every annotation the file could describe (text marks, notes, shapes, drawings, polygons, free text, replies, statuses) is removed first, so the document ends up with the file's alone; stamps, links and form fields stay either way.

skipped reasons: unsupported (a stamp, caret, attachment, sound or redaction, a state that is not a review status, an unknown element), no_page, invalid (missing geometry or a refused value, or a name that belongs to a form field, link or pop-up), no_parent (a reply to nothing). flags are written as the file states them; a member of a group (replyType="group") becomes an annotation of its own; an empty attribute is read as none. A name the API cannot write (an obj:… id, one too long) gets a fresh UUID; dashes, icon and a pop-up's own place are read but not written. The license is checked for both halves before either is written, and one annotations change is announced at the end. Needs annotations when the file has annotations, forms when it has fields.

On a certified document the annotations go in only where its certification allows them ('annotations'): at 'forms' a file with annotations is refused with INVALID_STATE unless options.invalidateSignatures (pass annotations: false to take its field values alone), and at 'none' the field values are rejected as read_only.

ParameterTypeDescription
xmlstringA well-formed XFDF document.
options?XFDFImportOptionsmode ('merge', the default, or 'replace'), annotations and fields (default true; switch each half off) and invalidateSignatures.
Returns Promise<XFDFImportResult> imported and replaced ids, fields (what setValues reported, or null), and skipped — every entry not applied, with its element, page, name, a reason (unsupported, no_page, invalid, no_parent) and an English message.
  • INVALID_ARGUMENT — text that is not a well-formed XFDF document, or a mode that is neither.
  • FEATURE_NOT_LICENSED — the license lacks annotations (file with annotations) or forms (file with fields).
  • INVALID_STATE — a certified document whose certification forbids the file's annotations, unless options.invalidateSignatures; or a progressive document still downloading.
  • DOCUMENT_CLOSED — the document has been closed.
TypeScript
const { imported, replaced, skipped, fields } = await doc.annotations.importXFDF(
  await (await fetch('/reviews/' + id)).text(),
);
if (skipped.length > 0) console.warn('Not applied:', skipped);