PDF pages embed in canvas boards as vector content
PDF file nodes on a JSON Canvas board now render as the PDF's actual page — vector, tagged, with #page=N honoured — instead of a placeholder card.
A canvas board that embeds a PDF no longer loses its content. POST /api/v1/canvas imports one page of each .pdf file node as vector content — the page's own text, lines and images, copied into the output as a Form XObject. Text stays selectable and searchable, and the page renders sharp at any zoom, exactly like the rest of the board.
The page comes from the node's subpath the way Obsidian writes it: three-pages.pdf with subpath #page=2 embeds page 2, a bare path embeds page 1. The page is contain-fitted and centred in the node's rectangle with no card border, and it joins the accessibility tree as a single Figure whose alt text is PDF: <file name>, page N — so a screen reader announces which document and page the figure is.
The import is deliberately careful. Before anything is copied, the source page goes through a conservative compliance check — encryption, fonts without embedded programs, CMYK colour spaces, optional content, JavaScript and other things that would break the output's PDF/A-2A and PDF/UA-1 conformance all reject the import. Any rejection is non-fatal: the node renders the same placeholder card as before, and the response's layoutWarnings says exactly why (canvas-pdf-not-embeddable with a reason). A #page= outside the document falls back to page 1 with canvas-pdf-page-out-of-range; a PDF over 5MB, or an 11th PDF on one board, warns with canvas-pdf-limit.
Send PDFs the same way as images: a files entry keyed by the node's file path, { "data": "data:application/pdf;base64,…" }. A bare https:// PDF path is fetched for you (5MB cap, same protections as images). Annotations, embedded XMP metadata and the source's own tags don't travel with the page — the import is one figure, so a source file can't break the board's structure tree. See docs/json-canvas.md for the full behaviour and the fallback rules.