PDF Annotation
Annotate PDFs in Potato with the pdf display type, covering span highlighting, bounding boxes on the page, cross-page linking, and OCR for scanned files.
The pdf display renders a PDF in the browser with PDF.js and puts the annotation surface on the rendered page rather than on text extracted beforehand. An annotator sees the real document, with its columns, tables, figures, and page breaks intact, and labels it in place. The display takes one required key, the field holding the PDF path or URL, and everything else is a display option.
instance_display:
fields:
- key: pdf
type: pdf
label: "Document"
display_options:
view_mode: scroll
max_height: 760
zoom: page-widthview_mode takes scroll, paginated, or side-by-side. Continuous scroll stacks every page in one container, which matters when an annotation has to reach across a page break. Paginated view shows one page at a time with navigation controls and suits long documents where the annotator works page by page.
Three annotation modes
annotation_mode decides what the annotator can draw, and it is the option that changes the task rather than its appearance. The default is span.
| Mode | What the annotator does | Anchored to |
|---|---|---|
span | Selects text and applies a label | The PDF.js text layer |
bounding_box | Draws a box anywhere on the page | Page coordinates |
link | Marks anchors, then connects them | Text spans and page regions |
Span mode depends on the PDF carrying a text layer, which is true of any PDF generated from a word processor or a typesetting system and false of a scan. Bounding-box mode does not, so a scanned page can still be labeled by region without any text extraction at all.
Because the pdf display anchors spans through the PDF.js text layer rather than the .text-content wrapper that other displays use, it does not accept the span_target flag. Span annotation still works, through the PDF's own text layer.
Cross-page linking
Link mode is for relations that span pages, such as a claim on page 2 that rests on a figure on page 9. The annotator marks anchors first, then draws typed links between them, and Potato records the anchors and the links under separate schema names.
instance_display:
fields:
- key: pdf
type: pdf
label: "Document"
display_options:
annotation_mode: link
view_mode: scroll
enable_text_anchors: true
enable_region_anchors: true
anchor_schema: pdf_anchors
link_schema: pdf_links
anchor_labels:
- name: claim
color: "#dc2626"
- name: figure
color: "#2563eb"
link_types:
- name: refers_to
directed: true
color: "#dc2626"
allowed_source_labels: [claim]
allowed_target_labels: [figure]allowed_source_labels and allowed_target_labels restrict which anchors a link type can join, which is how a guideline becomes something the interface enforces instead of something the annotator has to remember. A refers_to link configured as above can start only on a claim and end only on a figure, so a link drawn from a figure back to a claim is refused. Set directed: false for a symmetric relation such as same_as.
Anchors come in two kinds and both can be turned off independently. enable_text_anchors allows highlighting a text span, and enable_region_anchors allows drawing a region box, which is what a figure or a table needs. A complete worked example ships upstream in examples/advanced/pdf-link-scroll/.
Scanned documents and OCR
ocr is off by default, and Potato reads it only in link mode. When it is set, words are extracted server-side and used to build a client text layer, so a scan with no embedded text can still carry text anchors. The option takes false, true, or auto, and Potato rejects any other value.
instance_display:
fields:
- key: pdf
type: pdf
display_options:
annotation_mode: link
ocr: auto
ocr_dpi: 200
ocr_lang: engThe three settings differ in when the pass runs. false uses the embedded text layer alone, true always runs OCR, and auto falls back to OCR only when the embedded text layer comes back empty. Prefer auto on a mixed corpus, because OCR is slow and needs Tesseract installed. ocr_dpi controls the resolution each page is rasterized at before OCR reads it, and raising it costs time on every page. ocr_lang takes a Tesseract language code and defaults to eng.
Display options
| Option | Default | Effect |
|---|---|---|
view_mode | scroll | scroll, paginated, or side-by-side |
max_height | 700 | Container height in pixels |
max_width | none | Container width |
text_layer | true | Enables text selection |
show_page_controls | true | Page navigation controls |
initial_page | 1 | Page shown first |
zoom | auto | auto, page-fit, page-width, or a percentage |
annotation_mode | span | span, bounding_box, or link |
bbox_min_size | 10 | Smallest box accepted, in pixels |
bbox_colors | none | Per-label box colors |
show_bbox_labels | true | Draws labels on boxes |
thumbnail_sidebar | true | Page thumbnails in paginated view |
enable_text_anchors | true | Text spans usable as link anchors |
enable_region_anchors | true | Region boxes usable as link anchors |
anchor_schema | pdf_anchors | Schema recorded on anchors |
link_schema | pdf_links | Schema recorded on links |
ocr | false | false, true, or auto |
ocr_dpi | 200 | OCR rendering resolution |
ocr_lang | eng | Tesseract language code |
Word and Markdown files
A DOCX or Markdown file uses the document display instead, which keeps the document's heading and paragraph structure and does accept span_target. It reads annotation_mode of span or bounding_box, and show_outline builds a table of contents from the headings in the body. Potato passes the rendered markup through an allowlist before it reaches the annotator, so a corpus field carrying an executable tag is stripped rather than run.
instance_display:
fields:
- key: report
type: document
label: "Report"
display_options:
show_outline: true
max_height: 600Further reading
- Instance display lists every display type and which of them accept span targets.
- Span annotation covers the label options a span scheme takes.
- How to annotate documents works through a document-review task end to end.
- For implementation details, see the source documentation.