From 282a890ebcfa19a292aa9206c62baa2b8db148d7 Mon Sep 17 00:00:00 2001 From: Kresna <13603341+slaveofcode@users.noreply.github.com> Date: Fri, 14 Aug 2026 07:45:07 +0700 Subject: [PATCH 1/3] feat(image): add passport framing guide to Pas Foto Overlay crown/chin lines and a centered head oval on the preview so users can frame to standard ID/passport head proportions, with a show/hide toggle. Guides are preview-only and never appear in the exported PDF. --- src/islands/image/PasFoto.tsx | 38 +++++++++++++++++++++++++++- src/tools/image/pas-foto.lib.test.ts | 20 ++++++++++++++- src/tools/image/pas-foto.lib.ts | 29 +++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/islands/image/PasFoto.tsx b/src/islands/image/PasFoto.tsx index 2d3e841..0cfaf4f 100644 --- a/src/islands/image/PasFoto.tsx +++ b/src/islands/image/PasFoto.tsx @@ -13,6 +13,7 @@ import { cmToPt, photoPx, sheetLayout, + headGuideBox, type PhotoSize, type Sheet, } from '@/tools/image/pas-foto.lib'; @@ -20,6 +21,8 @@ import type { Lang } from '@/i18n/config'; const GAP_CM = 0.2; const MARGIN_CM = 0.3; +// Framing-guide geometry in a 0–100 viewBox (drawn only on the preview). +const GUIDE = headGuideBox(100, 100); const BG_PRESETS = [ { color: '#e02424', label: 'Merah' }, { color: '#1e50e0', label: 'Biru' }, @@ -45,6 +48,8 @@ const TR: Record string; reset: string; + guide: string; + guideHint: string; }> = { en: { intro: 'Make a print-ready ID photo (pas foto): remove the background, pick a color and size, and download a PDF that tiles copies onto a photo sheet — ready to print. Everything runs in your browser.', @@ -65,6 +70,8 @@ const TR: Record `${n} copies per sheet`, reset: 'Clear', + guide: 'Show framing guide', + guideHint: 'Align the crown and chin to the dashed lines for a passport-style crop.', }, id: { intro: 'Buat pas foto siap cetak: hapus latar belakang, pilih warna dan ukuran, lalu unduh PDF berisi banyak salinan dalam satu lembar foto — siap dicetak. Semuanya berjalan di browser Anda.', @@ -85,6 +92,8 @@ const TR: Record `${n} salinan per lembar`, reset: 'Bersihkan', + guide: 'Tampilkan panduan bingkai', + guideHint: 'Sejajarkan puncak kepala dan dagu ke garis putus-putus untuk hasil gaya paspor.', }, }; @@ -98,6 +107,7 @@ export default function PasFoto({ lang = 'en' }: { lang?: Lang }) { const [sheet, setSheet] = useState(SHEETS[0]); // 4R const [zoom, setZoom] = useState(1); const [offsetY, setOffsetY] = useState(0); // -0.5 .. 0.5 of frame height + const [showGuide, setShowGuide] = useState(true); const [busy, setBusy] = useState(false); const [stage, setStage] = useState(''); const [percent, setPercent] = useState(0); @@ -276,8 +286,34 @@ export default function PasFoto({ lang = 'en' }: { lang?: Lang }) { {/* Preview */}
- +
+ + {showGuide && ( + + {/* Dark halo behind, light line on top → visible on any background. */} + + + + + + + + + + + + )} +
+ + {showGuide &&

{t.guideHint}

}