目录
  • 前言
  • QuestPDF介绍
  • QuestPDF License
  • 项目源代码
  • 创建一个控制台应用
  • 安装QuestPDF Nuget包
  • 快速实现发票PDF文档生成
    • 创建InvoiceModel
    • CreateInvoiceDetails
    • CreateInvoiceDocument
    • Program
  • 完整示例源代码
    • 示例运行效果图
      • 注意问题
        • 中文报异常
        • 中文乱码问题

      前言

      在.NET平台中操作生成PDF的类库有很多如常见的有iTextSharp、PDFsharp、Aspose.PDF等,今天我们分享一个用于生成PDF文档的现代开源.NET库:QuestPDF,本文将介绍QuestPDF并使用它快速实现发票PDF文档生成功能。

      .NET使用QuestPDF高效地生成PDF文档

      QuestPDF介绍

      QuestPDF 是一个用于生成 PDF 文档的现代开源 .NET 库。QuestPDF 由简洁易用的 C# Fluent API 提供全面的布局引擎。轻松生成 PDF 报告、发票、导出等。QuestPDF它提供了一个布局引擎,在设计时考虑了完整的分页支持。与其他库不同,它不依赖于 HTML 到 PDF 的转换,这在许多情况下是不可靠的。相反,它实现了自己的布局引擎,该引擎经过优化,可以满足所有与分页相关的要求。

      QuestPDF License

      分为社区版、专业版、和企业版。

      QuestPDF

      .NET使用QuestPDF高效地生成PDF文档

      项目源代码

      .NET使用QuestPDF高效地生成PDF文档

      创建一个控制台应用

      创建一个名为QuestPDFTest的控制台应用。

      .NET使用QuestPDF高效地生成PDF文档

      .NET使用QuestPDF高效地生成PDF文档

      安装QuestPDF Nuget包

      搜索:QuestPDF包进行安装。

      .NET使用QuestPDF高效地生成PDF文档

      快速实现发票PDF文档生成

      创建InvoiceModel

      namespace QuestPDFTest
      {
          public class InvoiceModel
          {
      
              /// <summary>
              /// 发票号码
              /// </summary>
              public int InvoiceNumber { get; set; }
      
              /// <summary>
              /// 发票开具日期
              /// </summary>
              public DateTime IssueDate { get; set; }
      
              /// <summary>
              /// 发票到期日期
              /// </summary>
              public DateTime DueDate { get; set; }
      
              /// <summary>
              /// 卖方公司名称
              /// </summary>
              public string SellerCompanyName { get; set; }
      
              /// <summary>
              /// 买方公司名称
              /// </summary>
              public string CustomerCompanyName { get; set; }
      
              /// <summary>
              /// 订单消费列表
              /// </summary>
              public List<OrderItem> OrderItems { get; set; }
      
              /// <summary>
              /// 备注
              /// </summary>
              public string Comments { get; set; }
          }
      
          public class OrderItem
          {
              /// <summary>
              /// 消费类型
              /// </summary>
              public string Name { get; set; }
      
              /// <summary>
              /// 消费金额
              /// </summary>
              public decimal Price { get; set; }
      
              /// <summary>
              /// 消费数量
              /// </summary>
              public int Quantity { get; set; }
          }
      }
      
      

      CreateInvoiceDetails

      namespace QuestPDFTest
      {
          public class CreateInvoiceDetails
          {
              private static readonly Random _random = new Random();
      
              public enum InvoiceType
              {
                  餐饮费,
                  交通费,
                  住宿费,
                  日用品,
                  娱乐费,
                  医疗费,
                  通讯费,
                  教育费,
                  装修费,
                  旅游费
              }
      
              /// <summary>
              /// 获取发票详情数据
              /// </summary>
              /// <returns></returns>
              public static InvoiceModel GetInvoiceDetails()
              {
                  return new InvoiceModel
                  {
                      InvoiceNumber = _random.Next(1_000, 10_000),
                      IssueDate = DateTime.Now,
                      DueDate = DateTime.Now + TimeSpan.FromDays(14),
                      SellerCompanyName = "追逐时光者",
                      CustomerCompanyName = "DotNetGuide技术社区",
                      OrderItems = Enumerable
                      .Range(1, 20)
                      .Select(_ => GenerateRandomOrderItemInfo())
                      .ToList(),
                      Comments = "DotNetGuide技术社区是一个面向.NET开发者的开源技术社区,旨在为开发者们提供全面的C#/.NET/.NET Core相关学习资料、技术分享和咨询、项目推荐、招聘资讯和解决问题的平台。在这个社区中,开发者们可以分享自己的技术文章、项目经验、遇到的疑难技术问题以及解决方案,并且还有机会结识志同道合的开发者。我们致力于构建一个积极向上、和谐友善的.NET技术交流平台,为广大.NET开发者带来更多的价值和成长机会。"
                  };
              }
      
              /// <summary>
              /// 订单信息生成
              /// </summary>
              /// <returns></returns>
              private static OrderItem GenerateRandomOrderItemInfo()
              {
                  var types = (InvoiceType[])Enum.GetValues(typeof(InvoiceType));
                  return new OrderItem
                  {
                      Name = types[_random.Next(types.Length)].ToString(),
                      Price = (decimal)Math.Round(_random.NextDouble() * 100, 2),
                      Quantity = _random.Next(1, 10)
                  };
              }
          }
      }
      
      

      CreateInvoiceDocument

      using QuestPDF.Fluent;
      using QuestPDF.Helpers;
      using QuestPDF.Infrastructure;
      
      namespace QuestPDFTest
      {
          public class CreateInvoiceDocument : IDocument
          {
              /// <summary>
              /// 获取Logo的的Image对象
              /// </summary>
              public static Image LogoImage { get; } = Image.FromFile("dotnetguide.png");
      
              public InvoiceModel Model { get; }
      
              public CreateInvoiceDocument(InvoiceModel model)
              {
                  Model = model;
              }
      
              public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
      
              public void Compose(IDocumentContainer container)
              {
                  container
                      .Page(page =>
                      {
                          //设置页面的边距
                          page.Margin(50);
      
                          //字体默认大小18号字体
                          page.DefaultTextStyle(x => x.FontSize(18));
      
                          //页眉部分
                          page.Header().Element(BuildHeaderInfo);
      
                          //内容部分
                          page.Content().Element(BuildContentInfo);
      
                          //页脚部分
                          page.Footer().AlignCenter().Text(text =>
                          {
                              text.CurrentPageNumber();
                              text.Span(" / ");
                              text.TotalPages();
                          });
                      });
              }
      
              #region 构建页眉部分
              void BuildHeaderInfo(IContainer container)
              {
                  container.Row(row =>
                  {
                      row.RelativeItem().Column(column =>
                      {
                          column.Item().Text($"发票编号 #{Model.InvoiceNumber}").FontFamily("fangsong").FontSize(20).SemiBold().FontColor(Colors.Blue.Medium);
      
                          column.Item().Text(text =>
                          {
                              text.Span("发行日期: ").FontFamily("fangsong").FontSize(13).SemiBold();
                              text.Span($"{Model.IssueDate:d}");
                          });
      
                          column.Item().Text(text =>
                          {
                              text.Span("终止日期: ").FontFamily("fangsong").FontSize(13).SemiBold();
                              text.Span($"{Model.DueDate:d}");
                          });
                      });
      
                      //在当前行的常量项中插入一个图像
                      row.ConstantItem(130).Image(LogoImage);
                  });
              }
      
              #endregion
      
              #region 构建内容部分
      
              void BuildContentInfo(IContainer container)
              {
                  container.PaddingVertical(40).Column(column =>
                  {
                      column.Spacing(20);
      
                      column.Item().Row(row =>
                      {
                          row.RelativeItem().Component(new AddressComponent("卖方公司名称", Model.SellerCompanyName));
                          row.ConstantItem(50);
                          row.RelativeItem().Component(new AddressComponent("客户公司名称", Model.CustomerCompanyName));
                      });
      
                      column.Item().Element(CreateTable);
      
                      var totalPrice = Model.OrderItems.Sum(x => x.Price * x.Quantity);
                      column.Item().PaddingRight(5).AlignRight().Text($"总计: {totalPrice}").FontFamily("fangsong").SemiBold();
      
                      if (!string.IsNullOrWhiteSpace(Model.Comments))
                          column.Item().PaddingTop(25).Element(BuildComments);
                  });
              }
      
              /// <summary>
              /// 创建表格
              /// </summary>
              /// <param name="container">container</param>
              void CreateTable(IContainer container)
              {
                  var headerStyle = TextStyle.Default.SemiBold();
      
                  container.Table(table =>
                  {
                      table.ColumnsDefinition(columns =>
                      {
                          columns.ConstantColumn(25);
                          columns.RelativeColumn(3);
                          columns.RelativeColumn();
                          columns.RelativeColumn();
                          columns.RelativeColumn();
                      });
      
                      table.Header(header =>
                      {
                          header.Cell().Text("#").FontFamily("fangsong");
                          header.Cell().Text("消费类型").Style(headerStyle).FontFamily("fangsong");
                          header.Cell().AlignRight().Text("花费金额").Style(headerStyle).FontFamily("fangsong");
                          header.Cell().AlignRight().Text("数量").Style(headerStyle).FontFamily("fangsong");
                          header.Cell().AlignRight().Text("总金额").Style(headerStyle).FontFamily("fangsong");
                          //设置了表头单元格的属性
                          header.Cell().ColumnSpan(5).PaddingTop(5).BorderBottom(1).BorderColor(Colors.Black);
                      });
      
                      foreach (var item in Model.OrderItems)
                      {
                          var index = Model.OrderItems.IndexOf(item) + 1;
      
                          table.Cell().Element(CellStyle).Text($"{index}").FontFamily("fangsong");
                          table.Cell().Element(CellStyle).Text(item.Name).FontFamily("fangsong");
                          table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price}").FontFamily("fangsong");
                          table.Cell().Element(CellStyle).AlignRight().Text($"{item.Quantity}").FontFamily("fangsong");
                          table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price * item.Quantity}").FontFamily("fangsong");
                          static IContainer CellStyle(IContainer container) => container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5);
                      }
                  });
              }
      
              #endregion
      
              #region 构建页脚部分
      
              void BuildComments(IContainer container)
              {
                  container.ShowEntire().Background(Colors.Grey.Lighten3).Padding(10).Column(column =>
                  {
                      column.Spacing(5);
                      column.Item().Text("DotNetGuide技术社区介绍").FontSize(14).FontFamily("fangsong").SemiBold();
                      column.Item().Text(Model.Comments).FontFamily("fangsong");
                  });
              }
      
              #endregion
          }
      
          public class AddressComponent : IComponent
          {
              private string Title { get; }
              private string CompanyName { get; }
      
              public AddressComponent(string title, string companyName)
              {
                  Title = title;
                  CompanyName = companyName;
              }
      
              public void Compose(IContainer container)
              {
                  container.ShowEntire().Column(column =>
                  {
                      column.Spacing(2);
      
                      column.Item().Text(Title).FontFamily("fangsong").SemiBold();
                      column.Item().PaddingBottom(5).LineHorizontal(1);
                      column.Item().Text(CompanyName).FontFamily("fangsong");
                  });
              }
          }
      }
      
      

      Program

      using QuestPDF;
      using QuestPDF.Fluent;
      using QuestPDF.Infrastructure;
      
      namespace QuestPDFTest
      {
          internal class Program
          {
              static void Main(string[] args)
              {
                  // 1、请确保您有资格使用社区许可证,不设置的话会报异常。
                  Settings.License = LicenseType.Community;
      
                  // 2、禁用QuestPDF库中文本字符可用性的检查
                  Settings.CheckIfAllTextGlyphsAreAvailable = false;
      
                  // 3、PDF Document 创建
                  var invoiceSourceData = CreateInvoiceDetails.GetInvoiceDetails();
                  var document = new CreateInvoiceDocument(invoiceSourceData);
      
                  // 4、生成 PDF 文件并在默认的查看器中显示
                  document.GeneratePdfAndShow();
              }
          }
      }
      

      完整示例源代码

      https://github.com/YSGStudyHards/QuestPDFTest

      .NET使用QuestPDF高效地生成PDF文档

      示例运行效果图

      .NET使用QuestPDF高效地生成PDF文档

      注意问题

      中文报异常

      QuestPDF.Drawing.Exceptions.DocumentDrawingException:“Could not find an appropriate font fallback for glyph: U-53D1 '发'. Font families available on current environment that contain this glyph: Microsoft JhengHei, Microsoft JhengHei UI, Microsoft YaHei, Microsoft YaHei UI, SimSun, NSimSun, DengXian, FangSong, KaiTi, SimHei, FZCuHeiSongS-B-GB. Possible solutions: 1) Use one of the listed fonts as the primary font in your document. 2) Configure the fallback TextStyle using the 'TextStyle.Fallback' method with one of the listed fonts. You can disable this check by setting the 'Settings.CheckIfAllTextGlyphsAreAvailable' option to 'false'. However, this may result with text glyphs being incorrectly rendered without any warning.”

      加上这段代码:

      .NET使用QuestPDF高效地生成PDF文档

      // 2、禁用QuestPDF库中文本字符可用性的检查
      Settings.CheckIfAllTextGlyphsAreAvailable = false;
      

      原因:

      默认情况下,使用 QuestPDF 生成 PDF 文档时,它会检查所使用的字体是否支持文本中的所有字符,并在发现不能显示的字符时输出一条警告消息。这个选项可以确保文本中的所有字符都能正确地显示在生成的 PDF 文件中。

      中文乱码问题

      .NET使用QuestPDF高效地生成PDF文档

      解决方案:

      假如Text("")中为汉字一定要在后面加上FontFamily("fangsong")[仿宋字体]或FontFamily("simhei")[黑体字体],否则中文无法正常显示。

      .NET使用QuestPDF高效地生成PDF文档

      以上就是.NET使用QuestPDF高效地生成PDF文档的详细内容,更多关于.NET QuestPDF生成PDF的资料请关注其它相关文章!

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。