new-site/api/migrations/090_esign_signature_vector.sql
justin b0a8563a93 ink-signature: pen-plotter pipeline for original wet-ink CMS signatures
The Standard no-login CMS path needs an ORIGINAL ink signature on paper
(CMS-10114: 'Stamped, faxed or copied signatures will not be accepted'). This
adds a pipeline to redraw the provider's own captured strokes in real ink with a
pen on a CR-10 V2 (or any Marlin/GRBL machine) — original, in ink, never copied.

- migration 090: esign_records.signature_vector (JSONB stroke paths, 0..1).
- signing page now captures normalized stroke paths alongside the PNG; API
  stores a size-bounded vector for drawn signatures.
- ink_signature_plotter.py (hardware-independent): fit strokes to the signature
  anchor box, PDF-pt -> bed-mm via jig offset, emit Marlin/GRBL G-code (Z pen or
  M280 servo/BLTouch), SVG toolpath preview, and render_signature_on_pdf (a
  digital twin that proves the toolpath lands on the cert line). Gated serial
  sender (dry_run default).
- ink_signature_cli.py: end-to-end load-record -> gcode+preview, --test-box jig
  calibration, --plot to stream over USB.
- Corrected CMS-10114 signature anchor to sit inside the Section 4A signing cell
  (above the bottom rule, below the label).
- docs/ink-signature-plotter.md documents the CR-10 retrofit + interpretive risk.

Tests: test_ink_signature.py 30/30, test_cms10114.py 27/27, test_paper_batch.py
15/15, API tsc clean, Astro build 58 pages.
2026-06-07 02:34:17 -05:00

29 lines
1.5 KiB
SQL

-- 090: Capture the vector (stroke-path) form of a drawn signature.
--
-- Today esign_records.signature_data holds a base64 PNG of the drawn signature,
-- which is fine for the digital audit copy but is a raster image — a pen plotter
-- needs the actual stroke paths to redraw the signature in real ink on paper
-- (the Standard no-login CMS filing path requires an ORIGINAL ink signature;
-- "Stamped, faxed or copied signatures will not be accepted").
--
-- We store the captured strokes as JSON so the same signing event yields both:
-- * signature_data — base64 PNG (digital stamp, audit trail)
-- * signature_vector — stroke paths (drives the pen plotter)
--
-- Format (normalized into a 0..1 box, origin top-left, matching canvas capture):
-- {
-- "v": 1,
-- "w": <canvas css width px>, "h": <canvas css height px>,
-- "strokes": [ [ {"x":0.12,"y":0.40,"t":12}, ... ], ... ]
-- }
-- x/y are fractions of the capture box (resolution-independent); t is ms since
-- stroke start (optional, for future pressure/speed modeling). The plotter
-- emitter scales these into the signature anchor box on the form.
ALTER TABLE esign_records
ADD COLUMN IF NOT EXISTS signature_vector JSONB;
COMMENT ON COLUMN esign_records.signature_vector IS
'Stroke-path form of a drawn signature (normalized 0..1, origin top-left). '
'Drives the pen-plotter ink-signature pipeline. NULL for typed signatures '
'or signatures captured before this column existed.';