序言
在数字化时代,电子文档的安全性和真实性越来越受到重视。电子印章作为一种数字化的身份验证工具,已经成为确保文档合法性和不可篡改性的重要手段。然而,传统的电子印章往往需要人工操作,不仅效率低下,而且在处理大量文件时容易出错。为了解决这一问题,自动化地给PDF文件盖电子章成为了一个迫切的需求。本文将详细介绍,如何通过 .net 程序实现这一功能,废话不多说,步入正题
Nuget 包
本文的核心包为:
iTextSharp,用它来操作 pdf 文件非常方便,具体的用法这里不多赘述,请参考官网
DynamicExpresso,一个非常好用的动态表达式解析工具包
<ItemGroup> <PackageReference Include="DynamicExpresso.Core" Version="2.16.1" /> <PackageReference Include="iTextSharp" Version="5.5.13.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> </ItemGroup>
素材准备
本案例用到的素材包括:用于测试的 pdf 文件一个,模拟电子章图片一张,以及盖章配置文件,文件内容如下:
[ { "SignType" : "image",//素材类型,image表示图片素材,text 表示文本素材 "LastPage" : true,//是否仅最后一页盖章 "ImageUrl" : "https://xxxxxxxx",//图片素材的下载链接 "FileName" : "sign.png",//图片素材文件名称 "ScalePercent" : 20,//图片缩放百分比,100 表示不缩放 "Opacity" : 0.6,//图片透明度,1 表示不透明 "LocationX" : "(input.Width/10)*6",//图片素材的绝对位置表达式,(0,0) 表示左下角 "LocationY" : "input.Height/23 +20",//input.With 和 input.Height 代表 pdf 文件的宽度及高度 "Rotation" : 0//素材的旋转角度 }, { "SignType" : "text", "LastPage" : true, "LocationX" : "(input.Width/10)*6+85", "LocationY" : "input.Height/23 ", "Rotation" : 0, "FontSize" : 20, "Opacity" : 0.6, "FontColor" : {//文本素材的字体颜色值 "R" : 255, "G" : 0, "B" : 0 }, "Text" : "input.Date"//文本素材的表达式,也可以直接写固定文本 } ]
说明:
这里之所以设计为一个数组,是因为可能有些场景下,不仅需要盖电子章,还需要自动签上日期,比如本案例。
签署位置可以自定义,坐标(0,0)代表的是左下角,x 变大即表示横向右移,y 变大表示纵向上移。
配置文件存储,我这里是把配置文件放在了本地,当然你可以存储在任何地方,比如 MongoDB等。
代码展示
本案例采用的是 .net7.0,当然 .net6及以后都是可以的。
配置文件类,与上一步的 json 配置文件对应
namespace PdfSign; public class SignOpt { public string SignType { get; set; } public bool LastPage { get; set; } public string ImageUrl { get; set; } public string FileName { get; set; } public int ScalePercent { get; set; } = 50; public string LocationX { get; set; } public string LocationY { get; set; } public float LocationYf { get; set; } public float Rotation { get; set; } = 0; public int FontSize { get; set; } public float Opacity { get; set; } public RBGColor FontColor { get; set; } public string? Text { get; set; } public record RBGColor(int R, int G, int B); }
pdf 签署方法
using System.Dynamic; using DynamicExpresso; using iTextSharp.text; using iTextSharp.text.pdf; using Newtonsoft.Json.Linq; namespace PdfSign; public class SignService { public static string PdfSign(List<SignOpt> signOpts, string pdfName) { var beforeFileName = pdfName; //签名之前文件名 var afterFileName = pdfName + "_sign"; //签名之后文件名 var idx = 0; foreach (var opt in signOpts) { //创建盖章后生成pdf var outputPdfStream = new FileStream(afterFileName + ".pdf", FileMode.Create, FileAccess.Write, FileShare.None); //读取原有pdf var pdfReader = new PdfReader(beforeFileName + ".pdf"); var pdfStamper = new PdfStamper(pdfReader, outputPdfStream); //读取页数 var pdfPageSize = pdfReader.NumberOfPages; //读取pdf文件第一页尺寸,得到 With 和 Height var size = pdfReader.GetPageSize(1); //通过表达式计算出签署的绝对坐标 var locationX = Eval<float>(opt.LocationX, new { size.Width, size.Height }); var locationY = Eval<float>(opt.LocationY, new { size.Width, size.Height }); if (opt.LastPage) { //盖章在最后一页 var pdfContentByte = pdfStamper.GetOverContent(pdfPageSize); var gs = new PdfGState { FillOpacity = opt.Opacity }; pdfContentByte.SetGState(gs); switch (opt.SignType.ToLower()) { case "image": //获取图片 var image = Image.GetInstance(opt.FileName); //设置图片比例 image.ScalePercent(opt.ScalePercent); //设置图片的绝对位置,位置偏移方向为:左到右,下到上 image.SetAbsolutePosition(locationX, locationY); //图片添加到文档 pdfContentByte.AddImage(image); break; case "text": if (string.IsNullOrWhiteSpace(opt.Text)) continue; var font = BaseFont.CreateFont(); var text = Eval<string>(opt.Text, new { Date = DateTime.Now.ToString("yyyy-MM-dd") }); //开始写入文本 pdfContentByte.BeginText(); pdfContentByte.SetColorFill( new BaseColor(opt.FontColor.R, opt.FontColor.G, opt.FontColor.B)); pdfContentByte.SetFontAndSize(font, opt.FontSize); pdfContentByte.SetTextMatrix(0, 0); pdfContentByte.ShowTextAligned(Element.ALIGN_CENTER, text, locationX, locationY, opt.Rotation); pdfContentByte.EndText(); break; } } pdfStamper.Close(); pdfReader.Close(); idx++; if (idx >= signOpts.Count) continue; //文件名重新赋值 beforeFileName = afterFileName; afterFileName += "_sign"; } return afterFileName + ".pdf"; } //计算动态表达式的值 public static T? Eval<T>(string expr, object context) { if (string.IsNullOrWhiteSpace(expr)) return default; var target = new Interpreter(); var input = JObject.FromObject(context); target.SetVariable("input", input.ToObject<ExpandoObject>()); return target.Eval<T>(expr); } }
测试调用
using Newtonsoft.Json; using PdfSign; //读取签名所需配置文件 var signOpts = await GetSignOpt(); if (signOpts != null && signOpts.Any()) { //执行 pdf 文件盖章 var signFileName= SignService.PdfSign(signOpts, "test"); } //读取配置文件 static async Task<List<SignOpt>?> GetSignOpt() { var strSign = await File.ReadAllTextAsync("cfg.json"); return JsonConvert.DeserializeObject<List<SignOpt>>(strSign); }
效果展示
原 pdf 文件如下图: 最终效果如下图:
结束语
随着本文的深入探讨,我们共同经历了一个完整的旅程,从理解电子印章的重要性到实现一个自动化的.NET程序,用于在PDF文件上高效、准确地加盖电子章。我们不仅学习了.NET环境下处理PDF文件的技术细节,还掌握了如何将电子印章整合到我们的应用程序中,以实现自动化的文档认证过程。