0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

画像を枠内に収める前に、壊れる条件を決める

0
Posted at

画像をページ内に配置するとき、縦横比だけを見て「収まった」と判定すると、横か縦の片方がはみ出すケースを見落とします。正常系より先に、受け付けない入力と、結果が守るべき条件を決めます。

この記事の関数は矩形の数値を計算するだけです。画像のデコード、向きのメタデータの解釈、PDF の生成は行いません。座標の原点や描画 API の扱いは、実際の組み込み先で別途確認してください。

先に失敗させる

ページの幅と高さから左右・上下それぞれの余白を引いたとき、使える領域が空になってはいけません。例えば幅 600、高さ 800 のページで余白を 300 にすると、横方向の残りは 0 です。この入力は例外にします。負の余白や無限大も受け付けません。

画像・ページの各寸法には、有限の数値で 1 以上 1,000,000 以下という範囲を設けます。文字列の "1200" は数値に見えても拒否します。この範囲はサンプルのために選んだ予算であり、一般的な規格でも、本番コードの入力検証と同一だという主張でもありません。

別の数値ハーネスでは、560 × 760 へ引き伸ばす計算が元の 2:1 の比率を壊すことと、倍率の上限を外すと 100 × 50 の小画像を拡大してしまうことも比較しました。縦横比と非拡大の方針が必要な理由を示す負例です。以下の短いコードには、この2つの比較は含めていません。

正常値で守る四つの条件

幅と高さには同じ倍率を掛けます。倍率は、使用可能な幅に対する比、使用可能な高さに対する比、そして 1 のうち最小値です。したがって、どちらの辺も余白の内側に収まり、元の縦横比を維持します。浮動小数点の比較では誤差を考慮してください。

残った空間は左右と上下でそれぞれ等分します。また倍率を 1 以下に制限するため、小さい画像はこの計算では拡大されません。ここでは元画像の幅・高さをページ座標と同じ数値対応で扱い、元の1単位を高々ページの1単位に対応させます。物理的な DPI 換算ではありません。

export function containRect(iw, ih, pw, ph, margin) {
  if (![iw, ih, pw, ph].every(v =>
    typeof v === "number" && Number.isFinite(v) && v >= 1 && v <= 1e6
  )) throw new RangeError("dimensions must be numbers from 1 to 1000000");
  if (typeof margin !== "number" || !Number.isFinite(margin) ||
      margin < 0 || 2 * margin >= Math.min(pw, ph)) {
    throw new RangeError("margin leaves no usable rectangle");
  }
  const scale = Math.min(1, (pw - 2 * margin) / iw, (ph - 2 * margin) / ih);
  const width = iw * scale;
  const height = ih * scale;
  return { x: (pw - width) / 2, y: (ph - height) / 2, width, height };
}

次の検査を同じファイルの関数の後に置き、example.mjs として node example.mjs で実行できます。成功時は何も表示されません。入力の拒否も同時に確かめます。

import assert from "node:assert/strict";

assert.deepEqual(containRect(1200, 600, 600, 800, 20),
  { x: 20, y: 260, width: 560, height: 280 });
assert.deepEqual(containRect(100, 50, 600, 800, 20),
  { x: 250, y: 375, width: 100, height: 50 });
for (const args of [
  ["1200", 600, 600, 800, 20],
  [1200, 600, 600, 800, 300],
  [0, 600, 600, 800, 20],
]) assert.throws(() => containRect(...args), RangeError);

最初の例では使用可能領域が 560 × 760 です。横幅が制約となり、結果は 560 × 280、位置は (20, 260) になります。次の 100 × 50 は拡大せず、(250, 375) に配置されます。余白の内側にあることは横と縦の両方で確認します。

2026-10-28 に Codex エージェントが Darwin 上の Node v22.23.4 で数値のみを検査しました。正常9件、異常12件、本番ヘルパーとの有効な描画寸法の比較9件、負のコントロール2件です。この比較は実装全体や入力方針の一致を証明しません。エンコード済み画像や PDF の試験も含みません。

開示:この記事は Yan Wang / LensUp 向けに GPT-6.1 Terra が下書きしました。Codex エージェントが 2026-10-28 に正式ソースと公開ページの文言を確認し、上記の数値検査を実行しました。新たな HEIC・画像から PDF への変換、ブラウザー・電話・プリンターでの出力、または人間のオーナーによるテストは行っていません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?