CSS及びドキュメントを修正

This commit is contained in:
ry.yamafuji 2025-04-03 20:52:16 +09:00
parent 09c26b0a4d
commit 1f67a85881
3 changed files with 140 additions and 99 deletions

View File

@ -74,3 +74,17 @@ RUN docker-php-ext-configure gd \
&& docker-php-ext-install gd && docker-php-ext-install gd
``` ```
### ImageMagickの対応
```Dockerfile
RUN apk add --no-cache \
imagemagick \
imagemagick-dev \
libheif \
RUN pecl install imagick \
&& docker-php-ext-enable imagick
```

View File

@ -1,8 +1,4 @@
<?php <?php
// ImageEdit.php
// 画像をリサイズして変換するPHPWEBアプリケーション
// 画像の最大サイズを指定し、JPEGまたはPNG形式に変換します
function processImage($file, $maxWidth = 400, $maxHeight = 400, $format = 'jpeg') function processImage($file, $maxWidth = 400, $maxHeight = 400, $format = 'jpeg')
{ {
$srcImage = @imagecreatefromstring(file_get_contents($file['tmp_name'])); $srcImage = @imagecreatefromstring(file_get_contents($file['tmp_name']));
@ -13,20 +9,17 @@ function processImage($file, $maxWidth = 400, $maxHeight = 400, $format = 'jpeg'
$origWidth = imagesx($srcImage); $origWidth = imagesx($srcImage);
$origHeight = imagesy($srcImage); $origHeight = imagesy($srcImage);
// サイズ比率を維持してリサイズ
$scale = min($maxWidth / $origWidth, $maxHeight / $origHeight, 1); $scale = min($maxWidth / $origWidth, $maxHeight / $origHeight, 1);
$newWidth = (int) ($origWidth * $scale); $newWidth = (int) ($origWidth * $scale);
$newHeight = (int) ($origHeight * $scale); $newHeight = (int) ($origHeight * $scale);
$dstImage = imagecreatetruecolor($newWidth, $newHeight); $dstImage = imagecreatetruecolor($newWidth, $newHeight);
// 透過画像PNGなどの背景を白にする
$white = imagecolorallocate($dstImage, 255, 255, 255); $white = imagecolorallocate($dstImage, 255, 255, 255);
imagefilledrectangle($dstImage, 0, 0, $newWidth, $newHeight, $white); imagefilledrectangle($dstImage, 0, 0, $newWidth, $newHeight, $white);
imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $newWidth, $newHeight, $origWidth, $origHeight); imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $newWidth, $newHeight, $origWidth, $origHeight);
// 出力
ob_start(); ob_start();
if ($format === 'png') { if ($format === 'png') {
imagepng($dstImage); imagepng($dstImage);
@ -45,42 +38,56 @@ function processImage($file, $maxWidth = 400, $maxHeight = 400, $format = 'jpeg'
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="ja">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>画像変換ツール</title> <title>画像変換ツール</title>
<!-- Bootstrap 5 CDN -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head> </head>
<body> <body>
<h1>画像変換ツール</h1> <div class="container py-5">
<form method="post" enctype="multipart/form-data"> <div class="card shadow">
<label>画像を選択:</label><br> <div class="card-body">
<input type="file" name="image" accept="image/*" required><br><br> <h1 class="card-title mb-4">画像変換ツール</h1>
<label>変換形式:</label> <form method="post" enctype="multipart/form-data" class="mb-4">
<select name="format"> <div class="mb-3">
<option value="jpeg">JPEG</option> <label for="image" class="form-label">画像を選択:</label>
<option value="png">PNG</option> <input type="file" name="image" id="image" accept="image/*" class="form-control" required>
</select><br><br> </div>
<label>最大サイズ(幅×高さ):</label> <div class="mb-3">
<input type="number" name="maxWidth" value="400" min="1"> × <label for="format" class="form-label">変換形式:</label>
<input type="number" name="maxHeight" value="400" min="1"><br><br> <select name="format" id="format" class="form-select">
<button type="submit">変換</button> <option value="jpeg">JPEG</option>
</form> <option value="png">PNG</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">最大サイズ(幅×高さ):</label>
<div class="d-flex gap-2">
<input type="number" name="maxWidth" value="400" min="1" class="form-control" placeholder="">
<span class="align-self-center">×</span>
<input type="number" name="maxHeight" value="400" min="1" class="form-control" placeholder="高さ">
</div>
</div>
<button type="submit" class="btn btn-primary">変換</button>
</form>
<?php <?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
$result = processImage($_FILES['image'], $_POST['maxWidth'] ?? 400, $_POST['maxHeight'] ?? 400, $_POST['format'] ?? 'jpeg'); $result = processImage($_FILES['image'], $_POST['maxWidth'] ?? 400, $_POST['maxHeight'] ?? 400, $_POST['format'] ?? 'jpeg');
if (isset($result['error'])) { if (isset($result['error'])) {
echo "<p style='color:red;'>エラー: {$result['error']}</p>"; echo "<div class='alert alert-danger'>エラー: {$result['error']}</div>";
} else { } else {
$base64 = base64_encode($result['data']); $base64 = base64_encode($result['data']);
echo "<h2>変換結果:</h2>"; echo "<h2 class='mt-4'>変換結果:</h2>";
echo "<img src='data:{$result['mime']};base64,{$base64}'><br>"; echo "<img src='data:{$result['mime']};base64,{$base64}' class='img-fluid mb-3' alt='変換画像'>";
echo "<a download='converted.{$result['mime']}' href='data:{$result['mime']};base64,{$base64}'>画像を保存</a>"; echo "<div><a download='converted.{$result['mime']}' class='btn btn-success' href='data:{$result['mime']};base64,{$base64}'>画像を保存</a></div>";
} }
} }
?> ?>
</div>
</div>
</div>
</body> </body>
</html> </html>

View File

@ -5,15 +5,12 @@ function canUseImagick(): bool {
function loadImage($filePath): array { function loadImage($filePath): array {
$mime = mime_content_type($filePath); $mime = mime_content_type($filePath);
// HEIC対応Imagick前提
if (strpos($mime, 'heic') !== false || strpos($mime, 'heif') !== false) { if (strpos($mime, 'heic') !== false || strpos($mime, 'heif') !== false) {
if (!canUseImagick()) return ['error' => 'HEIC形式にはImagickが必要です']; if (!canUseImagick()) return ['error' => 'HEIC形式にはImagickが必要です'];
$image = new Imagick($filePath); $image = new Imagick($filePath);
return ['image' => $image]; return ['image' => $image];
} }
// 通常の画像読み込みGD
$imgStr = file_get_contents($filePath); $imgStr = file_get_contents($filePath);
$gdImage = @imagecreatefromstring($imgStr); $gdImage = @imagecreatefromstring($imgStr);
if (!$gdImage) return ['error' => '画像の読み込みに失敗しました']; if (!$gdImage) return ['error' => '画像の読み込みに失敗しました'];
@ -62,76 +59,99 @@ function resizeAndCropGD($img, $maxW, $maxH, $cropX, $cropY, $cropW, $cropH): Gd
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="ja">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>画像変換ツール</title> <title>画像変換ツール</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head> </head>
<body> <body>
<h1>画像変換ツール</h1> <div class="container py-5">
<form method="post" enctype="multipart/form-data"> <div class="card shadow">
<label>画像を選択:</label><br> <div class="card-body">
<input type="file" name="image" accept="image/*" required><br><br> <h1 class="card-title mb-4">画像変換ツール</h1>
<form method="post" enctype="multipart/form-data">
<div class="mb-3">
<label class="form-label">画像を選択:</label>
<input type="file" name="image" accept="image/*" class="form-control" required>
</div>
<label>出力形式:</label> <div class="mb-3">
<select name="format"> <label class="form-label">出力形式:</label>
<option value="jpeg">JPEG</option> <select name="format" class="form-select">
<option value="png">PNG</option> <option value="jpeg">JPEG</option>
<option value="webp">WebP</option> <option value="png">PNG</option>
</select><br><br> <option value="webp">WebP</option>
</select>
</div>
<label>透過背景を保持PNG/WebPのみ:</label> <div class="mb-3 form-check">
<input type="checkbox" name="transparent"><br><br> <input type="checkbox" name="transparent" class="form-check-input" id="transparent">
<label class="form-check-label" for="transparent">透過背景を保持PNG/WebPのみ</label>
</div>
<label>最大サイズ(幅×高さ):</label> <div class="mb-3">
<input type="number" name="maxWidth" value="400" min="1"> × <label class="form-label">最大サイズ(幅×高さ):</label>
<input type="number" name="maxHeight" value="400" min="1"><br><br> <div class="d-flex gap-2">
<input type="number" name="maxWidth" value="400" min="1" class="form-control" placeholder="">
<span class="align-self-center">×</span>
<input type="number" name="maxHeight" value="400" min="1" class="form-control" placeholder="高さ">
</div>
</div>
<label>トリミングX, Y, , 高さ):</label><br> <div class="mb-3">
<input type="number" name="cropX" placeholder="X" min="0"> <label class="form-label">トリミングX, Y, , 高さ):</label>
<input type="number" name="cropY" placeholder="Y" min="0"> <div class="row g-2">
<input type="number" name="cropW" placeholder="" min="1"> <div class="col"><input type="number" name="cropX" placeholder="X" min="0" class="form-control"></div>
<input type="number" name="cropH" placeholder="高さ" min="1"><br><br> <div class="col"><input type="number" name="cropY" placeholder="Y" min="0" class="form-control"></div>
<div class="col"><input type="number" name="cropW" placeholder="" min="1" class="form-control"></div>
<div class="col"><input type="number" name="cropH" placeholder="高さ" min="1" class="form-control"></div>
</div>
</div>
<button type="submit">変換</button> <button type="submit" class="btn btn-primary">変換</button>
</form> </form>
<?php <?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
$tmp = $_FILES['image']['tmp_name']; $tmp = $_FILES['image']['tmp_name'];
$format = $_POST['format'] ?? 'jpeg'; $format = $_POST['format'] ?? 'jpeg';
$transparent = isset($_POST['transparent']); $transparent = isset($_POST['transparent']);
$maxW = (int)($_POST['maxWidth'] ?? 400); $maxW = (int)($_POST['maxWidth'] ?? 400);
$maxH = (int)($_POST['maxHeight'] ?? 400); $maxH = (int)($_POST['maxHeight'] ?? 400);
$cropX = (int)($_POST['cropX'] ?? 0); $cropX = (int)($_POST['cropX'] ?? 0);
$cropY = (int)($_POST['cropY'] ?? 0); $cropY = (int)($_POST['cropY'] ?? 0);
$cropW = isset($_POST['cropW']) ? (int)$_POST['cropW'] : 0; $cropW = isset($_POST['cropW']) ? (int)$_POST['cropW'] : 0;
$cropH = isset($_POST['cropH']) ? (int)$_POST['cropH'] : 0; $cropH = isset($_POST['cropH']) ? (int)$_POST['cropH'] : 0;
$result = loadImage($tmp); $result = loadImage($tmp);
if (isset($result['error'])) { if (isset($result['error'])) {
echo "<p style='color:red;'>エラー: {$result['error']}</p>"; echo "<div class='alert alert-danger mt-4'>エラー: {$result['error']}</div>";
} else { } else {
if (isset($result['gd'])) { if (isset($result['gd'])) {
$img = resizeAndCropGD($result['gd'], $maxW, $maxH, $cropX, $cropY, $cropW, $cropH); $img = resizeAndCropGD($result['gd'], $maxW, $maxH, $cropX, $cropY, $cropW, $cropH);
$out = gdToBlob($img, $format, $transparent); $out = gdToBlob($img, $format, $transparent);
} elseif (isset($result['image'])) { } elseif (isset($result['image'])) {
$image = $result['image']; $image = $result['image'];
if ($cropW && $cropH) { if ($cropW && $cropH) {
$image->cropImage($cropW, $cropH, $cropX, $cropY); $image->cropImage($cropW, $cropH, $cropX, $cropY);
}
$image->scaleImage($maxW, $maxH, true);
$out = imagickToBlob($image, $format);
}
$base64 = base64_encode($out['data']);
$mime = $out['mime'];
echo "<h2 class='mt-4'>変換結果:</h2>";
echo "<img src='data:$mime;base64,$base64' class='img-fluid mb-3' alt='変換画像'>";
echo "<div><a download='converted.$format' class='btn btn-success' href='data:$mime;base64,$base64'>画像を保存</a></div>";
}
} }
$image->scaleImage($maxW, $maxH, true); ?>
$out = imagickToBlob($image, $format); </div>
} </div>
</div>
$base64 = base64_encode($out['data']);
$mime = $out['mime'];
echo "<h2>変換結果:</h2>";
echo "<img src='data:$mime;base64,$base64'><br><br>";
echo "<a download='converted.$format' href='data:$mime;base64,$base64'>画像を保存</a>";
}
}
?>
</body> </body>
</html> </html>