I run SmokePDF, a set of free PDF tools that work entirely in the browser (built on pdf-lib and pdf.js). Before writing a guide about cropping, I tested my own crop tool on a deliberately nasty file. It failed on half the pages, and the test also showed something most people don't know about cropping.
The test file
Six fictional pages, each a case that shows up in real documents:
1. A normal memo with an internal reference in the footer and a red reviewer note in the left margin ("budget not final").
2. A fake scan with an uneven black border.
3. A landscape page with a table.
4. A page stored rotated: it displays as portrait but is stored as landscape with a /Rotate value. Very common in scans.
5. A page whose coordinates don't start at zero (its MediaBox origin is shifted). Looks identical to page 1 in a viewer.
6. A page that was already cropped once before.
Same values for every page: 8% top, 6% bottom, 5% right, 8% left.
What broke
Pages 1–3 were fine. The other three were not:
• Rotated page → wrong sides cropped. The tool measured sides on the page as stored, not as displayed. It took 5% off the top instead of 8%.
• Shifted origin → mangled page. The frame was computed as if the page started at (0, 0). Result: ~20% cut from the top, lines of text sliced in half, and the red note still visible.
• Already cropped → the page got bigger. The new frame was calculated from the full sheet, so it brought back areas the earlier crop had hidden.
And the preview only showed page 1, so none of this was visible before downloading.
The fix (pdf-lib)
Three rules: start from the current CropBox (not the MediaBox), respect its real origin, and convert "as displayed" sides into "as stored" sides.
// top/right/bottom/left are fractions (0.08 = 8%) as the user SEES the page
function cropPage(page, top, right, bottom, left) {
const box = page.getCropBox(); // current visible area, with its real x/y origin
const angle = ((page.getRotation().angle % 360) + 360) % 360;
// map displayed sides -> stored sides
let [t, r, b, l] = [top, right, bottom, left];
if (angle === 90) [t, r, b, l] = [right, bottom, left, top];
if (angle === 180) [t, r, b, l] = [bottom, left, top, right];
if (angle === 270) [t, r, b, l] = [left, top, right, bottom];
page.setCropBox(
box.x + l * box.width,
box.y + b * box.height,
box.width * (1 - l - r),
box.height * (1 - t - b)
);
}
I also added a preview for every page, not just the first.
The part most people miss
Even with a perfect crop, nothing is deleted. Cropping only sets the CropBox, the area a viewer shows. Everything outside it is still in the file.
On page 1, the footer reference and the red note disappeared from view, but pdftotext on the output still printed both. Many editors also show the full page if you open the file in them.
So if a margin holds something the recipient shouldn't see, a normal crop is not enough. In the tool I added a "permanently remove what is outside the frame" option: the page is rendered and only the visible area is kept. That really removes the hidden content, with a trade-off: the page becomes an image, so text is no longer selectable.
Takeaways
• Build crop math on getCropBox() with its origin, never on (0, 0, width, height).
• Always account for /Rotate when users think in "top/left".
• Cropping hides, it doesn't delete. For anything sensitive in the middle of a page, use real redaction instead.
The full write-up, with the measurements for each page, is on the SmokePDF blog: https://smokepdf.com/en/blog/crop-pdf-margins/
Top comments (0)