65.9K
CodeProject 正在变化。 阅读更多。
Home

使用C#、Nustache、Mustache和Pechkin从HTML创建PDF

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (23投票s)

2013 年 8 月 19 日

CPOL

3分钟阅读

viewsIcon

69809

downloadIcon

1527

本文介绍了如何使用 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

示例解决方案包含三个项目

  1. WCF 服务
  2. Silverlight应用程序
  3. 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的工作,我现在分享了。如果有人觉得这很有趣并且它可以提供帮助,那么我会觉得这是值得的努力。一如既往,欢迎提出意见/建议。

© . All rights reserved.