Web viewer

Drawing and notes in the web viewer

Let visitors draw, highlight text and leave notes on a PDF in your site, download the result, and save the annotations to your own server as JSON.

With the annotate attribute, visitors can mark up a PDF right in your page: draw with a pen, highlight or underline text, leave notes and add shapes. Download then gives them the PDF with their marks in it. Nothing leaves the browser unless your own page sends it somewhere.

<vive-pdf src="/files/contract.pdf" annotate></vive-pdf>

The drawing tools are off by default. Without annotate the viewer does not even download their code, so reading-only pages stay as light as before.

The tools

The Draw button in the toolbar opens a second bar above it.

Tool What it does
Pen Free drawing
Marker A wide, see-through stroke, like a highlighter pen
Highlight, underline, strike out text Drag across text to mark it
Note Click to pin a note, then type in the box that opens
Rectangle, ellipse Drag to draw an outline
Arrow Drag from the tail to the tip

Next to the tools are six colours, three line widths, Undo, Redo, Delete selected and Close. Clicking a mark selects it: it can then be moved, resized, recoloured or deleted. The marks a PDF already has can be selected and changed too.

Keys while the bar is open: Ctrl/Cmd+Z undoes, Ctrl/Cmd+Shift+Z redoes, Delete or Backspace deletes the selected mark and Escape puts the tool down.

Text tools need real text

Highlight, underline and strike out work on text the PDF really contains. A scanned page is a picture: use the pen or the marker there.

Notes keep every letter, including ı, ş, ğ and İ. There is no free text box on the page itself yet, because the standard PDF fonts it would use cannot show those letters.

Download and print

Once something is drawn, Download saves a copy of the PDF with the marks in it, under the file’s own name. Print puts them on paper. The original file on your server is not changed.

The marks are standard PDF annotations, so Adobe Acrobat, Preview, the vivePDF app and other readers show them and can edit them.

For developers

Four methods and one event let your page keep the marks, for example per user on your own server.

What it does
annotations() Resolves with the list of marks on every page, including those the PDF already had
setAnnotations(list) Puts the list in place of all those marks; it does not fire vive-annotationchange and clears Undo
save() Resolves with the PDF, marks included, as a Blob
setTool(name) Opens the drawing bar with a tool: pen, marker, highlight, underline, strikeout, note, rectangle, ellipse or arrow; null puts the tool down
vive-annotationchange Fires with { type, page } when a mark is created, updated or deleted

Saving to your server

const viewer = document.querySelector("vive-pdf");

viewer.addEventListener("vive-ready", async () => {
  const response = await fetch("/api/marks?file=contract");
  if (response.ok) await viewer.setAnnotations(await response.json());
});

let timer = 0;
viewer.addEventListener("vive-annotationchange", () => {
  clearTimeout(timer);
  timer = setTimeout(async () => {
    const marks = await viewer.annotations();
    await fetch("/api/marks?file=contract", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(marks) });
  }, 1000);
});

To keep the whole PDF instead, send await viewer.save() as the request body.

The format

Each mark is a small object. Positions are in PDF points (1/72 inch) from the top-left corner of the page, page counts from 1 and color is #rrggbb. opacity (0–1) and text are optional.

type Fields
pen, marker width, strokes: a list of strokes, each a list of { x, y }
highlight, underline, strikeout boxes: one { x, y, width, height } per line of text
note box: where the note icon sits; text: the note
rectangle, ellipse width, box: the outline
line, arrow width, from, to: { x, y }; an arrow’s tip is at to
[
  { "type": "note", "page": 1, "color": "#FFCD45", "opacity": 1, "text": "Check the dates", "box": { "x": 400, "y": 90, "width": 20, "height": 20 } },
  { "type": "pen", "page": 2, "color": "#E44234", "opacity": 1, "width": 4, "strokes": [[{ "x": 100, "y": 600 }, { "x": 220, "y": 640 }]] }
]

Links, form fields, stamps and other kinds of annotation are not in the list and are left alone by setAnnotations().

Checks on the input

setAnnotations() reads the whole list before it changes anything. If an entry is wrong, nothing changes and the promise rejects with code invalid and a message that names the entry and the field, for example annotation 3 is not valid: color must look like #rrggbb.

Limit
Marks in one list 5 000
Points in one drawing 20 000
Boxes in one highlight 1 000
Characters in one note 2 000
Line width 0.1 to 100 points

Only the fields above are read. Anything else in an entry, such as a link address, a picture or a script, is dropped, so a list from an untrusted source cannot add them to the PDF.

Errors

The four methods reject with an Error whose code is one of:

Code Meaning
annotate-off The element has no annotate attribute
not-ready No document is open yet; wait for vive-ready
invalid setAnnotations() got a list it cannot use
replaced Another document was opened before the answer came
failed The marks could not be written into the PDF