Online forms are picky. A passport photo must be 2×2 inches. A signature must sit under 20 KB. A school office has one scanned sheet with thirty student photos and needs thirty clean files by tomorrow.
Most “free” tools solve that by uploading your ID photo to a server. I did not want that for Crop Desk, so every tool on cropdesk.co.in runs in the browser. The sheet, the crop, the ZIP, and the JPEG encode all stay on the device.
This post walks through three pieces: canvas cropping, sheet auto-detection with thresholding and connected components, and hitting an exact KB target with a binary search on JPEG quality. The rest of the toolkit (PDF merge/split/compress, image↔PDF, format conversion, age calculator) follows the same privacy model.
Why client-side only
The data path is intentionally short:
File → FileReader / createImageBitmap → draw on <canvas> → canvas.toBlob → download or JSZip.
There is no upload step in the happy path. After the page loads, the tools keep working offline. Ads pay for hosting; there is no account and no “pro” paywall for basic crops.
That constraint shaped the algorithms. No cloud vision API for box detection. No server-side ImageMagick for compression. Everything has to finish in a second or two on a mid-range Android phone, because that is where a lot of form uploads actually happen.
Canvas cropping (the boring foundation)
Every crop is the same pattern:
- Load the image into an
HTMLImageElementorImageBitmap. - Create a canvas sized to the output pixels (e.g. 600×600 for a US 2×2 at 300 DPI, or whatever the preset needs).
-
drawImage(source, sx, sy, sw, sh, 0, 0, dw, dh)to copy the selected region and scale it in one call. - Export with
toBlob('image/jpeg', quality)or PNG/WebP.
Presets (US 2×2, 35×45 mm, Instagram, YouTube thumbnails, and similar) are mostly metadata: target width/height, optional print DPI label written into the file, and UI guides such as head-size lines for passport photos. The crop math does not care which portal you are filling — only the pixels you ask for.
For print-oriented sizes, convert millimetres or inches to pixels with px = mm / 25.4 * dpi (or inches × dpi). DPI itself does not change pixel data; some forms still check the EXIF/JFIF density tag, so a small metadata pass helps without re-encoding quality.
Signature background removal is the same canvas pipeline with an extra pixel pass: after drawing the crop, treat near-white (or near-paper) samples as background — either fill them white for JPG forms, or set alpha to 0 when exporting PNG.
Auto-detecting photos and signatures on a sheet
The bulk tool is for a common real workflow: one A4 scan (or phone photo of a sheet) with a grid of passport photos and signature boxes. Manual cropping per person is slow and easy to mis-number when you are preparing a whole class.
Rough pipeline:
- Downscale a working copy so luminance and labeling loops stay cheap (detection runs on the small image; crops still use full resolution).
- Threshold to a binary mask. Scanned forms are usually dark ink/photos on light paper, so a luminance threshold — or a simple adaptive threshold when lighting is uneven — separates “ink/photo” from background.
- Connected components on the foreground mask (4-connectivity via union-find or two-pass labeling). Each blob gets a bounding box and area.
- Filter dust, staple marks, and full-page borders using minimum area and aspect-ratio bounds.
- Classify remaining boxes: larger, squarer regions tend to be photos; wider, shorter ones tend to be signatures. Layout heuristics (Sheet A: signature above photo; Sheet B: photo above signature) pair them and sort in reading order (top-to-bottom, then left-to-right within a row).
- Draw green / dashed overlays so the user can nudge one box or all boxes, or scale them slightly, before export.
Edge cases are honest limitations: skewed phone photos, heavy shadows, patterned table backgrounds, and unusual form layouts. For those, Blank mode lets you draw boxes, add a grid, or reuse a saved layout from last week’s batch. Detection is a helper, not a guarantee.
When you confirm, each box is mapped back to full-resolution coordinates, cropped on a canvas, and named in order: 1_photo.jpg, 1_sign.jpg, 2_photo.jpg, … then packed with JSZip in the browser. Numbers follow box order on the sheet, so a numbered attendance list stays aligned with the ZIP.
Compressing to an exact KB target
Upload portals say “photo max 50 KB,” not “quality 0.72.” JPEG size is not linear in the quality slider, but it is monotonic at fixed dimensions: higher quality never produces a smaller file. That makes binary search a clean fit.
Sketch of the approach used in the compressor:
async function fitQuality(canvas, targetBytes) {
const floorQ = 0.3;
const ceilQ = 0.95;
const atCeil = await canvasToJpeg(canvas, ceilQ);
if (atCeil.size <= targetBytes) return atCeil;
const atFloor = await canvasToJpeg(canvas, floorQ);
if (atFloor.size > targetBytes) return null; // need smaller dimensions
let lo = floorQ, hi = ceilQ, best = atFloor;
for (let i = 0; i < 8; i++) {
const mid = (lo + hi) / 2;
const blob = await canvasToJpeg(canvas, mid);
if (blob.size <= targetBytes) {
best = blob;
lo = mid; // try higher quality
} else {
hi = mid; // too big — lower quality
}
}
return best;
}
canvasToJpeg is just canvas.toBlob(..., 'image/jpeg', q) wrapped in a Promise. Eight iterations are enough to land close to the best quality that still fits; more iterations give diminishing returns because the encoder’s quality scale is coarse.
If even floor quality is too big, quality alone cannot win. Encoded size scales roughly with pixel area, so the fallback shrinks width and height by about sqrt(targetBytes / currentBytes), redraws onto a smaller canvas, and runs the quality search again. Landing a little under the target is normal and desirable — portals accept “≤ 50 KB,” and JPEG entropy depends on how detailed the picture is.
The same pattern backs “resize and stay under N KB” flows: lock the required pixel size first when the form demands it, search quality, and only shrink further if the floor still overshoots.
PDFs and the rest of the toolkit
Not every rejection is about photos. PDF merge, split, rearrange, rotate, and compress use client-side PDF libraries with the same privacy model: parse in memory, rewrite pages, download. Image→PDF draws canvas pages into a PDF; PDF→image renders pages back out. JPG/PNG/WebP conversion is decode → canvas → encode (with a chosen background fill when flattening transparency to JPG). An age calculator sits alongside for forms that ask for exact age on a reference date.
None of that requires your files on my server. Practical write-ups for passport sizes, KB limits, signatures, and HEIC quirks live at cropdesk.co.in/guides.
What I am not claiming
- Auto-detect will not nail every crumpled phone photo or fancy coloured form.
- Hitting “exactly 50.0 KB” is less useful than “highest quality under 50 KB.”
- Crop Desk is not affiliated with any exam board, university, or government portal — always check the official notification for sizes and formats.
- The site is free and ad-supported; ads are how hosting stays paid without a sign-up wall.
Try it
If you prepare form uploads, run a cyber café / CSC desk, teach a class that submits photos in bulk, or just hate uploading ID photos to random compressors, try the tools at https://cropdesk.co.in. Feedback on failed sheet detections, missing country presets, and PDF edge cases is welcome — those reports steer what gets fixed next.
Top comments (0)