[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>
View Code

使用双大括号{{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;
            }
        }
    }
}
View Code

 

posted @ 2020-12-17 16:40  孤独成派  阅读(2129)  评论(0编辑  收藏  举报