Everything the human decides, in normalised page coordinates so the
file is independent of DPI and of which renderer produced it. The
bundle will be generated from this, which is what makes re-export
possible without repeating human work.
Cuts are polylines from the start, two points being the ordinary
straight case, so the stepped cuts Engel needs are a data question
rather than a migration. Adding a cut splits a slice and copies its
discard flag to both halves; removing one merges them.
Boundary cuts belong here rather than in detection: detection emits
cuts only between systems, so a page would have exactly as many slices
as systems, with the header and footer inside the first and last.
Isolating and discarding them is a slicing decision.
Saves are write-then-rename, so a crash mid-save cannot destroy the
previous state. The PDF is hashed, not copied, so an edit underneath is
reported rather than silently re-cut.
Ketun joululaulu now yields 24 kept slices over 12 pages and Feliz
Navidad 20 over 4, with headers and footers discarded on every page.
Closes#10, #11, #13
Systems anchor on the vertical bracket and expand over nearby ink, per
ADR 0006. Two corrections the corpus forced:
- Expansion measures distance from the bracket, never from the growing
extent. A chaining expansion hops between the closely-stacked lines of
a title block and walks the whole way up the page — on Engel p1 it
swallowed the title into system 1 and the copyright footer into
system 3.
- Reach is 1.5 staff heights, which takes in the lyrics below the last
staff and the tempo mark and INTRO box above the first, while leaving
the title block and footer out.
Verified against the corpus. Ketun joululaulu: 2 systems per page except
p7 (3) and p12 (1), staff 48px throughout, skew -2.7 to +1.2 per page,
and p2 system 1 spans 179-1071 where its bracket is 177-994 — the
difference being the bottom voice's lyric line. Engel: 2-3 systems per
page, staff 70-72px. Elaman nalka: 3 systems per page, 0 skew, staff
118px at 600 DPI.
The overlay dump is what made both bugs visible, and stays as the tool
for diagnosing a page that comes out wrong.
Closes#4, #5, #6, #7, #8, #9
The pdf module is the whole of M1 except the debug overlay, which needs
detection results to draw.
Source type is detected per PDF by looking for a page-covering image,
and is always reported for confirmation rather than applied silently
(ADR 0004). Rasters are extracted via Pixmap(doc, xref) rather than by
decoding extract_image() bytes, because MuPDF handles JBIG2 and CCITT
scans that no image library will.
Scanned pages are extracted at the embedded image's native resolution;
only vector pages are rendered, at 600 DPI. Verified against the corpus:
Elaman nalka (vector) renders 4959x7017, Ketun joululaulu (scan) loads
1653x2332, Engel (scan) 2552x3504 — and Engel's page 2 is 2480 wide
where page 1 is 2552, so scan width varies within one PDF.
tests/test_pdf.py builds its own PDFs so the check runs without corpus
files, which are copyrighted and gitignored.
Closes#1, #2, #3