通过代码实现 pdf 文件自动盖章

序言

在数字化时代,电子文档的安全性和真实性越来越受到重视。电子印章作为一种数字化的身份验证工具,已经成为确保文档合法性和不可篡改性的重要手段。然而,传统的电子印章往往需要人工操作,不仅效率低下,而且在处理大量文件时容易出错。为了解决这一问题,自动化地给PDF文件盖电子章成为了一个迫切的需求。本文将详细介绍,如何通过 .net 程序实现这一功能,废话不多说,步入正题

Nuget 包

本文的核心包为:

  • iTextSharp,用它来操作 pdf 文件非常方便,具体的用法这里不多赘述,请参考官网
  • DynamicExpresso,一个非常好用的动态表达式解析工具包
ini 复制代码
<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 文件一个,模拟电子章图片一张,以及盖章配置文件,文件内容如下:

json 复制代码
[
  {
    "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"//文本素材的表达式,也可以直接写固定文本
  }
]

说明:

  1. 这里之所以设计为一个数组,是因为可能有些场景下,不仅需要盖电子章,还需要自动签上日期,比如本案例。
  2. 签署位置可以自定义,坐标(0,0)代表的是左下角,x 变大即表示横向右移,y 变大表示纵向上移。
  3. 配置文件存储,我这里是把配置文件放在了本地,当然你可以存储在任何地方,比如 MongoDB等。

代码展示

本案例采用的是 .net7.0,当然 .net6及以后都是可以的。

  1. 配置文件类,与上一步的 json 配置文件对应
csharp 复制代码
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);
}
  1. pdf 签署方法
ini 复制代码
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);
    }
}
  1. 测试调用
swift 复制代码
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);
}
  1. 效果展示
    原 pdf 文件如下图: 最终效果如下图:

结束语

随着本文的深入探讨,我们共同经历了一个完整的旅程,从理解电子印章的重要性到实现一个自动化的.NET程序,用于在PDF文件上高效、准确地加盖电子章。我们不仅学习了.NET环境下处理PDF文件的技术细节,还掌握了如何将电子印章整合到我们的应用程序中,以实现自动化的文档认证过程。

相关推荐
小蜗牛慢慢爬行12 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
A小白590834 分钟前
Docker部署实践:构建可扩展的AI图像/视频分析平台 (脱敏版)
后端
goTsHgo41 分钟前
在 Spring Boot 的 MVC 框架中 路径匹配的实现 详解
spring boot·后端·mvc
waicsdn_haha1 小时前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
Q_19284999061 小时前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
良许Linux1 小时前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
求知若饥1 小时前
NestJS 项目实战-权限管理系统开发(六)
后端·node.js·nestjs
左羊1 小时前
【代码备忘录】复杂SQL写法案例(一)
后端
gb42152872 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶2 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot