Files
noteman-slicer/docs/adr/0002-raster-only-svg-renderer-deferred.md
Esa Kataja 15f64e4131 Record the spent-project reversal as ADR 0007
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.
2026-07-28 23:51:53 +03:00

65 lines
3.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` + `clipPath` slice contains the
entire page's geometry and merely hides eleven-twelfths of it.
- **`set_cropbox` does 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 `d` coordinates. 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](0007-a-project-is-spent-once-exported.md). 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`, `.svg` content type, CSP header
on SVG responses) until the renderer ships.