Phase 4 — closes the last gap in the ExportDialog. Each PenPage
becomes one PDF page laid out at its content bounding box plus a
16-pt margin. Empty pages are skipped; all-empty docs return
'nothing to export' to match the raster export convention.
Mechanism: `skia_safe::pdf::new_document(&mut buf, None)` returns
the document; per-page `begin_page` / `end_page` reuses the same
`paint_node` pipeline as the raster path, so every variant the PNG
exporter handles (Rect / Ellipse / Polygon / Line / Path / Frame /
Group with rotation + corner radius) emits as real vector PDF ops —
glyphs and shapes stay selectable and zoom-clean. The TS app hand-
rolls a PDF stream (Catalog + Pages + Image XObjects with DCTDecode
JPEG blobs); skia's backend produces a smaller, sharper file.
Wiring:
- `export.rs::page_bounds` + `paint_node` exposed as `pub(crate)`
so the new `export_pdf.rs` sibling can reuse them.
- `Cargo.toml`: skia-safe `pdf` feature flag enabled.
- `persistence.rs::ExportImageConfirm` PDF branch now calls
`export_pdf::export_pdf` instead of returning the placeholder
error.
- `ExportFormat::is_implemented` now returns true for every variant
so the dialog stops greying out the PDF pill.
Tests: 2 unit tests in `export_pdf.rs` cover `%PDF-` header +
`%%EOF` trailer for a 2-page doc, plus the all-empty failure path.
197 total tests pass.
Phase 5 codex review still pending; will land in the next pass.
|
||
|---|---|---|
| .. | ||
| openpencil-app | ||
| openpencil-desktop | ||
| openpencil-shell-core | ||
| openpencil-shell-native | ||
| openpencil-shell-web | ||
| pen-codegen | ||
| pen-core | ||
| pen-engine | ||
| pen-figma | ||
| pen-types | ||
| wasm-libc-shim | ||
| CLAUDE.md | ||