☰
PHP印章类实战:GD与Imagick生成电子印章并合成到图片和PDF
2026/10/12 19:10:17 网站建设 项目流程

简介:这是一份面向PHP开发者的电子印章生成类代码资源,适合需要在中后台系统、合同签署或OA场景中动态生成圆形印章的开发者参考。资源包共3个文件,包含2个PHP源码文件与1个TTF字体文件,压缩包约2.24MB,其中PHP文件封装了印章类的核心逻辑与调用示例,TTF字体用于印章文字的渲染输出。该类围绕圆形印章绘制展开,涵盖五角星绘制、文字旋转、字体加载、颜色与角度等属性配置,以及创建印章、绘制星形、旋转文本、保存与显示图像等方法,并附有实例化调用的完整演示。目前已有848人学习下载,可帮助读者快速理解GD库绘图与面向对象封装的结合方式,掌握印章尺寸、透明度与图像质量等细节处理思路,直接复用到实际项目中。

1. 印章类到底在做什么:从一张报销单说起

财务同事甩过来一张报销单,PDF 上盖了红章,但章是歪的、颜色发灰、边缘还有锯齿。业务方要求「电子章要跟真章一样」,于是你打开搜索引擎敲下「php 印章类」——这就是多数人接触这个需求的起点。所谓印章类,本质是用 PHP 在图片或 PDF 上合成一枚电子印章:生成圆形或椭圆形的章面、把单位名称沿弧线排布、叠加五角星、再以合适的透明度和旋转角度盖到目标文档上。它解决的是合同、证明、票据在流转环节的「盖章」动作自动化,适合做 OA、合同管理、电子档案的 PHP 开发者。别指望它替代合规的电子签章,它更像一个图像合成工具,把「盖章」这个视觉动作程序化。

2. 用 GD 还是 Imagick:先把技术选型定下来

2.1 两种扩展的能力边界

PHP 处理图像有两条路:GD 和 Imagick。做印章类,选型直接决定后面能走多远。

GD 是 PHP 默认捆绑的扩展,函数式 API,上手快,画圆、画线、写字、旋转、透明叠加都能做。缺点是字体渲染依赖 FreeType,弧形文字要自己算坐标,抗锯齿效果一般,输出 PNG 时透明通道处理需要手动开imagealphablending和imagesavealpha,稍不注意章底就是黑块。

Imagick 基于 ImageMagick,面向对象,支持 SVG、PDF 直接读写,弧形文字可以用ImagickDraw配合annotate逐字旋转,抗锯齿和色彩管理更专业。代价是安装门槛高,扩展和 ImageMagick 版本要匹配,部分虚拟主机不提供。

我的建议:如果只是给 PNG/JPG 盖章,GD 够用且部署简单;如果要直接盖到 PDF、要矢量输出、要精细的弧形排版,上 Imagick。下面两条路线都会给。

2.2 环境准备与最小验证

先确认扩展状态,别写完代码才发现没装。

# 查看 GD 是否启用及版本 php -m | grep -i gd php -r "print_r(gd_info());" # 查看 Imagick 是否启用 php -m | grep -i imagick php -r "echo Imagick::getVersion()['versionString'];"

如果 GD 没开,在 php.ini 里去掉extension=gd的注释;Imagick 则通常需要extension=imagick.so,并确保系统装了 ImageMagick 运行库。验证通过后再往下走。

2.3 用 GD 画一枚基础圆章

先做最核心的章面:红色圆环、内圈细线、中间五角星。

<?php // 创建 400x400 真彩色画布,留出边距 $size = 400; $img = imagecreatetruecolor($size, $size); // 开启透明通道,否则 PNG 背景是黑的 imagealphablending($img, false); imagesavealpha($img, true); $transparent = imagecolorallocatealpha($img, 0, 0, 0, 127); imagefill($img, 0, 0, $transparent); imagealphablending($img, true); // 印章红,RGB 约 200,16,20,可按需微调 $red = imagecolorallocate($img, 200, 16, 20); // 外圆环:中心 200,200,半径 180,线宽 6 imagesetthickness($img, 6); imageellipse($img, 200, 200, 360, 360, $red); // 内圈细线,半径 150,线宽 2 imagesetthickness($img, 2); imageellipse($img, 200, 200, 300, 300, $red); // 五角星:用多边形顶点近似 $star = []; $cx = 200; $cy = 200; $outer = 70; $inner = 28; for ($i = 0; $i < 10; $i++) { $r = ($i % 2 === 0) ? $outer : $inner; // 从 -90 度开始,保证一个角朝上 $angle = deg2rad(-90 + $i * 36); $star[] = $cx + $r * cos($angle); $star[] = $cy + $r * sin($angle); } imagefilledpolygon($img, $star, $red); // 输出 PNG header('Content-Type: image/png'); imagepng($img); imagedestroy($img);

