使用C#、Nustache、Mustache和Pechkin从HTML创建PDF
本文介绍了如何使用 C# 对象创建 PDF 文件。
背景
最近,我需要完成一项任务,需要创建一个Silverlight页面的PDF版本。标记(XAML)的数据来自C#对象(没错,你猜对了!我们使用了MVVM模式),我们想将该对象用于PDF文件。以下是我们用来创建PDF文件的组件。
- Nustache [^],一个.NET库,用于将对象数据转换为字符串(在本例中为HTML)
- Mustache模板[^] 用于视觉外观
- Pechkin[^],一个.NET PDF库,用于将string转换为PDF
依赖项
如上所述,您需要
- Nustache库
- Pechkin
我在示例代码中排除了这些DLL,因为Pechkin包中的一个DLL,wkhtmltox0,大约有29 MB,导致附件变大。因此,如果您只是打开并按下F5,您将收到构建错误(源代码可以在VS 2010或VS 2012中打开)。
安装依赖项
使用Nuget包管理器

手动安装
您也可以下载这些文件的.zip版本,并手动将其添加到您的项目中。在这种情况下,您应该进行以下设置才能使代码正常工作。
添加引用

将这些文件添加到项目的根目录,并设置“如果较新则复制”

更新web.config(如果您在控制台中使用它,则为App.config)
<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Common.Logging" 
        publicKeyToken="af08829b84f0328e" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.1.2.0" newVersion="2.1.2.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
Using the Code
示例解决方案包含三个项目
- WCF 服务
- Silverlight应用程序
- Web应用程序

PDFService
PdfService类提供两种方法
- PdfBytes- 接受任何- string内容并将其转换为客户端的PDF字节。
- EmployeePdf- 特定于- Employee对象,它将- employee数据转换为html- string,然后可以将其传递给- PdfBytes方法以获取PDF字节。
    public class PdfService : IPdfService
    {
        public PdfResult PdfBytes(string content)
        {
            var result = new PdfResult();
            try
            {
                var oc = new ObjectConfig();
                oc.SetPrintBackground(true); //to apply the background colors in the pdf
                var footer = oc.Footer;
                footer.SetFontSize(8);
                footer.SetTexts("For internal use only", string.Empty, string.Empty);
                result.Bytes = new SynchronizedPechkin(new GlobalConfig()).Convert(oc, content);
            }
            catch (Exception ex)
            {
                result.Error = ex.Message;
            }
            return result;
        }
        public PdfResult EmployeePdf(Employee employee)
        {
            string nustachTemplate = Path.Combine
		(HostingEnvironment.ApplicationPhysicalPath, "App_Data", "EmployeeTemplate.html");
            var employeeHtml = Render.FileToString(nustachTemplate, employee);
            return PdfBytes(employeeHtml);
        }
    }
Employee对象的数据被注入到Mustache html模板中。您可以应用样式(只有内联样式,样式表无法链接)来设计页面输出。
<html>
<head>
    <title>Employee Print</title>
    <style>
        body {
            margin: 5px;
        }
 
        h3 {
            text-align: center;
            border-radius: 10px;
        }
 
        h4 {
            background: silver;
            border-radius: 5px;
            padding-left: 10px;
        }
    </style>
</head>
<body>
 
    <h3>Employee {{Name}}</h3>
    <h4>Basic Details</h4>
    <div>
        <p>Name:</p>
        <p>{{Name}}</p>
    </div>
    <div>
        <p>Email:</p>
        <p>{{Email}}</p>
    </div>
    <div>
        <p>Address:</p>
        <p>{{Address}}</p>
    </div>
    <h4>Skills</h4>
 
    {{#Skills}}
  <p>{{Name}}</p>
    {{/Skills}}
    
    {{^Skills}}
  <p>No skills</p>
    {{/Skills}}
    <h4>Hobbies</h4>
 
    {{#Hobbies}}
  <p>{{Name}}</p>
    {{/Hobbies}}
 
      {{^Hobbies}}
  <p>No hobbies</p>
    {{/Hobbies}}
 
     <h4>Jobs</h4>
    {{#Jobs}}
  <p>{{Company}}: as a {{Role}} for {{NoOfYears}} years.</p>
    {{/Jobs}}
 
    {{^Jobs}}
  <p>No jobs</p>
    {{/Jobs}}
</body>
</html>
以及Employee类
    [DataContract]
    public class Employee
    {
        public Employee()
        {
            Hobbies = new List<Hobby>();
            Skills = new List<Skill>();
            Jobs = new List<Job>();
        }
 
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Address { get; set; }
        [DataMember]
        public string Email { get; set; }
        [DataMember]
        public List<Hobby> Hobbies { get; set; }
        [DataMember]
        public List<Skill> Skills { get; set; }
        [DataMember]
        public List<Job> Jobs { get; set; }
    }
这是最终的PDF

SilverlightApplication1
一个使用PDFService生成Pdf的客户端应用程序。调用服务并保存文件的代码。
        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            var dialog = new SaveFileDialog()
                {
                    DefaultExt = "Adobe PDF Files(*.pdf)",
                    Filter = "PDF (*.PDF)|*.PDF",
                    FilterIndex = 2
                };
            if (dialog.ShowDialog() == true)
            {
                var client = new PdfServiceClient();
                var emp = GetEmployee();
                client.EmployeePdfCompleted += (s, ea) =>
                {
                    if (ea.Error != null)
                    {
                        MessageBox.Show(string.Format("Error:{0}", ea.Error.Message));
                        return;
                    }
                    if (!string.IsNullOrWhiteSpace(ea.Result.Error))
                    {
                        MessageBox.Show(string.Format("Error:{0}", ea.Result.Error));
                        return;
                    }
                    using (System.IO.Stream stream = dialog.OpenFile())
                    {
                        stream.Write(ea.Result.Bytes, 0, ea.Result.Bytes.Length);
                    }
                    MessageBox.Show("Pdf saved");
                };
                client.EmployeePdfAsync(emp);
            }
        }
优点和缺点
优点
- 易于构建页面内容
- 我们可以使用CSS来设置输出样式
- 无需手动计算PDF中的页数。页面根据内容自动生成。
- 免费库
缺点
- DLL大小很大
- 来自Quake2Player:(我还没有遇到这种情况,但感谢您的信息)一个重要的缺点是,一些主机阻止通过内核执行DLL。例如,Azure网站。因此,例如,不可能执行wkhtmltopdf.exe。 
- 您可能还会想到其他什么?
关注点
Nustache和Mustache模板并不是创建PDF文件的必要条件。您可以无需它,只需传递一个string或构建您自己的html,只要它满足要求即可。我个人更喜欢Mustache,因为它易于构建html,更重要的是,您可以在具有完整HTML布局的情况下根据条件显示或隐藏元素。
在下载示例代码之前
再次说明,除非您按照上面依赖项部分中提到的说明操作,否则示例代码将无法工作。
历史
- 第一版
最后...
我想分享我关于创建PDF的工作,我现在分享了。如果有人觉得这很有趣并且它可以提供帮助,那么我会觉得这是值得的努力。一如既往,欢迎提出意见/建议。




