Skip to content

PDF export: broken oxc-parser native binding silently disables print post-pass (includeBleed/marks no-op) #142

Open

Description

@iagh66

Summary

/design:export pdf (and presumably the ⌘E dialog, same code path) silently ignores options.pdfPrint (includeBleed, marks) for kind="print" artboards. No error, no degraded flag — the output is just the raw Chromium-captured page (full bleed box, i.e. the artboard's own pixel size), regardless of what includeBleed/marks were set to.

Root cause

exporters/pdf.ts's post-pass resolves each page's print geometry via readArtboardPrintProp (canvas-edit.ts), which calls parseSync from oxc-parser. In this environment, oxc-parser's native binding fails to load when the dev-server process (a bundled Bun binary) tries to resolve it:

error: Cannot find module '/$bunfs/root/parser.darwin-arm64-3x28d8cw.node' from
  '/Users/iagh/.bun/install/cache/oxc-parser@0.152.0@@@1/src-js/bindings.js'

Since readArtboardPrintProp treats parsed.errors.length > 0 (or a thrown parse failure) as "not a print artboard" and returns null, the post-pass's if (printProp && printOpts) branch is never taken for ANY page — includeBleed: false, marks: {crop, registration}, all become silent no-ops. The exported PDF comes back as plain Chromium output (full bleed box, no TrimBox/BleedBox set, no marks drawn), with no error and no degraded flag on the export result.

Repro

curl -s -X POST http://localhost:<port>/_api/export \
  -H "Content-Type: application/json" \
  -d '{"format":"pdf","scope":"canvas-as-separate","options":{"pdfPrint":{"includeBleed":false}}}' \
  -o out.pdf
pdfinfo out.pdf | grep "Page size"
# Expected: trimmed page size (artboard size minus 2× bleed)
# Actual: unchanged — same size as the raw artboard (bleed included)

Reproduced against a kind="print" artboard whose print prop is a referenced const (print={A4_PRINT as any} where const A4_PRINT = { paper: "a4", orientation: "landscape", bleedMm: 3 } as const;) — resolveValueNode in canvas-edit.ts is written to follow exactly this pattern (identifier → VariableDeclarator → unwrap as const), so the prop-resolution logic itself isn't the issue; the parse step upstream of it never runs successfully.

Impact

  • Any export requesting includeBleed/marks on a print artboard silently returns the untrimmed, unmarked page instead.
  • No error surfaces to the caller — ExportResult.degraded is only set by the font-preflight path, not by a parse failure here. A user has no signal that their print-ready request was ignored.
  • Worked around locally by exporting via the fast path (no pdfPrint options → raw Chromium page, bypasses oxc-parser entirely) and post-processing the MediaBox with pypdf outside Maude.

Suggested fix

  1. Fix/pin the oxc-parser native binding resolution for the bundled Bun dev-server binary (likely an asset-bundling issue with Bun's $bunfs virtual FS not including the platform .node/.wasi.cjs binary oxc-parser expects at runtime).
  2. Defense in depth: when readArtboardPrintProp/parseSync fails for a page that explicitly requested pdfPrint options, surface this via ExportResult.degraded (or throw) rather than silently falling through to "not a print artboard" — a parse failure and "this isn't a print artboard" are different conditions and currently collapse to the same silent no-op.

Environment

  • macOS (darwin), Bun v1.3.3
  • oxc-parser@0.152.0
  • Maude dev-server running as a long-lived bundled process (maude design serve)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions