当前位置: 首页 > news >正文

使用PdfSharp从模板生成Pdf文件

最近在做一个生成文档的需求。通过先制作一个包含各字段占位符的文档模板,导入这个模板并填写内容替换掉占位符,再输出成最终文件。

由于版式固定,安全性更好,业务上常用Pdf作为最终标准化的格式, 在.Net平台下,可以使用PdfSharp导入,编辑,导出Pdf文档。这次做一个生成电子处方Pdf的小示例:

制作模板

使用一个Pdf编辑器(如福昕PDF编辑器)创建模板RecipeTemplate

用[形状编辑]绘制表格框体,用[编辑文本]工具,先插入好固定的内容,比如标题、和各栏目冒号之前的内容。

绘制完成如下图

 再用[表单 - 文本域] 工具,在各个需要生成内容的地方插入表单项。

文本域名称中,填入占位符

假定占位符规则为:

  1. 图片占位符: #{字段名称}#
  2. 文字占位符: ${字段名称}$

那么“医院名称”展位符则设置如下: 

 完成所有字段的占位符,如下图:

 

编写代码

用visual studio新建一个PdfGenerator的项目,保存RecipeTemplate.pdf至Assets目录并设置复制输出目录方式为“始终复制”

项目引用PdfSharp库

dotnet add package PdfSharp --version 1.50.5147

创建模型类RecipeDocInfo,此类用于承载业务数据

    public class RecipeDocInfo
    {
        public int Id { get; set; }
        public string HospitalName { get; set; }
        public string DepartmentName { get; set; }
        public string ClientName { get; set; }
        public string ClientAge { get; set; }
        public string ClientSex { get; set; }
        public string Rps { get; set; }
        public string DraftEmployeeName { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Status { get; set; }
        public string AuditEmployeeName { get; set; }
        public string DraftEmployeeSignature { get; set; }
        public string AuditEmployeeSignature { get; set; }
        public string StartTimeString { get; set; }
    }

 Exporter.cs中,创建ExportDocxByObject方法,使用PdfReader.Open()可以获取PdfDocument对象

public static PdfDocument ExportDocxByObject(string templatePath, object data)
{
      var doc = PdfReader.Open(templatePath, PdfDocumentOpenMode.Modify);
      return doc;
}

PdfDocument.AcroForm()方法可以拿到Pdf文档中的表单对象,该对象中的Fields存储表单项目集合,遍历Key值获取每个表单项

PdfAcroForm form = doc.AcroForm;
foreach (var fieldName in form.Fields.Names)
{
     var run = form.Fields[fieldName] as PdfTextField;
     text = run.Name;  //获取每一个占位符名称

}          

 表单项的Name属性为我们设置的表单名称,即占位符。

接下来处理数据对象,通过反射方式获取对象成员名称,并与占位符作匹配,若占位符包含(string.Contains())该成员名称,则将值写入这个表单项的Value中,这里注意一个多行处理的情况。

foreach (PropertyInfo p in pi)
{
	string key = $"${p.Name}$";
	if (text.Contains(key))
	{
		var value = "";
		try
		{
			value =  p.GetValue(model, null).ToString();
		}
		catch (Exception ex)
		{
		}

		if (value.Contains('\n'))
		{
			run.MultiLine = true;

		}

		run.Value = new PdfString(value);
		run.ReadOnly = true;
	}
}

 readOnly设置为true,以防止Pdf表单中的值被随意修改。

同理我们处理图片:

首先数据对象中的内容,应为图片的本地路径或者网络Url

var filePath = p.GetValue(model, null) as string;

 然后读取,绘制图片,注意图片的大小以及位置坐标显示,与表单所对应的框架(/Rect)一致

详细的绘图方式,请参考官方文档:PDFsharp Sample: Graphics - PDFsharp and MigraDoc Wiki

var rectangle = run.Elements.GetRectangle("/Rect");
var xForm = new XForm(doc, rectangle.Size);
using (var xGraphics = XGraphics.FromPdfPage(doc.Pages[0]))
{
	var image = XImage.FromStream(fileStream);
	xGraphics.DrawImage(image, rectangle.ToXRect() +new XPoint(0, 400));
	var state = xGraphics.Save();
	xGraphics.Restore(state);
}

完成Exporter.cs之后,在Main函数中使用

public class Program
{
	public static async Task Main(string[] args)
	{
		Console.WriteLine("Generator begin");
		var docinfo = GetRecipeDocInfo() {  ...  };
		var result = Exporter.ExportDocxByObject(/*template path*/, docinfo);
		result.Save(/*output path*/);
	}
}

 测试

至此完成了所有工作,运行程序,待程序执行完毕后,打开output目录下生成的文档,看看最后效果:

 

项目地址:

jevonsflash/PdfGenerator (github.com)

结束语

根据这一思想,我们可以直观地编辑我们想要的最终文件,无论这个文档多么复杂,我们只用关心占位符和最终的值。

同样,这一思想也可以应用到NPOI库来生成Word文档。

相关文章:

  • HTML篇三——(2)
  • 【012】基于JavaWeb酒店客房管理系统(附源码、数据库、数据库文档、运行教程)
  • Gitee账号注册以及Git下载安装
  • 边学边记——Java中有关接口的知识
  • ant-design-vue 库 Loading 组件封装
  • 2022 年前端趋势的 技术发展情况
  • Observability:使用 Elastic Agent 来收集定制的 TCP 日志
  • 【C++】C++ 入门
  • Flink Kafka Sink 源码分析
  • 高斯消元法(2)——保姆级笔记
  • R语言因子分析全流程
  • Nginx简单配置 - 基础安全
  • 基于sdrpi的openwifi实践2:生成BOOT.BIN
  • 七、安装Centos7+8系统+超级优化
  • Kali-登录暴力破解器工具-medusa使用
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 【EOS】Cleos基础
  • 30天自制操作系统-2
  • css的样式优先级
  • go语言学习初探(一)
  • java概述
  • JS+CSS实现数字滚动
  • pdf文件如何在线转换为jpg图片
  • React Native移动开发实战-3-实现页面间的数据传递
  • SpiderData 2019年2月16日 DApp数据排行榜
  • Spring框架之我见(三)——IOC、AOP
  • vue:响应原理
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 多线程 start 和 run 方法到底有什么区别?
  • 规范化安全开发 KOA 手脚架
  • 利用DataURL技术在网页上显示图片
  • 前嗅ForeSpider采集配置界面介绍
  • 入口文件开始,分析Vue源码实现
  • 删除表内多余的重复数据
  • 深入浏览器事件循环的本质
  • 数据科学 第 3 章 11 字符串处理
  • 通过git安装npm私有模块
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 怎么把视频里的音乐提取出来
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)php新闻发布平台 毕业设计 141646
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (蓝桥杯每日一题)love
  • (力扣题库)跳跃游戏II(c++)
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (七)理解angular中的module和injector,即依赖注入
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (一) springboot详细介绍