简介:这是一份面向PHP开发者的电子印章生成类代码资源,适合需要在中后台系统、合同签署或OA流程中实现印章绘制的开发者参考。资源围绕自定义Seal类展开,涵盖圆形印章绘制、五角星坐标计算与旋转、文本角度调整、字体文件加载等核心环节,并给出印章颜色、文字内容、星角、字号、直径等属性与创建、绘制、保存、显示等方法的设计思路。压缩包共3个文件,包含2个PHP源码文件与1个TTF字体文件,整体约2.24MB,其中PHP文件承载印章类的核心逻辑与调用示例,TTF字体用于印章文字的渲染输出。目前已有848人学习下载,可帮助读者快速理解GD库或Imagick在印章场景下的实际用法,掌握从属性配置到图像落盘的完整实现路径,并在此基础上扩展透明度、尺寸与图像质量等细节。
1. 从一张报销单说起:PHP 印章类到底能干什么
上个月帮某公司行政组看一个内部报销系统,财务在后台点“审批通过”之后,需要把审批人的电子签章盖到 PDF 上,再归档。他们原来的做法是让美工提前把章 P 到模板里,每换一个审批人就得重做一版,一个月下来光改图就耗掉两三天。后来换成一个纯 PHP 的印章生成类,上传一张透明底的印章图,代码按坐标把章压到指定位置,整个流程从半天缩到几秒。
这就是 PHP 印章类要解决的问题:用服务端脚本动态生成或合成印章图像,不依赖设计软件,不依赖前端截图,输出即所得。它适合三类人——做 OA、合同、报销、审批流的后端;需要批量给文档盖章的运维或数据岗;以及想在自己项目里加一个“电子章”功能但不想引入重型图形库的独立开发者。核心能力通常就三块:画圆章(外圈、五角星、弧形文字)、合成已有印章图到目标图、控制透明度和旋转角度。下面按“怎么用 → 参数怎么调 → 坑在哪”的顺序拆开讲。
2. 印章类的底层依赖与选型:GD 还是 Imagick
2.1 两种扩展的能力边界
PHP 处理图像绕不开两个扩展:GD 和 Imagick。印章类如果只做“把现成印章图贴到文档上”,GD 足够;如果要“从零画一个标准圆章”,GD 的弧形文字支持很弱,得靠imagettftext逐字旋转拼,Imagick 则直接有annotateImage配合setTextAngle,省事很多。
我一般这样判断:项目里已经有 Imagick 且服务器允许装,就优先 Imagick;如果是虚拟主机、只开了 GD,那就用 GD 方案,但弧形文字要接受一定程度的锯齿和间距误差。下面这张表是实际选型时对比的几个点。
| 维度 | GD | Imagick |
|---|---|---|
| 弧形文字 | 需手动逐字旋转 | 原生支持角度 |
| 透明通道 | imagealphablending要手动开 | 默认保留 |
| 旋转合成 | imagerotate有黑边风险 | rotateImage更干净 |
| 内存占用 | 较低 | 较高 |
| 部署难度 | 多数环境自带 | 需额外装扩展 |
2.2 环境确认与最小可用代码
不管选哪个,先确认扩展是否可用。命令行执行:
php -m | grep -E 'gd|imagick'如果只输出gd,说明 Imagick 没装,后面的代码就走 GD 分支。接着用一段最小代码验证画布和透明通道是否正常:
<?php // 检查 GD 是否支持 PNG 透明 $im = imagecreatetruecolor(200, 200); imagesavealpha($im, true); // 保留 alpha 通道 $trans = imagecolorallocatealpha($im, 0, 0, 0, 127); // 全透明背景 imagefill($im, 0, 0, $trans); $red = imagecolorallocate($im, 200, 0, 0); imageellipse($im, 100, 100, 180, 180, $red); // 画外圈 imagepng($im, 'test_seal.png'); imagedestroy($im); echo "ok";这段代码做了三件事:建 200×200 真彩画布、把背景填成全透明、画一个红色圆环。关键参数是imagecolorallocatealpha的第四个值 127,代表完全透明,0 代表完全不透明。如果输出图打开是黑底而不是透明底,说明imagesavealpha没生效,常见原因是 PNG 保存前又调用了imagefill覆盖。
2.3 合成已有印章图的推荐路径
大多数业务不是从零画章,而是拿一张已经做好的透明 PNG 印章,贴到目标图上。GD 的合成顺序必须是“先开混色、再复制、再关混色”,顺序错了透明区域会变黑。
<?php $target = imagecreatefromjpeg('contract.jpg'); // 目标文档 $seal = imagecreatefrompng('seal.png'); // 透明印章 imagealphablending($target, true); // 允许叠加 imagesavealpha($target, true); $w = imagesx($seal); $h = imagesy($seal); // 参数:目标图、印章图、目标x、目标y、源x、源y、宽、高 imagecopy($target, $seal, 300, 500, 0, 0, $w, $h); imagejpeg($target, 'signed.jpg', 90); // 质量 90 imagedestroy($target); imagedestroy($seal);imagecopy的 300 和 500 是印章左上角在文档上的坐标,这个值通常由前端传过来或按页面比例算。质量参数 90 是 JPEG 压缩比,低于 80 印章边缘会出现明显色块。如果印章要旋转,GD 得先imagerotate再合成,旋转后的图尺寸会变,坐标要重新算,这是后面避坑章要重点说的。
3. 从零画一个圆章:弧形文字与五角星的参数拆解
3.1 圆章的结构拆解
一个标准圆章由四部分组成:外圈圆环、内圈细线(可选)、上方弧形单位名、下方弧形编号、中心五角星。用 GD 画,外圈和五角星简单,难的是弧形文字。思路是:把每个字符单独旋转一个角度,再贴到圆周对应位置上。
先定义几个核心参数,这些值直接决定章好不好看:
| 参数 | 含义 | 常用值 |
|---|---|---|
| canvas | 画布边长 | 300~400 px |
| radius | 外圈半径 | canvas/2 - 10 |
| fontSize | 字号 | 16~22 |
| startAngle | 文字起始角 | -60°~-80° |
| charSpacing | 字间距补偿 | 2~5 px |
3.2 弧形文字的实现代码
<?php $size = 300; $im = imagecreatetruecolor($size, $size); imagesavealpha($im, true); imagefill($im, 0, 0, imagecolorallocatealpha($im, 0, 0, 0, 127)); $red = imagecolorallocate($im, 200, 0, 0); $cx = $cy = $size / 2; $radius = $size / 2 - 15; imageellipse($im, $cx, $cy, $radius * 2, $radius * 2, $red); $text = '某某单位专用章'; $font = __DIR__ . '/simhei.ttf'; // 必须用支持中文的字体 $fontSize = 18; $angleStart = -70; // 起始角度,负值偏左上 $step = 360 / (mb_strlen($text) + 6); // 每个字占的角度 for ($i = 0; $i < mb_strlen($text); $i++) { $char = mb_substr($text, $i, 1, 'UTF-8'); $angle = $angleStart + $i * $step; $rad = deg2rad($angle); // 计算字符中心坐标 $x = $cx + $radius * cos($rad) - $fontSize / 2; $y = $cy + $radius * sin($rad) - $fontSize / 2; // 字符自身旋转,使其朝向圆心 imagettftext($im, $fontSize, $angle + 90, $x, $y, $red, $font, $char); } imagepng($im, 'round_seal.png');逻辑说明:deg2rad把角度转弧度,cos/sin算出字符在圆周上的位置。$angle + 90是让每个字垂直于半径方向,看起来才是“立”在圆上的。$step里的+6是给文字留出上下空白,不然字会挤满整圈。字体文件必须用支持中文的 TTF,用默认字体中文会变成方块,这是新手最常翻车的地方。
3.3 五角星与内圈线
五角星用imagefilledpolygon画,需要算出 10 个顶点(5 个外点、5 个内点)。外点半径取radius * 0.35,内点取radius * 0.15,交替连接。
<?php $points = []; $outerR = $radius * 0.35; $innerR = $radius * 0.15; for ($i = 0; $i < 10; $i++) { $r = ($i % 2 == 0) ? $outerR : $innerR; $a = deg2rad(-90 + $i * 36); // 从正上方开始 $points[] = $cx + $r * cos($a); $points[] = $cy + $r * sin($a); } imagefilledpolygon($im, $points, $red);-90是让第一个顶点朝正上方,36是 360/10。如果五角星歪了,检查这个起始角。内圈细线用imageellipse把半径缩小到radius * 0.75再画一次即可,线宽 GD 不支持直接设,只能靠多次画偏移 1px 的圆来模拟。
4. 避坑与排查:印章合成里最容易翻车的五件事
4.1 透明背景变黑底
现象:合成后印章的透明区域变成黑色方块。原因:目标图或印章图在imagecopy前没有正确开启 alpha 混合,或者保存时用了imagejpeg而 JPEG 不支持透明。解决:合成前对目标图调imagealphablending($target, true),印章图用imagecreatefrompng读入,最终输出如果必须 JPEG,透明区会被填成白色或黑色,这是格式限制,要透明就输出 PNG。
4.2 弧形文字方向反了
现象:字是倒着的,或者朝外。原因:imagettftext的角度参数和坐标计算不匹配。解决:记住角度是逆时针为正,字符旋转角用$angle + 90,如果发现整体反了,把$angleStart从 -70 改成 250 试试,本质是起始象限选错了。
4.3 旋转印章后坐标偏移
现象:印章旋转 30° 后,盖的位置和预期差了一大截。原因:imagerotate返回的新图尺寸变了,原来的左上角坐标不再对应。解决:旋转后重新计算新图的宽高,用(新宽 - 原宽)/2补偿偏移,或者干脆先旋转再按中心点定位。
4.4 中文字体不生效
现象:输出全是方框或问号。原因:imagettftext用的字体文件不支持中文,或者路径写错。解决:用绝对路径__DIR__ . '/simhei.ttf',并确认字体文件确实存在。虚拟主机上常见问题是open_basedir限制,导致字体读不到,看 PHP 错误日志里的 warning 就能定位。
4.5 大批量合成内存爆掉
现象:循环处理 200 张图时脚本被 kill。原因:每轮imagecreatefromxxx后没有imagedestroy,内存持续累积。解决:每轮结束显式销毁所有图像资源,或者用gc_collect_cycles()辅助回收。常见做法是把批量任务拆成每 50 张一批,用队列跑。
5. 进阶技巧:把印章类封装成可配置的合成器
5.1 用配置数组驱动不同章型
实际项目里不会只有一种章。我一般把印章参数抽成一个数组,让调用方只传“盖在哪、盖什么章、转多少度”,内部根据类型走不同分支。
<?php class SealMaker { private $config; public function __construct(array $config) { $this->config = $config; } public function stamp($targetPath, $sealPath, $x, $y, $rotate = 0) { $target = imagecreatefromstring(file_get_contents($targetPath)); $seal = imagecreatefrompng($sealPath); if ($rotate != 0) { // 旋转并保留透明 $seal = imagerotate($seal, $rotate, imagecolorallocatealpha($seal, 0, 0, 0, 127)); imagesavealpha($seal, true); } imagealphablending($target, true); imagesavealpha($target, true); imagecopy($target, $seal, $x, $y, 0, 0, imagesx($seal), imagesy($seal)); $out = $this->config['output'] ?? 'png'; if ($out === 'png') { imagepng($target, $targetPath); } else { imagejpeg($target, $targetPath, $this->config['quality'] ?? 90); } imagedestroy($target); imagedestroy($seal); } }调用时:
<?php $maker = new SealMaker(['output' => 'png']); $maker->stamp('contract.jpg', 'seal.png', 300, 500, 15);这个封装的好处是旋转、输出格式、质量都收在配置里,业务代码只关心坐标和角度。imagerotate的第三个参数是旋转后空白区的填充色,这里用全透明,否则旋转出来的四个角会是黑色。
5.2 验证合成结果是否正确的三个检查点
盖完章不能只看“有没有图”,要验证三件事。第一,透明区是否真的透明——用getimagesize看通道数,PNG 应该是 4 通道。第二,印章位置是否在预期区域——可以写个简单脚本把目标区域裁出来单独看。第三,批量任务是否每张都成功——记录每张的输出路径和文件大小,大小为 0 或异常小的直接标记失败。
# 检查 PNG 是否带 alpha 通道 php -r "$i=getimagesize('signed.png'); echo $i['channels'];" # 输出 4 表示带透明通道,3 表示没有5.3 一个我踩过的坑
早期做批量合同时,我图省事在循环里直接imagecreatefromjpeg读原图,结果 300 张跑完内存涨到 2G 被系统杀掉。后来改成每处理完一张就imagedestroy,并且把输出从 JPEG 换成 PNG 再转 JPEG,内存稳定在 200M 以内。从那以后我每次写图像批处理,都强制在循环末尾加一行销毁,并且先用 10 张跑一遍看内存曲线。希望这些能帮到你,少走一段弯路。
本文还有配套的精品资源,点击获取