mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
refactor(passport-photo): rebuild UI for online applications with file size control
This commit is contained in:
@@ -33,6 +33,7 @@ const generateSettingsSchema = z.object({
|
||||
documentType: z.string().default("passport"),
|
||||
bgColor: z.string().default("#FFFFFF"),
|
||||
printLayout: z.string().default("none"),
|
||||
maxFileSizeKb: z.number().default(0),
|
||||
adjustX: z.number().default(0),
|
||||
adjustY: z.number().default(0),
|
||||
landmarks: landmarksSchema,
|
||||
@@ -287,6 +288,7 @@ export function registerPassportPhoto(app: FastifyInstance) {
|
||||
documentType,
|
||||
bgColor,
|
||||
printLayout,
|
||||
maxFileSizeKb,
|
||||
adjustX,
|
||||
adjustY,
|
||||
landmarks: rawLandmarks,
|
||||
@@ -369,7 +371,7 @@ export function registerPassportPhoto(app: FastifyInstance) {
|
||||
const targetHeightPx = Math.round((docSpec.height / MM_PER_INCH) * docSpec.dpi);
|
||||
|
||||
// Extract crop region and resize to target dimensions
|
||||
const cropped = await sharp(bgLayer)
|
||||
let cropped = await sharp(bgLayer)
|
||||
.extract({
|
||||
left: cropLeft,
|
||||
top: cropTop,
|
||||
@@ -380,6 +382,25 @@ export function registerPassportPhoto(app: FastifyInstance) {
|
||||
.jpeg({ quality: 95 })
|
||||
.toBuffer();
|
||||
|
||||
// Compress to fit within max file size if specified
|
||||
if (maxFileSizeKb > 0) {
|
||||
const targetBytes = maxFileSizeKb * 1024;
|
||||
let quality = 90;
|
||||
while (cropped.length > targetBytes && quality > 10) {
|
||||
quality -= 5;
|
||||
cropped = await sharp(bgLayer)
|
||||
.extract({
|
||||
left: cropLeft,
|
||||
top: cropTop,
|
||||
width: cropW,
|
||||
height: cropH,
|
||||
})
|
||||
.resize(targetWidthPx, targetHeightPx, { fit: "fill" })
|
||||
.jpeg({ quality })
|
||||
.toBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
// Save output
|
||||
const outputFilename = `${filename.replace(/\.[^.]+$/, "")}_passport.jpg`;
|
||||
const outputPath = join(workspacePath, "output", outputFilename);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -274,6 +274,11 @@ const PassportPhotoSettings = lazy(() =>
|
||||
default: m.PassportPhotoSettings,
|
||||
})),
|
||||
);
|
||||
const PassportPhotoPreview = lazy(() =>
|
||||
import("@/components/tools/passport-photo-settings").then((m) => ({
|
||||
default: m.PassportPhotoPreview,
|
||||
})),
|
||||
);
|
||||
const RedEyeRemovalSettings = lazy(() =>
|
||||
import("@/components/tools/red-eye-removal-settings").then((m) => ({
|
||||
default: m.RedEyeRemovalSettings,
|
||||
@@ -423,7 +428,14 @@ export const toolRegistry = new Map<string, ToolRegistryEntry>([
|
||||
],
|
||||
["colorize", { displayMode: "before-after", Settings: ColorizeSettings }],
|
||||
["noise-removal", { displayMode: "before-after", Settings: NoiseRemovalSettings }],
|
||||
["passport-photo", { displayMode: "no-comparison", Settings: PassportPhotoSettings }],
|
||||
[
|
||||
"passport-photo",
|
||||
{
|
||||
displayMode: "custom-results",
|
||||
Settings: PassportPhotoSettings,
|
||||
ResultsPanel: PassportPhotoPreview,
|
||||
},
|
||||
],
|
||||
["red-eye-removal", { displayMode: "before-after", Settings: RedEyeRemovalSettings }],
|
||||
["restore-photo", { displayMode: "before-after", Settings: RestorePhotoSettings }],
|
||||
]);
|
||||
|
||||
@@ -670,6 +670,126 @@ export const PASSPORT_SPECS: PassportSpec[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "IE",
|
||||
name: "Ireland",
|
||||
flag: "\u{1F1EE}\u{1F1EA}",
|
||||
region: "europe",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Irish Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "IT",
|
||||
name: "Italy",
|
||||
flag: "\u{1F1EE}\u{1F1F9}",
|
||||
region: "europe",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Italian Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "ES",
|
||||
name: "Spain",
|
||||
flag: "\u{1F1EA}\u{1F1F8}",
|
||||
region: "europe",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Spanish Passport",
|
||||
width: 26,
|
||||
height: 32,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "NL",
|
||||
name: "Netherlands",
|
||||
flag: "\u{1F1F3}\u{1F1F1}",
|
||||
region: "europe",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Dutch Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "PL",
|
||||
name: "Poland",
|
||||
flag: "\u{1F1F5}\u{1F1F1}",
|
||||
region: "europe",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Polish Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "SE",
|
||||
name: "Sweden",
|
||||
flag: "\u{1F1F8}\u{1F1EA}",
|
||||
region: "europe",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Swedish Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Asia
|
||||
{
|
||||
code: "IN",
|
||||
@@ -811,6 +931,106 @@ export const PASSPORT_SPECS: PassportSpec[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "SG",
|
||||
name: "Singapore",
|
||||
flag: "\u{1F1F8}\u{1F1EC}",
|
||||
region: "asia",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Singapore Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "MY",
|
||||
name: "Malaysia",
|
||||
flag: "\u{1F1F2}\u{1F1FE}",
|
||||
region: "asia",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Malaysian Passport",
|
||||
width: 35,
|
||||
height: 50,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "VN",
|
||||
name: "Vietnam",
|
||||
flag: "\u{1F1FB}\u{1F1F3}",
|
||||
region: "asia",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Vietnamese Passport",
|
||||
width: 40,
|
||||
height: 60,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "PK",
|
||||
name: "Pakistan",
|
||||
flag: "\u{1F1F5}\u{1F1F0}",
|
||||
region: "asia",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Pakistani Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF", "#BFDBFE"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "BD",
|
||||
name: "Bangladesh",
|
||||
flag: "\u{1F1E7}\u{1F1E9}",
|
||||
region: "asia",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Bangladeshi Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Oceania
|
||||
{
|
||||
code: "AU",
|
||||
@@ -832,6 +1052,26 @@ export const PASSPORT_SPECS: PassportSpec[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "NZ",
|
||||
name: "New Zealand",
|
||||
flag: "\u{1F1F3}\u{1F1FF}",
|
||||
region: "oceania",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "New Zealand Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Middle East
|
||||
{
|
||||
code: "SA",
|
||||
@@ -853,6 +1093,26 @@ export const PASSPORT_SPECS: PassportSpec[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "AE",
|
||||
name: "United Arab Emirates",
|
||||
flag: "\u{1F1E6}\u{1F1EA}",
|
||||
region: "middle-east",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "UAE Passport",
|
||||
width: 43,
|
||||
height: 55,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Africa
|
||||
{
|
||||
code: "NG",
|
||||
@@ -874,6 +1134,46 @@ export const PASSPORT_SPECS: PassportSpec[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "EG",
|
||||
name: "Egypt",
|
||||
flag: "\u{1F1EA}\u{1F1EC}",
|
||||
region: "africa",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "Egyptian Passport",
|
||||
width: 40,
|
||||
height: 60,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "ZA",
|
||||
name: "South Africa",
|
||||
flag: "\u{1F1FF}\u{1F1E6}",
|
||||
region: "africa",
|
||||
documents: [
|
||||
{
|
||||
type: "passport",
|
||||
label: "South African Passport",
|
||||
width: 35,
|
||||
height: 45,
|
||||
dpi: 300,
|
||||
headHeightMin: 0.7,
|
||||
headHeightMax: 0.8,
|
||||
eyeLineFromBottom: 0.63,
|
||||
bgColor: "#FFFFFF",
|
||||
bgColors: ["#FFFFFF"],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export interface PrintLayout {
|
||||
|
||||
Reference in New Issue
Block a user