逻辑说明:imagecreatetruecolor建的是真彩色画布,透明必须靠imagecolorallocatealpha分配一个 alpha=127 的颜色再填充,同时imagesavealpha要在填充前设为 true。五角星用 10 个顶点交替取外半径和内半径,角度步长 36 度,起始 -90 度让尖角朝上。参数上,imagesetthickness影响所有后续线条,画完外圈记得改回细线再画内圈,否则内圈也会变粗。

2.4 弧形文字的坐标计算

印章的灵魂是沿圆弧排布的单位名称。GD 没有现成的弧形文字函数,得逐字旋转。

<?php // 假设 $img、$red 已由上一步创建 $text = '某某技术服务有限公司'; $fontFile = __DIR__ . '/simsun.ttc'; // 需自备中文字体 $fontSize = 22; $radius = 130; // 文字基线所在圆半径 $startAngle = -150; // 起始角度,度 $totalChars = mb_strlen($text, 'UTF-8'); // 每个字占的角度,按字数均分,留出顶部缺口 $step = 300 / max($totalChars - 1, 1); for ($i = 0; $i < $totalChars; $i++) { $char = mb_substr($text, $i, 1, 'UTF-8'); $angle = $startAngle + $i * $step; $rad = deg2rad($angle); // 计算该字中心坐标 $x = 200 + $radius * cos($rad); $y = 200 + $radius * sin($rad); // 用临时画布旋转单字,避免直接旋转导致位置漂移 $tmp = imagecreatetruecolor(60, 60); imagealphablending($tmp, false); imagesavealpha($tmp, true); $t = imagecolorallocatealpha($tmp, 0, 0, 0, 127); imagefill($tmp, 0, 0, $t); imagealphablending($tmp, true); $tr = imagecolorallocate($tmp, 200, 16, 20); imagettftext($tmp, $fontSize, 0, 8, 40, $tr, $fontFile, $char); // 旋转角度:文字底部朝圆心,所以旋转角 = 角度 + 90 $rotated = imagerotate($tmp, -($angle + 90), imagecolorallocatealpha($tmp, 0, 0, 0, 127)); imagealphablending($rotated, true); imagesavealpha($rotated, true); // 把旋转后的字贴到主画布,偏移半个字宽高使其居中 imagecopy($img, $rotated, $x - 30, $y - 30, 0, 0, 60, 60); imagedestroy($tmp); imagedestroy($rotated); }

逻辑说明:核心是「先画正字,再旋转,最后贴到圆弧坐标」。旋转角取-(angle+90),是因为 GD 的imagerotate逆时针为正,而文字默认水平,要让字底朝向圆心需要补 90 度。参数上,$radius决定文字离中心的距离,$startAngle和$step共同决定文字覆盖的弧段,一般顶部留 60 度缺口放五角星。字体文件必须用支持中文的 TTF/TTC,路径写绝对路径最稳。

3. 把章盖到图片和 PDF 上:合成与定位

3.1 透明叠加与旋转角度

章画好了,盖的时候要控制透明度和轻微旋转,太正反而假。

<?php // $seal 是上一步生成的印章 PNG 资源,$target 是待盖章图片 $target = imagecreatefromjpeg(__DIR__ . '/contract.jpg'); // 印章缩放到合适尺寸,比如目标宽度的 25% $sealW = imagesx($seal); $sealH = imagesy($seal); $newW = (int)(imagesx($target) * 0.25); $newH = (int)($sealH * $newW / $sealW); $sealScaled = imagescale($seal, $newW, $newH); // 随机旋转 -15 到 15 度,模拟手工盖章 $angle = mt_rand(-150, 150) / 10; $sealRot = imagerotate($sealScaled, $angle, imagecolorallocatealpha($sealScaled, 0, 0, 0, 127)); imagealphablending($sealRot, true); imagesavealpha($sealRot, true); // 定位:右下角往内缩 80 像素 $dstX = imagesx($target) - imagesx($sealRot) - 80; $dstY = imagesy($target) - imagesy($sealRot) - 80; // 叠加时开启 alpha 混合,让章半透明 imagecopy($target, $sealRot, $dstX, $dstY, 0, 0, imagesx($sealRot), imagesy($sealRot)); imagejpeg($target, __DIR__ . '/contract_sealed.jpg', 92);

逻辑说明:imagescale按目标宽度比例缩放,保证不同分辨率下章的大小协调。旋转用imagerotate,背景色必须传透明色,否则旋转后四角是黑的。imagecopy前主画布要处于 alphablending 开启状态,这样章的半透明像素才会和目标融合。参数上,缩放比例 0.25、边距 80、旋转 ±15 度都是经验值,合同类文档章一般占页面宽度 20% 到 30%。

3.2 用 Imagick 直接盖到 PDF

如果目标是 PDF,GD 要先转图再转回,画质损失大,Imagick 一步到位。

<?php $pdf = new Imagick(__DIR__ . '/contract.pdf'); $pdf->setImageFormat('png'); // 先栅格化便于定位,也可保持 pdf $page = $pdf->getImage(); $seal = new Imagick(__DIR__ . '/seal.png'); $seal->resizeImage(200, 0, Imagick::FILTER_LANCZOS, 1); $seal->rotateImage(new ImagickPixel('transparent'), 8); $seal->setImageOpacity(0.85); // 合成到页面右下角 $page->compositeImage($seal, Imagick::COMPOSITE_OVER, 900, 1200); $page->setImageFormat('pdf'); $page->writeImage(__DIR__ . '/contract_sealed.pdf');

