猫の手ツール

Engineering Memo

登録した写真寸法をピクセルへ変換する
キャンバス演算の裏側

「ブラウザだけでビザ写真を作る」ために解決すべき最大の課題は、物理的なミリメートル規定とデジタル解像度の正確な変換、そしてモバイル端末のメモリ制約への対応でした。

このツールの入力から出力まで

現行コードの確認日:

入力
写真を選び、対象の写真規格と切取り位置を調整します。
処理
選んだ出力比率のCanvasに写真を収めて保存します。
出力
Web 用は選択プリセットの切り抜き1枚を JPEG(品質指定 0.9)で出力し、印刷用は 1051×1500px の白いシートへ配置して品質指定 0.95 で保存します。

まず確認する点:発行国や申請種別による条件を自動判定しません。ツールで作る寸法と提出先が求める頭部・背景の条件を照合します。

実装の確認箇所:src/pages/visa/photo/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。

仕組みを読み進めるときは、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)、File APIとObject URL(選んだ画像の読み込みとBlob URLの解放)が参考になります。

1. 物理サイズ(mm)を 300dpi 相当のピクセルへ変換する演算

証明写真、特にビザ写真は「横35mm × 縦45mm」といった厳密な物理サイズが求められます。しかし、Web上での canvas.toDataURL 等の処理はピクセルベースです。

本ツールでは、1051px を 89mm で割った約 11.81px/mm(約 300dpi 相当)を変換係数として使います。これは印刷用シートの寸法設計で、印刷機の設定を変える処理ではありません。特に「L判印刷モード」では、出力用紙の物理サイズから逆算したキャンバス解像度を動的に生成し、選んだプリセットの寸法に合わせた写真を中央揃えで配置するアルゴリズムを組んでいます。

プリセットの mm 値から L判シート上の px 値を求める例です。実際の印刷時には 89×127mm 全体に合わせる必要があります。

function visaPixels(mmWidth, mmHeight) {
  const pxPerMm = 1051 / 89;
  return { width: mmWidth * pxPerMm, height: mmHeight * pxPerMm };
}

2. メモリ負荷を抑える「動的リサイズハック」

近年のスマートフォンのカメラ性能向上により、1枚で10MBを超えるような高解像度画像が増えています。これをそのまま Canvas に読み込んで Cropper.js を動かすと、メモリ不足でブラウザがクラッシュ(落ちる)する原因になります。

本実装では、ファイルを読み込んだ直後に「最大解像度閾値(MAX_SIZE)」を超えているかをチェックし、超えている場合はオフスクリーン・キャンバスを用いて、縦横比を維持したまま縮小処理を行ってからエディタに渡しています。

3. HEIC形式のフロントエンド・リアルタイム変換

このツールでは、HEICを選んだ場合はブラウザ内で別の画像形式に変換してから編集します。

ユーザーに「事前にJPGに変換してください」という手間を与えないよう、heic2any ライブラリを動的インポートし、クライアントサイドで JPEG への変換を行っています。これを async/await で制御することで、シームレスなUXを提供しています。

出力の仕様と確認する点

入力は長辺 3000px 以下に縮小します。Web 用は選択プリセットの切り抜き1枚を JPEG(品質指定 0.9)で出力し、印刷用は 1051×1500px の白いシートへ配置して品質指定 0.95 で保存します。

印刷用の配置は米国プリセットが縦2枚、中国・EU プリセットが2列×2行、ブラジルプリセットが1枚です。国名だけで全てのビザ・申請経路の要件が同じになるわけではなく、プリセットはツールに登録した寸法として扱ってください。

顔ガイドは目安で、自動検出・背景や表情の審査・容量上限への自動圧縮はありません。提出先の最新の条件に合う寸法かを確認し、印刷用は実寸を測ってから使用します。受理を保証するものではありません。