Without components: exportPdf(pdfBytes, annotations, options), importAnnotations(pdfBytes, options), annotationsToMarkdown, annotationsToJSON / annotationsFromJSON from svelte-pdf-mini/core.
The contract
- Standard annotations. Every kind is written as its PDF counterpart (Highlight, Underline, StrikeOut, Squiggly with QuadPoints; Square, Circle, Line, PolyLine, Polygon, Ink, Text, FreeText, Stamp) with an appearance stream, plus
/Contents,/T(author), dates,/NM(id), popups and reply links. Apple Preview, Acrobat, Chrome and Firefox show them. - Lossless round trip. Fields PDF can’t express (palette key, label, tags, quote, Markdown,
extra) are stored in a private/SPM_Datakey on each annotation and in an embeddedsvelte-pdf-mini.json. Opening the file again withimportFromPdfgives back exactly what you exported. - Edits elsewhere win. If someone recolours or moves an annotation in another app, the standard fields override the stored copy on import.
- Foreign annotations already in the PDF are kept; with
importFromPdfthey load asorigin: 'foreign'(policy: theforeignprop —'editable','readonly'or'hidden'). Deleting an editable one sticks: the store lists it instore.removedForeign, andstore.exportPdf()passes that list as theremoveoption. Exporting yourself (in a worker, say)? Pass{ remove: store.removedForeign }.
Options
Apple Preview
Preview re-saves the whole file: it keeps/SPM_Data and appearance streams but drops /NM, /CA, /RC, /IRT and the embedded JSON. Import rebuilds from /SPM_Data and merges field by field, so a recolour made in Preview is picked up while labels and notes survive. Incremental updates match the original file's cross-reference format, which Preview requires.Encrypted PDFs
Many publisher PDFs carry an owner password that only restricts permissions: they open without a password, and exportPdf decrypts them and writes a full, unencrypted copy (the restrictions go, like in pdf.js and Preview). A PDF that needs a password to open can’t be saved: that would strip its protection, so exportPdf throws a PdfSaveError (reason: 'password').
Check before the user starts annotating: store.saveSupport (set by importFromPdf), result.saveSupport from importAnnotations, or await saveSupport(bytes):
Bytes before the %PDF- header (some servers prepend them) are dropped on export.
Re-anchoring
Highlights store their quote with a little context. When a different version of the paper is opened, reanchor (prop or store.reanchor()) finds each quote again and moves its quads, flagging the ones it cannot find as extra.orphan.