Commit Graph
2 Commits
Author SHA1 Message Date
Esa Kataja 6ff956a622 Add detection: skew, system anchors, ink extents, cuts, staff height
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
2026-07-28 22:38:50 +03:00
Esa Kataja e2f3e8fbdc Add package skeleton, source classification and raster loading
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
2026-07-28 22:33:26 +03:00