The project file existed to persist state, so a future reader finding the exported flag would otherwise re-litigate it. The ADR states plainly that this reverses an earlier decision, what the original reasoning was, and what changed in use. ADR 0002 deferred the SVG renderer partly because re-export made it free to add later. That no longer holds, so its 'not stranded' bullet is struck through and pointed at ADR 0007 rather than left standing to mislead whoever revisits the SVG question.
3.1 KiB
Raster only in release 1; the SVG renderer is deferred
Vector PDFs are most of the newer corpus, and keeping them vector all the way to the viewer was an early goal — sheet music is line art, and SVG stays crisp at any tablet zoom. We measured it before building it, and decided to rasterize vector sources like everything else in release 1 and revisit the SVG renderer once real songs have been cut.
The measurement
One real vector song, 6 pages, 65 systems, rendered both ways:
| Approach | Total | vs WebP |
|---|---|---|
| WebP slices (600 DPI → 1920, ink→alpha, lossless) | 1.19 MB | 1× |
SVG, naive viewBox + clipPath |
26.0 MB | 40× |
SVG, set_cropbox per band |
26.5 MB | 41× |
| SVG, bounding-box cull + glyph subset | 3.09 MB | 2.6× |
- The naive cut is unusable. A
viewBox+clipPathslice contains the entire page's geometry and merely hides eleven-twelfths of it. set_cropboxdoes not help. MuPDF renders full page content regardless of the crop, so there is no free version of the cull.- The cull works. PyMuPDF emits a
<defs>glyph table (111 KB of a 256 KB page) referenced by<use transform="matrix(...)">, plus body<path>elements. Filter both by y-extent, then keep only the glyphs the survivors reference. Roughly 50 lines, 15× improvement.
Why defer, given the cull works
Not size. At 3.1 MB vs 1.2 MB per song — 225 MB vs 87 MB across a 73-song corpus — both are nothing on a homelab. The measurement killed the lazy implementation, not the idea.
What defers it is risk and missing evidence:
- The cull is heuristic parsing: glyph extents bounded at baseline ±14pt,
path extents read from raw
dcoordinates. It is over-inclusive by design, so it fails safe — but "fails safe" still means a slice quietly carrying a neighbour's slur, or a hairline dropped because the y-window was wrong on some publisher's output. That needs eyeballing per song, a QA loop the raster path doesn't have. - Rendering 65 complex SVGs in a scrolling column may be slower than 65 WebPs. Unmeasured.
- The deciding question is unanswerable from here: does 1920px WebP actually feel insufficient when pinch-zooming on a tablet? Cutting real songs answers it; more measurement doesn't.
Vector PDFs are also the clean case for the raster path — deskew is a no-op, detection works best, there are no scan artefacts — so rasterizing them is not a degraded fallback.
Consequences
- The geometry model stays renderer-agnostic, in normalised page coordinates, so adding the SVG renderer later is an output stage rather than a redesign.
Re-export from the project file regenerates every song's bundle without repeating human work, so songs cut before the SVG renderer exists are not stranded.No longer true — see ADR 0007. A project is spent once its song has been exported, so songs cut before the SVG renderer ships stay WebP unless they are cut again by hand.- noteman needs no SVG support (
image/svg+xml,.svgcontent type, CSP header on SVG responses) until the renderer ships.