"""Runnable check for project state: round-trip, cut edits, discard pre-set.""" from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from noteman_slicer.detect import PageDetection, System # noqa: E402 from noteman_slicer.project import Cut, Project, default_path # noqa: E402 def main() -> int: tmp = Path(__file__).with_name("_tmp") tmp.mkdir(exist_ok=True) pdf = tmp / "song.pdf" pdf.write_bytes(b"%PDF-1.7 not really a pdf, only its bytes are hashed") height = 1000 detection = PageDetection( skew=-1.1, systems=[System(200, 400, 60.0), System(600, 800, 60.0)], cuts=[500], ) project = Project.from_detection(pdf, [detection], [height]) page = project.pages[0] # One cut between the systems, plus a boundary cut above the first and # below the last — so the header and footer become their own slices. assert len(page.cuts) == 3, [c.points for c in page.cuts] assert page.discards == [True, False, False, True], page.discards assert page.slice_count == 4 assert project.kept_slices() == [(0, 1), (0, 2)], project.kept_slices() # Geometry is normalised, so it survives any change of resolution. assert all(0.0 <= y <= 1.0 for cut in page.cuts for _, y in cut.points) assert page.cuts[1].straight_y == 0.5 # A straight cut is flat; a stepped one is not, and interpolates. step = Cut([(0.0, 0.20), (0.35, 0.20), (0.35, 0.40), (1.0, 0.40)]) assert step.straight_y is None assert step.y_at(0.0) == 0.20 assert step.y_at(1.0) == 0.40 assert step.y_at(0.35) == 0.20 or step.y_at(0.35) == 0.40 assert step.highest == 0.20 and step.lowest == 0.40 # Adding a cut splits a slice and keeps that slice's flag on both halves. before = page.slice_count index = page.add_cut(Cut.straight(0.65)) assert page.slice_count == before + 1 assert index == 2, index assert page.discards == [True, False, False, False, True], page.discards # Removing it merges them again. page.remove_cut(index) assert page.slice_count == before assert page.discards == [True, False, False, True], page.discards # Round-trip. saved = project.save() assert saved == default_path(pdf), saved reloaded = Project.load(saved) assert reloaded.pages[0].skew == -1.1 assert reloaded.pages[0].discards == page.discards assert [c.points for c in reloaded.pages[0].cuts] == [c.points for c in page.cuts] assert reloaded.source_hash == project.source_hash assert not reloaded.source_changed() # A project is spent once exported: reopening starts fresh. assert not reloaded.exported reloaded.exported = True reloaded.save() assert Project.load(saved).exported # A PDF edited underneath must be reported, not silently re-cut. pdf.write_bytes(b"%PDF-1.7 different bytes entirely") assert reloaded.source_changed() for f in (pdf, saved): f.unlink() tmp.rmdir() print("ok") return 0 if __name__ == "__main__": sys.exit(main())