资讯动态

PHP GD库实战:服务端生成透明圆形印章的完整方案

发布时间:2026/10/19 8:55:19 来源:尧图企业网站定制
简介面向PHP开发者的电子印章生成类资源核心封装了圆形印章绘制、五角星旋转、文字环绕与角度控制等常用能力适合需要在Web应用中快速生成带公章效果的图片或学习GD库图像处理的中级开发者。压缩包共3个文件包含两个PHP类文件与一个楷体字体文件ttf约2.24MB。其中类文件负责图像生成逻辑字体文件用于指定印章上的中文显示效果结构精简可直接参考或嵌入项目。资源描述中详细说明了从自定义类属性颜色、字体、角度、直径到方法创建圆形印章、绘制五角星、旋转文本、保存与显示的完整实现思路并附有可运行的调用示例读者可借此理解GD库绘图、真彩色字体渲染与图像输出等关键环节。目前已吸引849人浏览学习适合希望快速掌握PHP动态生成图片印章的开发者下载参考。1. PHP印章类项目办公系统里最刚需的一个小功能之前做OA审批流程时遇到一个很实际的需求合同审批走到最后一步要在电子版上盖公章。选了一圈第三方签章服务费用高且对接麻烦最后找到这个PHP印章类开源项目来改造。它本质上是一套基于GD库的PHP类库配置公司名称、印章用途、字体、颜色等参数后在服务端直接生成一张带透明背景的圆形PNG印章能无缝叠加到合同签字页、审批单据或者报销单上。适合两类人一是PHP后端开发想在自己业务系统里嵌入一键盖章能力二是正在做电子签、合同归档、OA流程印章自动化的开发者。它不依赖前端Canvas也不依赖任何第三方API纯服务端生成拿过来就能跑改起来也很快。2. 架构与目录一个PHP类怎么从配置走到PNG图片2.1 目录结构与职责划分这个项目的目录划分很直观核心代码全部收敛在src目录里字体、示例、输出目录独立放职责边界比较清楚。我当时拿到源码第一件事就是画目录树把每个文件对应到“配置-渲染-输出”这条链路上后面改起来就快很多。seal/ ├── src/ │ ├── Seal.php // 门面类对外唯一入口 │ ├── SealConfig.php // 配置对象所有可调参数 │ └── Renderer/ │ ├── SealRenderer.php // 渲染器接口定义render契约 │ └── CircleSealRenderer.php // 圆形印章渲染实现 ├── fonts/ │ └── SourceHanSansCN-Medium.ttf // 中文字体避免系统字体缺失 ├── examples/ │ └── demo.php // 可直接运行的示例脚本 └── output/ └── seal.png // 渲染结果输出目录Seal.php是门面类只暴露一个render方法内部按type字段选择具体渲染器SealConfig.php是参数持有者保存公司名、印章类型、尺寸、颜色、字体路径等配置Renderer目录下是渲染器实现目前主要是CircleSealRenderer也就是圆形印章。fonts目录专门放了一个思源黑体TTF原因是GD库渲染中文必须依赖本机字体文件不带字体的话换个服务器就乱码这个细节直接决定项目能不能跨机器跑。2.2 调用链与类协作方式整个项目的调用链非常短从业务代码到图片生成只经过三层这也是我比较喜欢它的地方。业务侧不用关心GD库的坐标计算和透明通道处理只需准备一个SealConfig对象丢给Seal即可。use Seal\Seal; use Seal\SealConfig; $config new SealConfig(); $config-company 某科技有限公司; // 环绕文字 $config-usage 合同专用章; // 中心下方文字 $config-type SealConfig::SEAL_CIRCLE; $config-width 300; // 画布宽度像素 $config-height 300; // 画布高度像素 $config-borderWidth 3; // 外边框粗细 $config-fontSize 24; // 正文字号 $config-centerFontSize 20; // 中心文字字号 $config-color #C7000B; // 印章红 $seal new Seal($config); $result $seal-render()-save(/tmp/seal.png);这里有一个我后来认为很关键的设计render方法返回的是一个SealResult对象而不是直接写文件这样既可以把图片保存成文件也可以转成Base64字符串走网络传输甚至转成Data URI直接塞给前端img标签。如果没有这层封装业务侧就得自己用ob_start配合imagepng去接缓冲区代码会散得到处都是。render内部的执行顺序是固定的校验配置合法性根据type实例化渲染器渲染器调用GD库画图最后包装成SealResult返回。每一步的异常都会在Seal层统一捕获并转为RuntimeException抛出方便业务侧做统一异常处理。2.3 配置参数说明我把经常动的配置参数整理成了一张表方便对照修改。需要注意GD库默认用像素作为单位所以所有尺寸类参数都是整数颜色统一用十六进制字符串内部再转成GD的RGBA数组。参数名类型默认值说明companystring无印章外圈公司全称必须设置usagestring空印章中心下方的用途文字如合同专用章、财务专用章typeintSEAL_CIRCLE印章类型预留扩展点width / heightint300画布尺寸方形印章通常相等borderWidthint3外圆边框像素宽度fontSizeint24外圈文字字号centerFontSizeint20中心用途文字字号colorstring#C7000B印章主色默认是标准印章红fontPathstring内置字体中文字体路径默认指向fonts目录参数命名还是偏直白的基本看到名字就知道是什么。唯一要注意的是fontPath如果你的生产环境对字体有合规要求可以直接替换成自己授权过的字体文件不需要改任何代码。3. 核心实现拆解从GD画布到圆形印章的关键代码3.1 初始化透明画布背景必须是“没有”不是“白色”印章图片最重要的一点是背景透明否则盖到合同上会压掉底下的正文。刚接手这个项目时我在这一步栽过跟头用imagecreatetruecolor创建的画布默认是黑色不透明的直接在上面画图最终输出PNG时黑底全露出来了。正确做法是创建画布后立刻关闭alpha混合、设置保存alpha通道再用透明色填充整个画布。// 创建真彩色画布 $image imagecreatetruecolor($width, $height); // 关键步骤先关闭混合保证填充的透明色不会被合成 imagealphablending($image, false); // 告诉GD输出PNG时保留alpha通道 imagesavealpha($image, true); // 分配一个完全透明的颜色并填充全图 $transparent imagecolorallocatealpha($image, 0, 0, 0, 127); imagefill($image, 0, 0, $transparent); // 后续绘制字符和图形时重新开启混合模式 imagealphablending($image, true);这里面的逻辑顺序不能乱。imagesavealpha必须在imagefill之前设置否则填充的透明色会被当作不透明颜色处理。imagecolorallocatealpha最后一个参数是透明度取值范围0到127127表示完全透明。填充完成后重新打开alphablending是为了让后续画的文字和圆框边缘有抗锯齿效果不然线条边缘会出现明显的像素颗粒。3.2 圆弧文字排列坐标、角度、基线三件事一次搞清楚圆形印章最核心的算法就是把公司名按圆弧排列在外圈。最初我用最笨的方式把公司名拆成单个字符逐个用imagettftext画结果文字方向全乱了有的字横躺、有的字倒立。后来理清了imagettftext的angle参数规则才明白它在GD里是逆时针为正的度数而印章文字需要每个字都保持“顶部朝圆心外”的姿态。// 将公司名拆成单字符数组 $chars mb_str_split($config-company); $total count($chars); // 圆弧半径外圆半径减去字符高度的一半 $charRadius $borderRadius - $config-fontSize * 0.7; // 从正上方开始顺时针均匀分布 for ($i 0; $i $total; $i) { // 当前字符所在角度度数制-90度为正上方 $angleDeg -90 (360 / $total) * ($i 0.5); $angleRad deg2rad($angleDeg); // 字符中心的坐标注意GD的Y轴向下 $x $cx $charRadius * cos($angleRad); $y $cy $charRadius * sin($angleRad); // 文字跟随圆弧方向旋转 $charAngle -($angleDeg 90); // 用imagettfbbox测出字符实际宽高做居中补偿 $bbox imagettfbbox($config-fontSize, $charAngle, $fontPath, $chars[$i]); $width abs($bbox[2] - $bbox[0]); $height abs($bbox[7] - $bbox[1]); // imagettftext的x是基线起点y是基线位置需要补偿到字符中心 $xPos $x - $width / 2; $yPos $y - $height / 2; imagettftext( $image, $config-fontSize, $charAngle, $xPos, $yPos, $textColor, $fontPath, $chars[$i] ); }这段代码里最值得琢磨的是三个角度。第一个是极坐标角度angleDeg它决定字符“摆在圆环的哪个位置”第二个是旋转角度charAngle它决定字符“朝哪个方向站立”第三个是imagettfbbox返回的边界框它决定“补偿几个像素才能让字符中心正好落在极坐标点上”。三个角度配合好文字才会既均匀分布又全部正立可读。如果公司名很长比如超过15个字可以按字符数量反推合适的fontSize公式是fontSize 圆周长 / 字符数 * 0.8。我通常会在渲染前加一道自适应逻辑避免大公司名被挤出圆外。3.3 五角星、内外边框与防伪线的绘制圆形印章中间的五角星是识别度的关键。GD没有直接画五角星的函数需要用imagefilledpolygon按十个顶点交替使用外半径和内半径来填充。内半径与外半径的比例用0.382比较接近标准五角星形状。绘制顺序上要先画五角星再画边框最后画防伪线这样层次感更接近真实印章。// 绘制中心五角星starRadius约为画布半径的25% $starPoints []; $starRadius $borderRadius * 0.25; $innerRadius $starRadius * 0.382; for ($i 0; $i 10; $i) { $rad deg2rad(-90 $i * 36); $radius ($i % 2 0) ? $starRadius : $innerRadius; $starPoints[] $cx $radius * cos($rad); $starPoints[] $cy $radius * sin($rad); } imagefilledpolygon($image, $starPoints, (int)(count($starPoints) / 2), $textColor); // 外边框粗线圆 imagesetthickness($image, $config-borderWidth); imageellipse($image, $cx, $cy, $borderRadius * 2, $borderRadius * 2, $textColor); // 内边框细线圆用于圈住外圈文字 imagesetthickness($image, 1); imageellipse($image, $cx, $cy, ($borderRadius - $config-fontSize) * 2, ($borderRadius - $config-fontSize) * 2, $textColor); // 防伪线外圈内侧一圈随机波动短线 $segments 72; for ($i 0; $i $segments; $i) { $angle1 deg2rad(360 / $segments * $i); $angle2 deg2rad(360 / $segments * ($i 1)); $r1 $borderRadius - 6 rand(-2, 2); $r2 $borderRadius - 6 rand(-2, 2); imageline( $image, $cx $r1 * cos($angle1), $cy $r1 * sin($angle1), $cx $r2 * cos($angle2), $cy $r2 * sin($angle2), $textColor ); } // 中心下方绘制用途文字比如“合同专用章” $bbox imagettfbbox($config-centerFontSize, 0, $fontPath, $config-usage); $textWidth abs($bbox[2] - $bbox[0]); imagettftext( $image, $config-centerFontSize, 0, $cx - $textWidth / 2, $cy $starRadius $config-centerFontSize, $textColor, $fontPath, $config-usage );防伪线的实现原理很简单把圆分成72段每段的两个端点半径在正常值附近随机抖动1到2像素连接后形成一圈不规则的细波浪模拟真实印章盖印时的边缘纹理。这种做法成本低肉眼效果却很接近真实盖章扫描后也能看出痕迹差异可以有效防止直接截图复用。4. 避坑指南五个让印章翻车的常见问题4.1 中文文字变成方框或乱码现象渲染出来的PNG里中文全是方框或者问号英文和数字正常。原因GD库的imagettftext依赖指定路径的字体文件如果字体文件不支持中文字形或者传进去的字符串不是UTF-8编码就会用默认字体兜底最终显示成方框。另一个隐患是Linux服务器上常见的中文字体目录和Windows不同直接用系统字体路径很容易踩空。解决项目自带的fonts目录下放一个完整中文字体fontPath参数用绝对路径或基于__DIR__的相对路径。字符串入库和读取时统一走UTF-8mb_str_split之前先确认编码能用mb_convert_encoding就显式转换不要依赖默认值。4.2 文字方向倒置与圆弧偏移现象外圈文字刚画出来时有的字是正立的、有的字是旋转90度的偶尔还会整体偏到圆环一侧。原因这是imagettftext的angle参数没有按极坐标角度联动造成的。文字旋转角度必须跟随字符所在圆弧位置实时计算顶部是0度、右侧是-90度不能全用同一个角度。偏移问题则是忽略了imagettftext的坐标基准点是基线而非字符中心直接用了圆心加半径的坐标。解决用代码里提到的charAngle公式让每个字符的旋转角等于“当前位置与正上方的夹角再取负”。基线补偿用imagettfbbox先量出宽高再向左向上各平移一半尺寸就能让字符中心准确落在圆弧上。我当时就是漏了这个补偿调了一个下午才发现是差半字高。4.3 输出背景是黑色而不是透明现象PNG图片看起来是正常的但拖到Word文档里发现白色区域全部变成黑块把底下的文字全遮住了。原因imagecreatetruecolor创建的是黑色不透明画布只设置imagesavealpha(true)是不够的还必须在填充透明色之前关闭alphablending。否则imagefill填充的透明色会被直接合成到黑色背景上最终保存的是“半透明的黑色”视觉上就是一块黑斑。解决严格按3.1的顺序执行关闭alphablending → 开启savealpha → 填充全透明色 → 重新开启alphablending。这条顺序是写死的调换任何两步结果都不一样。另外输出时用imagepng保存不要用imagejpegJPEG格式本身不支持透明通道。4.4 文字挤在一起或者首尾相连现象公司名短的时候印章很协调公司名一长文字全部挤在圆环下半部分甚至最后一个字和第一个字重叠。原因项目默认采用固定字号渲染不会根据字符数量动态调整。字符数量多时每个字符占用的弧长变小字距被压缩到零GD不会自动排布间距只会在原地重叠。如果圆环半径又偏小最后几个字就会落到圆外。解决在渲染流程的配置校验阶段增加一个自动适配逻辑用字符串长度除以圆弧周长算出每个字符的理论弧长再根据最小间距反推字号。我一般会保证每个字符弧长不小于字号乘1.2如果不够就逐档降低fontSize最多降两档否则印章会显得空旷。4.5 字体路径在Windows和Linux上表现不同现象本地Windows环境跑得好好的部署到Linux服务器就报找不到字体文件或者画出来的字变成豆腐块。原因开发环境用的字体路径是Windows风格的反斜杠和盘符部署到Linux后路径不存在。更隐蔽的是Windows对字体文件名大小写不敏感但Linux严格区分大小写文件名写错一个字母就加载失败。解决fontPath统一用__DIR__拼出绝对路径并加一个is_file校验加载失败时抛出带路径的异常信息。实际部署时用realpath确认字体真实路径同时检查字体文件读写权限HTTP进程用户至少要可读。从那以后我每次接新项目都会先检查字体路径是不是写死成Windows风格的避免上线前一刻才发现。5. 二次开发增加椭圆印章、输出Base64与对接框架5.1 从圆形扩展到椭圆印章不少财务场景需要椭圆印章这个项目的Renderer接口就是为扩展准备的。新增一个EllipseSealRenderer把画布宽高按2比3设置圆形绘制改成imageellipse传不同宽高参数圆弧文字部分把极坐标半径替换为椭圆的参数方程即可。椭圆弧长计算比圆复杂我一般会先采样绘制字符坐标让每个字符按角度均匀分布再用atan2校正实际坐标保证相邻字符间隔视觉上基本一致。// 椭圆印章的字符定位角度相同时半径要按椭圆方程计算 $x $cx $radiusX * cos($angleRad); $y $cy $radiusY * sin($angleRad); // radiusX和radiusY分别为椭圆的长短轴半径5.2 输出Base64与加缓存实际对接业务系统时多数情况不需要把图片落盘而是直接给前端一个Base64字符串。SealResult上我加了一个toBase64方法用输出缓冲把imagepng的结果接住再编码返回。这样接口层可以直接把这个字符串作为JSON字段返回前端用data:image/png;base64,前缀就能渲染。public function toBase64(): string { ob_start(); imagepng($this-image); $data ob_get_clean(); return data:image/png;base64, . base64_encode($data); }缓存方面用公司名、用途、尺寸、颜色的MD5值作为文件名相同配置直接读缓存文件避免每次请求都重新跑一遍GD渲染。印章生成属于CPU密集操作加一层文件缓存能明显降低接口延迟。如果只是要改颜色、加字体、调边框粗细完全不用动渲染器要新增印章形态时照着CircleSealRenderer的结构复制一个类实现render接口就行。整体改造成本比我预想的低不少。希望这些踩坑记录和扩展思路能帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