[C#]使用第三方开源库iText7.pdfHtml,将Html转换成Pdf,以及如何以Html作为打印模板
使用第三方开源库iText7.pdfHtml,将html和css转成pdf,官方:https://itextpdf.com/en/demos/convert-html-css-to-pdf-free-online;
官方示例:
using System.IO; using iText.Html2pdf; namespace WebsiteDemoPdfHtml { class Program { private static string ORIG = "/uploads/input.html"; private static string OUTPUT_FOLDER = "/myfiles/"; static void Main(string[] args) { string pdfDest = OUTPUT_FOLDER + "output.pdf"; HtmlConverter.ConvertToPdf(new FileStream(ORIG, FileMode.Open), new FileStream(pdfDest, FileMode.Create)); } } }
官方可以下载到详细的使用说明文档:
设置默认打印纸张大小:
var pdfDest = "hello.pdf"; var pdfWriter = new PdfWriter (pdfDest); var pdf = new PdfDocument (pdfWriter); var pageSize = PageSize.A4; // 设置默认打印纸张大小,css @page规则可覆盖这个 pdf.SetDefaultPageSize (pageSize);
支持css @page规则控制打印设置选项,例如css @page设置A3打印纸,横向打印,这些规规将覆盖上面的设置默认打印纸张大小:
@page { size: A3 landscape; }
如果需要引入其他资源,比如插入图片,需要设置根目录,将资源文件放入根目录或子文件夹下:
var properties = new ConverterProperties (); properties.SetBaseUri ("wwwroot"); // 设置根目录
默认不支持中文字体,需要修改默认字体提供者,使其支持系统字体:
var provider = new DefaultFontProvider (true, true, true); // 第三个参数为True,以支持系统字体,否则不支持中文 properties.SetFontProvider (provider);
支持css @media规则,使其在不同设备上显示不同效果,比如在预览时使用Screen设备显示彩色效果,在打印时使用Print设备增强黑白效果:
var mediaDeviceDescription = new MediaDeviceDescription (MediaType.PRINT); // 指当前设备类型,如果是预览使用SCREEN mediaDeviceDescription.SetWidth (pageSize.GetWidth ()); properties.SetMediaDeviceDescription (mediaDeviceDescription);
最后是以html作为打印模板,加载数据,再转成pdf;
官方推荐的是使用XSL转换(xmlns:xsl="http://www.w3.org/1999/XSL/Transform"),将xml转换成html,但该示例目前仅支持java,c#找不到相关源码,并且该方式不支持模板预览,不方便用户修改模板:
所以还是推荐使用正则替换规则导入数据,下面是示例html:
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title></title> <style type="text/css"> @page { size: A4 landscape; } ul { margin-left: 0; padding-left: 0; } ul li { list-style: none; } ul li:after { content: ""; display: block; clear: both; } ul li p { float: left; margin-left: 2em; } ul li p:first-child { margin-left: 0; } ul li p img { width: 36px; height: 36px; } </style> </head> <body> <h3>使用第三方库iText7.pdfHtml,将Html转换成Pdf,以及如何以Html作为打印模板</h3> <h5>{{ListOfNames}}</h5> <ul> <!--template-start--> <li> <p><img src="{{Avatar}}" /></p> <p>姓名:{{Name}}</p> <p>年龄:{{Age}}</p> <p>性别:{{Sex}}</p> </li> <!--template-end--> </ul> </body> </html>
使用双大括号{{field}}作为书签,但因为有列表数据,如果直接替换,可能将列表数据修改,所以应该先替换列表数据,在该示例中,以<!--template-start-->作为模板匹配头,以<!--template-end-->作为模板匹配尾,然后不断复制匹配字段,如果子模版又有子模版,则先替换子模版,以此类推,下面是完整代码:
using iText.Html2pdf; using iText.Html2pdf.Resolver.Font; using iText.Kernel.Geom; using iText.Kernel.Pdf; using iText.StyledXmlParser.Css.Media; using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; namespace Demo { class Program { static void Main(String[] args) { var pdfDest = "hello.pdf"; var pdfWriter = new PdfWriter(pdfDest); var pdf = new PdfDocument(pdfWriter); var pageSize = PageSize.A4; // 设置默认页面大小,css @page规则可覆盖这个 pdf.SetDefaultPageSize(pageSize); var properties = new ConverterProperties(); properties.SetBaseUri("wwwroot"); // 设置根目录 properties.SetCharset("utf-8"); var provider = new DefaultFontProvider(true, true, true); // 第三个参数为True,以支持系统字体,否则不支持中文 properties.SetFontProvider(provider); var mediaDeviceDescription = new MediaDeviceDescription(MediaType.PRINT); // 指当前设备类型,如果是预览使用SCREEN mediaDeviceDescription.SetWidth(pageSize.GetWidth()); properties.SetMediaDeviceDescription(mediaDeviceDescription); var peoples = new List<People> { new People { Avatar = "avatar.jpg", Name = "小明", Age = 23, Sex = "男" }, new People { Avatar = "avatar.jpg", Name = "小王", Age = 18, Sex = "男" }, new People { Avatar = "avatar.jpg", Name = "小樱", Age = 19, Sex = "女" }, new People { Avatar = "avatar.jpg", Name = "小兰", Age = 20, Sex = "女" }, }; var htmlTemplate = File.ReadAllText("wwwroot/hello.html"); Dictionary<String, String> dic = null; Regex regex = null; var start = "<!--template-start-->"; var end = "<!--template-end-->"; var match = Regex.Match(htmlTemplate, $@"{start}(.|\s)+?{end}"); if (match != null && match.Value.Length > start.Length + end.Length) { var template = match.Value.Substring(start.Length, match.Value.Length - start.Length - end.Length); var sb = new StringBuilder(start); foreach (var people in peoples) { dic = HtmlTemplateDataBuilder.Create(people); regex = new Regex(String.Join("|", dic.Keys)); sb.Append(regex.Replace(template, m => dic[m.Value])); } sb.Append(end); htmlTemplate = htmlTemplate.Replace(match.Value, sb.ToString()); } dic = new Dictionary<String, String> { ["{{ListOfNames}}"] = "人员列表" }; regex = new Regex(String.Join("|", dic.Keys), RegexOptions.IgnoreCase); var html = regex.Replace(htmlTemplate, m => dic[m.Value]); HtmlConverter.ConvertToPdf(html, pdf, properties); } struct People { public String Avatar { get; set; } // 头像 public String Name { get; set; } // 姓名 public Int32 Age { get; set; } // 年龄 public String Sex { get; set; } // 性别 } public static class HtmlTemplateDataBuilder { public static Dictionary<String, String> Create(Object obj) { var props = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty); var dic = new Dictionary<String, String>(); foreach (var prop in props) { dic.Add("{{" + prop.Name + "}}", prop.GetValue(obj, null).ToString()); } return dic; } } } }