2022年左右就写过一篇 FreeSpire.PDF 实现对pdf页面添加签名贴图 的笔记,但那时贴图定位完全是靠人工写死在配置文件里实现的,这次稍微有些不一样了,一定程度实现了程序自动找位置进行贴图,所以就想着可以记录一下。
说起这个活也挺郁闷的,因为核心也是pdf贴签名上传的活,同事不知道在哪听说过我这边2022年左右就接过一个类似的活,然后也不知道听谁说这功能开发起来贼简单一会就可以搞好(我也是服了,你TM倒是简单了,麻烦的是我呀。。。),近期这种奇葩活也明显多了起来。
然而确认开发业务流程事项时没人知道一问三不知,丢过来一家销售自己不知道在哪找到ca机构的sdk文档就让对接,然后就要求快速上线,搞不完就被投诉(中间因为ca合作费用问题折腾小半个月),牢骚归牢骚 但也是真挺佩服这些销售人员的,前期啥也没有就能拿下这些单子。
自动贴图的思路
相比之前的贴图定位写死,这次的区别是基于pdf内的文字锚点 使用 FreeSpire.PDF 读取到锚点的位置坐标,最后自己在代码里微调下位置偏移量,最后适当缩放贴图的尺寸,即可完成自动贴图定位
读取锚点定位
代码以【报告医师:】作为文字锚点(站长偏医疗方向),因为贴图需要贴在其后面空白处,文字锚点可以根据需求更换
using (PdfDocument doc = new PdfDocument()) { doc.LoadFromBytes(pdfBytes); PdfPageBase page = doc.Pages[0]; var reportDocterFinds = page.FindText("报告医师:").Finds; if (reportDocterFinds.HasItems()) { var find = reportDocterFinds.FirstOrDefault(); var reportDocterNamePointF = find.Position;//这个里就是文字锚点的坐标 } }
缩放签名尺寸
这个实现方式很多 站长需求比较简单 这里直接用的GDI+ 做一下尺寸调整,调整到贴图不超出空白区域的尺寸即可
private static Image Resize(this Image resourceImage, int width, int height) { // 用指定的大小和格式初始化Bitmap类的新实例 using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb)) { // 从指定的Image对象创建新Graphics对象 using (Graphics graphics = Graphics.FromImage(bitmap)) { // 在指定位置并且按指定大小绘制原图片对象 graphics.DrawImage(resourceImage, new Rectangle(0, 0, width, height)); } return new Bitmap(bitmap); } }
签名自动贴图
public static bool DocterSignedPdf(ref byte[] pdfBytes, byte[] docterSignedBytes, PointF signedPointF, out string errorMsg) { errorMsg = ""; try { using (var docterSignedMS = new MemoryStream(docterSignedBytes)) { using (var outputPdfStream = new MemoryStream()) { using (var signImage = Bitmap.FromStream(docterSignedMS)) { using (var doc = new PdfDocument()) { doc.LoadFromBytes(pdfBytes); PdfPageBase page = doc.Pages[0]; float sign_h = 30F; float bfb = sign_h / signImage.Height; float sign_w = signImage.Width * bfb; PdfRubberStampAnnotation loStamp = new PdfRubberStampAnnotation(new RectangleF(new PointF(0, 0), new SizeF(page.ActualSize.Width, page.ActualSize.Height))); PdfAppearance loApprearance = new PdfAppearance(loStamp); PdfTemplate template = new PdfTemplate(page.ActualSize.Width, page.ActualSize.Height); using (var resizeImage = signImage.Resize((int)sign_w, (int)sign_h)) { PdfImage image = PdfImage.FromImage(resizeImage); //template.Graphics.DrawImage(image, (int)pos_left - signedPointF.X, (int)pos_top - signedPointF.Y, (int)sign_w, (int)sign_h); template.Graphics.DrawImage(image, signedPointF.X + 100, signedPointF.Y - 6); loApprearance.Normal = template; loStamp.Appearance = loApprearance; page.AnnotationsWidget.Add(loStamp); doc.SaveToStream(outputPdfStream, FileFormat.PDF); outputPdfStream.Flush(); outputPdfStream.Seek(0, SeekOrigin.Begin); pdfBytes = outputPdfStream.ToArray(); return true; } } } } } } catch (Exception ex) { errorMsg = ex.Message; //Log4Net.Error("报告签失败", ex); return false; } }