← 피드로
Building a Browser-Based Passport Photo Maker
Passport photos have strict government specifications — exact dimensions, specific background colors, and precise head positioning. Here’s how to build a tool that handles all of this entirely in the browser.
The Challenge
Each country has different requirements:
Country Size (mm) Size (px at 300 DPI) Background US 51×51 600×600 White UK/EU 35×45 413×531 Light gray India 35×45 413×531 White Canada 50×70 591×827 White China 33×48 390×567 White Japan 35×45 413×531 White Australia 35×45 413×531 White France 35×45 413×531 Light gray Germany 35×45 413×531 Light gray UAE 43×55 508×650 WhiteLoading and Displaying the Image
First, load the user’s image and display it with an interactive crop overlay:
js
function loadImage(file) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(img);
img.src = URL.createObjectURL(file);
});
}
Interactive Crop Window
The crop window is an absolutely positioned div overlaid on the image. Users can drag it to reposition and resize using corner handles:
class CropWindow {
constructor(container, image, preset) {
this.container = container;
this.image = image;
this.preset = preset;
this.x = 0;
this.y = 0;
this.w = 200;
this.h = 260;
this.initHandles();
this.initKeyboard();
}
initHandles() {
// 4 corner handles for resize
const handles = ['nw', 'ne', 'sw', 'se'];
handles.forEach(pos => {
const handle = document.createElement('div');
handle.className = `crop-handle ${pos}`;
handle.addEventListener('pointerdown', (e) => this.startResize(e, pos));
this.container.appendChild(handle);
});
}
initKeyboard() {
document.addEventListener('keydown', (e) => {
const step = e.shiftKey ? 10 : 2;
switch(e.key) {
case 'ArrowUp': this.y = Math.max(0, this.y - step); break;
case 'ArrowDown': this.y = Math.min(this.maxY, this.y + step); break;
case 'ArrowLeft': this.x = Math.max(0, this.x - step); break;
case 'ArrowRight': this.x = Math.min(this.maxX, this.x + step); break;
}
this.render();
});
}
}
Background Replacement
To change the background color, we scan edge pixels and replace similar colors:
function replaceBackground(canvas, targetColor, tolerance = 30) {
const ctx = canvas.getContext('2d');
const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = data.data;
// Sample background color from corners
const bg = sampleCornerPixels(pixels, canvas.width, canvas.height);
for (let i = 0; i < pixels.length; i += 4) {
const diff = colorDistance(pixels, bg, i);
if (diff < tolerance) {
pixels[i] = targetColor.r; // R
pixels[i + 1] = targetColor.g; // G
pixels[i + 2] = targetColor.b; // B
}
}
ctx.putImageData(data, 0, 0);
}
Print Layout Generation
For users who want to print at a photo lab, we need to arrange multiple copies on a 4×6 inch (1024×1536 px at 300 DPI) print sheet:
function generatePrintLayout(singlePhoto, preset, sheetWidth = 1500, sheetHeight = 1000) {
const canvas = document.createElement('canvas');
canvas.width = sheetWidth;
canvas.height = sheetHeight;
const ctx = canvas.getContext('2d');
// Fill with white background
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, sheetWidth, sheetHeight);
// Calculate how many photos fit
const cols = Math.floor(sheetWidth / (preset.outW + 20));
const rows = Math.floor(sheetHeight / (preset.outH + 20));
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const x = col * (preset.outW + 20) + 10;
const y = row * (preset.outH + 20) + 10;
ctx.drawImage(singlePhoto, x, y, preset.outW, preset.outH);
}
}
return canvas;
}
Full Tool
The ToolBox Image Passport Photo Maker (https://toolboximage.com/tools/passport/) implements all of this with 10 country presets, 4 background colors, arrow key fine positioning, and both single photo + print layout downloads. All client-side, zero uploads.
Try it at toolboximage.com/tools/passport/ (https://toolboximage.com/tools/passport/)
Enter fullscreen mode Exit fullscreen mode
답글 남기기