The bug class we wanted to eliminate
PDF viewers can display only a cropped region of a larger MediaBox, and pages can also carry rotation. An annotation drawn against the visible PDF.js viewport must therefore be mapped back to the same visible rectangle when NoblePDF exports it. A simple “draw from 0,0 across the full page” rule is wrong for cropped files.
Measured output
| Case | Emitted placement / measured result | Status |
|---|---|---|
| Rotate 0°, CropBox [50 50 562 742] | Overlay covers x[50,562], y[50,742] | PASS |
| Rotate 90° | Rotation transform maps the overlay to the same visible CropBox | PASS |
| Rotate 180° | Rotation transform maps the overlay to the same visible CropBox | PASS |
| Rotate 270° | Rotation transform maps the overlay to the same visible CropBox | PASS |
| Reversed corners [562 742 50 50] | Normalized before export to the same 512×692 visible rectangle | PASS |
| Oversized [-100 -100 712 892] | Clipped to MediaBox before overlay placement | PASS |
Download the geometry fixtures
These four tiny PDFs make the unusual page-box cases concrete. They share a 612×792 MediaBox; the page content includes a rectangle marking the intended visible target.
[50 50 562 742]PDF ↓Reversed CropBox corners
[562 742 50 50]PDF ↓CropBox outside MediaBox
[-100 -100 712 892]PDF ↓CropBox + /Rotate 90
[50 50 562 742], rotation 90°PDF ↓
Why reversed and oversized boxes matter
Real-world PDFs are not always tidy. Corner order can be reversed, and a declared CropBox can extend beyond MediaBox. PDF.js normalises/intersects what it displays, so NoblePDF's export path must make the same decision or the annotation canvas and exported placement diverge. The V5.8.36.6 fix normalises box corners and clips the visible box before calculating overlay placement.
Rotation must preserve text too
The earlier editor path treated any rotated source page as destructive and rasterised it. That solved a geometry problem by losing searchable text. The replacement path composes source rotation and user-applied rotation while preserving original page text whenever the edit itself is non-destructive.
Regression rule: geometry is not considered fixed unless both placement and document semantics survive. A page that looks aligned but loses its searchable text is still a failed export.
How to reproduce the visible workflow
- Download one of the CropBox fixtures.
- Open it in NoblePDF's editor and add a small annotation near a visible corner.
- Export normally.
- Open the output in a second viewer and compare the annotation with the visible crop boundary.
- On the rotated fixture, also confirm the original text remains selectable.
Coordinate spaces involved in the test
The browser editor sees a PDF.js viewport, while the exported PDF uses PDF page coordinates. Those spaces can differ in origin, visible dimensions and rotation. The regression therefore checks the final emitted transform rather than assuming that a canvas pixel position maps directly to the PDF MediaBox.
The core fixture uses MediaBox [0 0 612 792] and visible CropBox [50 50 562 742], which is a 512×692 point rectangle. The measured export covers exactly x 50–562 and y 50–742. Reversed corners are normalized to the same bounds, while an oversized crop is intersected with the MediaBox before placement.
Internal regression-suite scope
Beyond the six published checks and four downloadable fixtures on this page, NoblePDF's internal release regression suite also exercised 8 geometry cases and 14 source/user rotation combinations. Those counts describe the internal suite; the reader-reproducible evidence published here is intentionally limited to the table and fixture files above.
Why the malformed cases stay in the test set
Reversed and oversized page boxes are legal or at least encountered in real-world PDFs even though authoring software normally writes cleaner values. Keeping those fixtures prevents a future “simplification” from reintroducing the bug. The rotated fixture also protects the separate requirement that alignment must not be bought by rasterising searchable source text.
The complete fixture hashes are listed in SHA256SUMS.txt, so the test inputs can be identified exactly.