逻辑说明:resizeImage第二参数传 0 表示按宽度等比缩放,FILTER_LANCZOS是质量较好的重采样滤镜。rotateImage的背景色用transparent保证透明。setImageOpacity控制整体不透明度,0.85 比全不透明更自然。compositeImage的坐标是左上角起点,需要根据页面尺寸算,A4 在 150dpi 下约 1240x1754,所以 900,1200 落在右下区域。

3.3 参数速查表

参数常用值作用
章面直径页面宽 20%~30%太小看不清,太大压正文
旋转角-15°~15°完全水平显得假
不透明度0.8~0.9太低像水印,太高盖住文字
红色 RGB200,16,20偏暗红更接近印泥
边距60~100px避免贴边被裁切
字体大小章直径 8%~12%保证弧形文字可读

4. 避坑与排查:那些让章变黑块的细节

4.1 透明背景变成黑块

现象:生成的 PNG 章贴到图上,四角是黑色方块。原因:创建画布后没有imagesavealpha($img, true),或者填充透明色时 alphablending 还开着导致透明色被混合。解决:先imagealphablending($img, false),再imagefill透明色,最后imagesavealpha($img, true),顺序不能乱。

4.2 中文显示成方框或乱码

现象:imagettftext输出一堆方块。原因:字体文件不支持中文,或字符串编码不是 UTF-8。解决:换用支持中文的字体(如宋体、黑体),并确认 PHP 文件本身是 UTF-8 无 BOM,mb_substr要显式传'UTF-8'。

4.3 弧形文字位置整体偏移

现象:文字不在圆弧上,挤成一团或散开。原因:$step按字数均分角度时没考虑字符实际宽度,或者旋转后贴图偏移量写死 30 导致错位。解决:把每个字画在固定尺寸临时画布的中心,贴图时用「目标坐标减去半个画布尺寸」计算偏移,不要用固定值。

4.4 Imagick 读取 PDF 报错

现象:new Imagick('x.pdf')抛异常。原因:ImageMagick 的 PDF 委托被策略禁用,或缺少 Ghostscript。解决:检查policy.xml是否禁用了 PDF,安装 Ghostscript,必要时改用 GD 先栅格化再处理。

4.5 盖章后图片体积暴涨

现象:一张 200KB 的 JPG 盖完章变 2MB。原因:GD 输出 PNG 默认无损,或 JPEG 质量参数给太高。解决:输出 JPG 时质量设 85~92,输出 PNG 时用imagepng($img, null, 9)提高压缩级别,或直接输出 JPG。

5. 进阶:批量盖章与效果自检

批量场景下,把印章生成和盖章拆成两个函数,印章只生成一次缓存起来,避免每张图重画。

<?php function makeSeal(string $text, string $font): GdImage { // 生成一次,返回资源,调用方自行缓存 // ... 省略绘制细节,同第 2 章 } function stampOn(string $src, string $dst, GdImage $seal, float $scale = 0.25): void { $img = imagecreatefromstring(file_get_contents($src)); $w = (int)(imagesx($img) * $scale); $h = (int)(imagesy($seal) * $w / imagesx($seal)); $s = imagescale($seal, $w, $h); $s = imagerotate($s, mt_rand(-120, 120) / 10, imagecolorallocatealpha($s, 0, 0, 0, 127)); imagealphablending($s, true); imagesavealpha($s, true); imagecopy($img, $s, imagesx($img) - $w - 60, imagesy($img) - $h - 60, 0, 0, $w, $h); imagejpeg($img, $dst, 90); imagedestroy($img); imagedestroy($s); } $seal = makeSeal('某某技术服务有限公司', __DIR__ . '/simsun.ttc'); foreach (glob(__DIR__ . '/batch/*.jpg') as $file) { stampOn($file, str_replace('.jpg', '_sealed.jpg', $file), $seal); }

逻辑说明:makeSeal只跑一次,stampOn里每次重新缩放和旋转,保证每张图的章角度不同。参数$scale控制章占图宽比例,批量时建议统一。注意imagerotate每次返回新资源,旧的$s在旋转后应销毁,这里为简洁省略了中间销毁,生产环境要补上避免内存堆积。

效果自检我一般做三件事:一是把盖章结果缩到 50% 看章是否还清晰,二是用取色器确认红色没有被目标图背景污染,三是把章单独输出到白底和黑底各看一次,透明边缘有没有白边或黑边。这三步能挡掉八成视觉翻车。

最后说个血泪经验:别在循环里反复imagecreatefrompng读同一个印章文件,IO 和内存都吃不消,缓存成资源或至少缓存文件内容。我早期做批量合同时没注意,一千张图跑下来内存直接爆掉,后来把印章预生成一次就再没出过事。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询