Free Job Alert: Latest Jobs | Admit Cards | Results

Passport Photo Maker

Vacancy Update Photo Studio

Passport Photo Maker

Upload a photo, frame it to 35 × 45 mm, arrange a print sheet, and export a ready-to-print PNG or PDF.

35×45mm
ISSUED
LOCALLY
" ); win.document.close(); } function downloadPDF(){ if(!image){ setStatusMsg("Upload a photo first.", true); return; } if(!window.jspdf){ setStatusMsg("PDF library still loading — try again in a moment.", true); return; } var orientation = paper.pdf[0] > paper.pdf[1] ? "landscape" : "portrait"; var pdf=new window.jspdf.jsPDF({orientation:orientation, unit:"in", format:paper.pdf}); pdf.addImage(finalSheet.current.toDataURL("image/png"), "PNG", 0, 0, paper.pdf[0], paper.pdf[1], undefined, "FAST"); pdf.save(fname+"-"+settings.paperId+".pdf"); } return e("div", {className:"grid"}, // LEFT PANEL e("div", {style:{display:"grid", gap:"16px"}}, e("div", {className:"panel"}, e("div", {className:"panel-head"}, e("h2", null, e("span",{className:"idx"},"1"), "Upload"), e("span", {className:"tag"}, image ? "Ready" : "None") ), e("div", {className:"panel-body"}, e("label", { className:"upload"+(dragOver?" active":""), onDragEnter:function(ev){ev.preventDefault();setDragOver(true);}, onDragOver:function(ev){ev.preventDefault();setDragOver(true);}, onDragLeave:function(ev){ev.preventDefault();setDragOver(false);}, onDrop:function(ev){ev.preventDefault();setDragOver(false);handleFile(ev.dataTransfer.files&&ev.dataTransfer.files[0]);} }, e("strong", null, image ? "Change photo" : "Choose or drop photo"), e("span", null, "JPG, PNG or WebP · under 15MB"), e("input",{ref:fileInput, type:"file", accept:"image/jpeg,image/png,image/webp", hidden:true, onChange:function(ev){handleFile(ev.target.files && ev.target.files[0]);}}) ), e("p", {className:"steps-note"}, "Use a sharp, front-facing photo with even lighting and a plain background for best results.") ) ), e("div", {className:"panel"}, e("div", {className:"panel-head"}, e("h2", null, e("span",{className:"idx"},"2"), "Sheet Setup"), e("span", {className:"tag"}, paper.label) ), e("div", {className:"panel-body"}, e("div", {className:"field"}, e("label", null, "Paper size"), e("div", {className:"opt-grid"}, Object.keys(PAPER).map(function(key){ return e(Option, { key:key, label:PAPER[key].label, hint: key==="4r" ? "Standard print" : key==="5r" ? "Larger print" : "Full page", active: settings.paperId===key, onClick: function(){ update("paperId", key); } }); }) ) ), e("div", {className:"row2"}, e(Field, {label:"Orientation"}, e("select", {value:settings.orientation, onChange:function(ev){update("orientation", ev.target.value);}}, e("option",{value:"landscape"},"Landscape"), e("option",{value:"portrait"},"Portrait") ) ), e(Field, {label:"Photo count"}, e("select", {value:settings.count, onChange:function(ev){update("count", Number(ev.target.value));}}, options.map(function(n){ return e("option",{key:n,value:n}, n+" photos"); }) ) ) ) ) ), e("div", {className:"panel"}, e("div", {className:"panel-head"}, e("h2", null, e("span",{className:"idx"},"3"), "Label (optional)") ), e("div", {className:"panel-body"}, e("div", {className:"row2"}, e(Field, {label:"Name"}, e("input", {type:"text", maxLength:40, placeholder:"ENTER YOUR NAME", value:settings.name, style:{textTransform:"uppercase"}, onChange:function(ev){update("name", ev.target.value);}}) ), e(Field, {label:"Date"}, e("input", {type:"text", inputMode:"numeric", placeholder:"DD-MM-YYYY", maxLength:10, value:settings.date, onChange:handleDateInput}) ) ) ) ) ), // RIGHT PANEL e("div", {style:{display:"grid", gap:"16px"}}, e("div", {className:"panel"}, e("div", {className:"panel-head"}, e("h2", null, e("span",{className:"idx"},"4"), "Frame & Adjust"), e("button", {className:"btn ghost", onClick:resetEdits}, "Reset") ), e("div", {className:"panel-body"}, e("div", {className:"stage"}, e("div", {className:"photo-box"}, e("canvas", { ref:photoPreview, onPointerDown:startDrag, onPointerMove:moveDrag, onPointerUp:endDrag, onPointerCancel:endDrag, title:"Drag to move, scroll to zoom" }) ), e("div", null, e("div", {className:"row2"}, e(Field, {label:"Background"}, e("div", {className:"btn-row"}, !bgRemoved ? e("button", {className:"btn", disabled:!image||busy, onClick:removeBackground}, busy ? "Removing…" : "Remove Background") : e("button", {className:"btn ghost", disabled:busy, onClick:restoreBackground}, "Restore Original") ) ), e(Field, {label:"Background color"}, e("input", {type:"color", value:settings.bg, disabled:!bgRemoved, onChange:function(ev){update("bg", ev.target.value);}, style:{width:"100%", height:"38px", padding:"2px", border:"1px solid var(--line)", borderRadius:"3px", background:"#fff", cursor: bgRemoved ? "pointer" : "not-allowed"}}) ) ), bgRemoved ? e("div", {className:"row2"}, e(Field, {label:"Edge Trim (removes leftover outline)"}, e("input",{type:"range", min:-20, max:60, step:2, value:settings.edgeTrim, onChange:function(ev){update("edgeTrim", Number(ev.target.value));}}) ), e("div", {style:{fontSize:"11.5px", color:"var(--ink-soft)", alignSelf:"end", paddingBottom:"9px"}}, "Higher = trims more background along the outline. Lower = keeps more hair/fine detail." ) ) : null, e("div", {className:"row3"}, e(Field, {label:"Zoom"}, e("input",{type:"range", min:0.6, max:3, step:0.01, value:settings.zoom, onChange:function(ev){update("zoom", Number(ev.target.value));}}) ), e(Field, {label:"Vertical"}, e("input",{type:"range", min:-260, max:260, step:1, value:settings.offsetY, onChange:function(ev){update("offsetY", Number(ev.target.value));}}) ), e(StepperField, { label: "Photo Rotation (°)", min: -180, max: 180, step: 1, value: settings.rotation, onDec: function(){ update("rotation", clamp(settings.rotation - 1, -180, 180)); }, onInc: function(){ update("rotation", clamp(settings.rotation + 1, -180, 180)); }, onChange: function(val){ update("rotation", clamp(val, -180, 180)); } }) ), e("div", {className:"row3"}, e(Field, {label:"Brightness"}, e("input",{type:"range", min:70, max:140, step:1, value:settings.brightness, onChange:function(ev){update("brightness", Number(ev.target.value));}}) ), e(Field, {label:"Contrast"}, e("input",{type:"range", min:70, max:150, step:1, value:settings.contrast, onChange:function(ev){update("contrast", Number(ev.target.value));}}) ), e(Field, {label:"Saturation"}, e("input",{type:"range", min:60, max:150, step:1, value:settings.saturation, onChange:function(ev){update("saturation", Number(ev.target.value));}}) ) ), e("div", {className:"meta-strip"}, e("span", {className:"meta-chip"}, "35 × 45 mm"), e("span", {className:"meta-chip"}, paper.label + " sheet"), e("span", {className:"meta-chip"}, settings.count + " photos") ) ) ), e("div", {className:"status"+(isErr?" err":"")}, status) ) ), e("div", {className:"panel"}, e("div", {className:"panel-head"}, e("h2", null, e("span",{className:"idx"},"5"), "Print Sheet Preview"), e("span", {className:"tag"}, "PNG · PDF") ), e("div", {className:"panel-body"}, e("div", {className:"sheet-box"}, e("canvas", {ref:sheetPreview}) ), e("div", {className:"btn-row", style:{marginTop:"14px"}}, e("button", {className:"btn", disabled:!image, onClick:function(){downloadPNG("photo");}}, "Download Photo PNG (\u00d7"+settings.count+")"), e("button", {className:"btn", disabled:!image, onClick:function(){downloadPNG("sheet");}}, "Download Sheet PNG"), e("button", {className:"btn ghost", disabled:!image, onClick:downloadPDF}, "Download PDF"), e("button", {className:"btn ghost", disabled:!image, onClick:printPhoto}, "Print Photo") ) ) ) ) ); } ReactDOM.createRoot(document.getElementById("app-root")).render(e(App)); } if(document.readyState==="loading"){ document.addEventListener("DOMContentLoaded", boot); } else { boot(); } })();
Scroll to Top