首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP GD 圖片文字水印:中文支援、透明度、字體載入與 Intervention Image 一鍵替代

     編輯:PHP 相關知識

原生 GD 實作 (完整支援中文 TTF、Alpha 透明度、自動換行)

<?php
/**
 * 為圖片添加文字水印
 * @param string $srcPath 來源圖路徑
 * @param string $dstPath 目標儲存路徑
 * @param string $text 水印文字 (支援多行 \n)
 * @param array $options 選項
 * @return bool
 */
function addTextWatermark(string $srcPath, string $dstPath, string $text, array $options = []): bool {
    $opts = array_merge([
        'font_path' => __DIR__ . '/fonts/NotoSansTC-Regular.otf', // 必須提供支援中文的 TTF/OTF
        'font_size' => 24,
        'font_color' => [255, 255, 255, 128], // RGBA, 128=50% 透明
        'position' => 'bottom-right', // top-left, top-right, bottom-left, bottom-right, center, tile
        'margin' => 10,
        'angle' => 0,
        'shadow' => true,
        'shadow_color' => [0, 0, 0, 100],
        'shadow_offset' => [2, 2],
        'line_height' => 1.4,
    ], $options);

    if (!is_file($srcPath) || !is_file($opts['font_path'])) return false;

    $info = getimagesize($srcPath);
    if (!$info) return false;
    [$w, $h, $mime] = $info;

    $srcImg = match ($mime) {
        IMAGETYPE_JPEG => imagecreatefromjpeg($srcPath),
        IMAGETYPE_PNG  => imagecreatefrompng($srcPath),
        IMAGETYPE_GIF  => imagecreatefromgif($srcPath),
        IMAGETYPE_WEBP => imagecreatefromwebp($srcPath),
        default => null,
    };
    if (!$srcImg) return false;

    // 計算文字區塊大小
    $lines = explode("\n", $text);
    $box = [0, 0];
    foreach ($lines as $line) {
        $b = imagettfbbox($opts['font_size'], $opts['angle'], $opts['font_path'], $line);
        $box[0] = max($box[0], $b[2] - $b[0]); // 寬
        $box[1] += ($b[1] - $b[7]) * $opts['line_height']; // 高累加
    }
    $textW = $box[0];
    $textH = $box[1];

    // 計算起始座標
    [$x, $y] = match ($opts['position']) {
        'top-left' => [$opts['margin'], $opts['margin'] + $textH],
        'top-right' => [$w - $textW - $opts['margin'], $opts['margin'] + $textH],
        'bottom-left' => [$opts['margin'], $h - $opts['margin']],
        'bottom-right' => [$w - $textW - $opts['margin'], $h - $opts['margin']],
        'center' => [($w - $textW) / 2, ($h + $textH) / 2],
        'tile' => null, // 特殊處理
        default => [$opts['margin'], $h - $opts['margin']],
    };

    // 分配顏色
    $color = imagecolorallocatealpha($srcImg, ...$opts['font_color']);
    $shadowColor = $opts['shadow'] ? imagecolorallocatealpha($srcImg, ...$opts['shadow_color']) : null;

    // 繪製 (支援 tile 平鋪)
    if ($opts['position'] === 'tile') {
        for ($ty = -$textH; $ty < $h + $textH; $ty += $textH * 2) {
            for ($tx = -$textW; $tx < $w + $textW; $tx += $textW * 2) {
                foreach ($lines as $i => $line) {
                    $ly = $ty + $i * ($textH / count($lines)) * $opts['line_height'];
                    if ($shadowColor) imagettftext($srcImg, $opts['font_size'], $opts['angle'], $tx + $opts['shadow_offset'][0], $ly + $opts['shadow_offset'][1], $shadowColor, $opts['font_path'], $line);
                    imagettftext($srcImg, $opts['font_size'], $opts['angle'], $tx, $ly, $color, $opts['font_path'], $line);
                }
            }
        }
    } else {
        foreach ($lines as $i => $line) {
            $ly = $y + $i * ($textH / count($lines)) * $opts['line_height'];
            if ($shadowColor) imagettftext($srcImg, $opts['font_size'], $opts['angle'], $x + $opts['shadow_offset'][0], $ly + $opts['shadow_offset'][1], $shadowColor, $opts['font_path'], $line);
            imagettftext($srcImg, $opts['font_size'], $opts['angle'], $x, $ly, $color, $opts['font_path'], $line);
        }
    }

    // 輸出 (保持原格式)
    $ok = match ($mime) {
        IMAGETYPE_JPEG => imagejpeg($srcImg, $dstPath, 90),
        IMAGETYPE_PNG  => imagepng($srcImg, $dstPath, 9),
        IMAGETYPE_GIF  => imagegif($srcImg, $dstPath),
        IMAGETYPE_WEBP => imagewebp($srcImg, $dstPath, 90),
        default => false,
    };
    imagedestroy($srcImg);
    return $ok;
}
?>

關鍵細節避坑

問題解決
中文顯示方框/亂碼**必須指定支援中文的字體檔** (Noto Sans TC, Source Han Sans, 微軟正黑體)。`imagettftext` 不支援字體回退。
字體路徑錯誤使用絕對路徑,`__DIR__ . '/fonts/...'`,檢查 `is_file()`。
透明度不生效PNG/GIF/WebP 需 `imagealphablending($img, true); imagesavealpha($img, true);`;JPEG 不支援透明度 (會變黑底),水印色建議不透明。
文字座標基準點`imagettftext` 的 `$y` 是 **baseline (基線)**,非文字頂部。需加上字體上升高度 (從 `imagettfbbox` 計算)。
記憶體耗盡大圖處理前 `ini_set('memory_limit', '512M')`,或改用 Imagick。

Intervention Image 一鍵水印 (推薦)

composer require intervention/image

<?php
use Intervention\Image\ImageManagerStatic as Image;

Image::make('photo.jpg')
    ->text('© MySite 2024', 800, 600, function ($font) {
        $font->file(__DIR__ . '/fonts/NotoSansTC-Regular.otf');
        $font->size(24);
        $font->color('#ffffff80'); // 8位十六進位含 Alpha
        $font->align('right');
        $font->valign('bottom');
    })
    ->save('watermarked.jpg');

// 圖片水印 (Logo)
Image::make('photo.jpg')
    ->insert('logo.png', 'bottom-right', 10, 10)
    ->save('watermarked2.jpg');
?>

總結

GD 原生水印需手動處理**字體載入、座標計算、Baseline 校正、透明度、多行、平鋪**,極易出錯。**生產環境請直接使用 Intervention Image**,一行鏈式呼叫解決所有細節,支援 GD/Imagick 雙驅動無縫切換。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved