Files
noteman-slicer/docs/adr/0005-pymupdf-for-all-pdf-access.md
Esa Kataja 46ae7e813a Replace the handoff notes with a durable spec and ADRs
The handoff was written as a message to relay information; several
decisions lived only there. Split into permanent homes:

- docs/spec.md — scope, geometry model, pipeline order, detection,
  levels, editor, project file, markers, bundle format, noteman's
  changes, reference values
- ADR 0002 — raster only in release 1; SVG slices measured at 40x WebP
  naive, 2.6x with a bounding-box cull, deferred on risk not size
- ADR 0003 — lossless WebP with levels and 16-level alpha; every lossy
  option and alternative format measured larger for line art
- ADR 0004 — detection proposes, the human disposes; no unattended mode
- ADR 0005 — PyMuPDF for all PDF access, accepting AGPL
- ADR 0006 — systems are found by vertical brackets, not row-darkness
  gaps, which miscount every page of a 6-voice score

Also from testing against the hardest score in the repertoire: scanned
PDFs carry their scan as an embedded image and must be extracted at
native resolution rather than re-rendered at 600 DPI, and per-page
deskew is mandatory (skew varies -2.6 to +1.2 within one PDF).
2026-07-28 22:05:09 +03:00

1.9 KiB

PyMuPDF for all PDF access, accepting AGPL

All PDF work — rasterizing at a chosen DPI, exporting SVG, and inspecting page content to classify a source as bitmap or vector — goes through PyMuPDF. It is a single wheel with MuPDF bundled, so the tool needs no system packages. Its licence is AGPL-3.0, which we accept.

Why not the permissive combination

The obvious permissive stack was pypdfium2 (Apache/BSD) for rasterizing plus mutool or pdftocairo shelled out for SVG. Both of those are system packagesmupdf-tools, poppler — and a system package on the vector path is precisely the failure the language choice was made to avoid: the tool is supposed to install once and run from any directory on any machine.

The SVG step can't simply be skipped, either. Music glyphs come from a notation font (Emmentaler, Bravura, or Sibelius/Finale's). An SVG that references a font renders as garbage on a device that lacks it, so text must be converted to paths at export. PyMuPDF does this by defaultpage.get_svg_image(text_as_path=1), verified to emit <path> elements and zero <text> — so the font risk is closed with no extra tooling.

Mixing the two (pypdfium2 for raster, PyMuPDF only for SVG) is the worst option: two libraries with overlapping responsibilities, and AGPL linked in anyway.

Consequences

  • The AGPL propagates only if the slicer is published. For a local personal tool it costs nothing. A future permissive release would need the rasterizer swapped back to pypdfium2 — a contained change, since PDF access sits behind the renderer-agnostic geometry model.
  • Source-type detection comes free from the same library: get_images() plus a full-page-image area check distinguishes a scan from an engraving.
  • The SVG export path is present and working even though the SVG renderer is deferred — see ADR 0002.