• 使用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,此类用于承载业务数据

    1. public class RecipeDocInfo
    2. {
    3. public int Id { get; set; }
    4. public string HospitalName { get; set; }
    5. public string DepartmentName { get; set; }
    6. public string ClientName { get; set; }
    7. public string ClientAge { get; set; }
    8. public string ClientSex { get; set; }
    9. public string Rps { get; set; }
    10. public string DraftEmployeeName { get; set; }
    11. public string Name { get; set; }
    12. public decimal Price { get; set; }
    13. public string Status { get; set; }
    14. public string AuditEmployeeName { get; set; }
    15. public string DraftEmployeeSignature { get; set; }
    16. public string AuditEmployeeSignature { get; set; }
    17. public string StartTimeString { get; set; }
    18. }

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

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

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

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

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

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

    1. foreach (PropertyInfo p in pi)
    2. {
    3. string key = $"${p.Name}$";
    4. if (text.Contains(key))
    5. {
    6. var value = "";
    7. try
    8. {
    9. value = p.GetValue(model, null).ToString();
    10. }
    11. catch (Exception ex)
    12. {
    13. }
    14. if (value.Contains('\n'))
    15. {
    16. run.MultiLine = true;
    17. }
    18. run.Value = new PdfString(value);
    19. run.ReadOnly = true;
    20. }
    21. }

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

    同理我们处理图片:

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

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

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

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

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

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

    1. public class Program
    2. {
    3. public static async Task Main(string[] args)
    4. {
    5. Console.WriteLine("Generator begin");
    6. var docinfo = GetRecipeDocInfo() { ... };
    7. var result = Exporter.ExportDocxByObject(/*template path*/, docinfo);
    8. result.Save(/*output path*/);
    9. }
    10. }

     测试

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

     

    项目地址:

    jevonsflash/PdfGenerator (github.com)

    结束语

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

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

  • 相关阅读:
    Solon 1.6.25 发布,轻量级应用开发框架
    Golang 中的字符串:常见错误和最佳实践
    8.WPF命令
    【Linux】 ps命令使用
    MybatisPlus整合笔记(2022)
    JMeter笔记10 | JMeter检查点
    Java之并发工具类的详细解析
    在.Core中用EF添加数据库实体类
    方法递归调用
    【C#】抽象方法、接口、虚方法
  • 原文地址:https://blog.csdn.net/jevonsflash/article/details/126971744