Pdf Processor
Coordinate Spaces and Page Snap: A Three-Part Series on Silent Math Bugs in a PDF Editor
TLDR
Interactive document engineering tools can operate without throwing runtime errors while quietly emitting incorrect math outputs. During UI development of the Ginexys PDF Processor, three silent defects were identified: unit mismatch between PDF user points and viewport pixels, scroll synchronization errors between fixed spatial PDF pages and variable semantic HTML documents, and state drift across three concurrent editing surfaces.| Series Part | Foundational Invariant | Architectural Remediation |
|---|---|---|
| Part 1: Coordinate Spaces | Unit equivalence across transforms | Derive explicit vWidth/vFont scale properties |
| Part 2: Multi-Surface Sync | Spatial vs. Semantic ruler separation | IntersectionObserver ratio-based page tracking |
| Part 3: Single Source of Truth | State consistency across surfaces | Controlled input pattern with source guard flags |
| Part 4: Root Pattern Analysis | Equivalence discipline | Strict unit type segregation & invariant checks |
The four-part invariant series
Part 1: coordinate space discipline
- The Problem: PDF.js emits text positions in PDF user space (points) and viewport elements in screen pixels. Mixing points and pixels in heuristic calculations causes $33\%$ measurement errors at $1.5\times$ scale without crashing.
- The Fix: Derive
scaleX/scaleYfrom the viewport matrix and store both PDF points (fontSize,width) and viewport pixels (vFont,vWidth) explicitly on every metadata record.
Part 2: spatial vs. Semantic page synchronization
- The Problem: Fixed-height spatial PDF pages ($1,100\text{px}$ per page) cannot share scroll offsets with variable-height semantic HTML documents ($200\text{px}$ to $2,500\text{px}$ per page depending on content).
- The Fix: Replace scroll-offset math with an
IntersectionObserverratio-based page tracking engine.
Part 3: single source of truth across editing surfaces
- The Problem: Three concurrent editing surfaces (Visual Preview, Raw HTML Editor, Monaco Diff) became out of sync when edits occurred in one surface.
- The Fix: Implement a controlled-input architecture with shared mutation guard flags (
isInternalUpdating = true) and source-origin tracking.
Part 4: the invariant anti-pattern
- The Problem: Treating distinct abstractions as equivalent (points vs. pixels, spatial scroll vs. semantic height, multi-surface state).
- The Fix: Enforce explicit unit types and invariant validation checks across domain boundaries.
Rule of thumb: Silent math bugs survive unit tests because they fail gracefully without crashing. Validate foundational domain invariants explicitly across transform boundaries.
Read this post in the full Engineering Journal →