.NET使用QuestPDF高效地生成PDF文檔
前言
在.NET平臺(tái)中操作生成PDF的類庫(kù)有很多如常見(jiàn)的有iTextSharp、PDFsharp、Aspose.PDF等,今天我們分享一個(gè)用于生成PDF文檔的現(xiàn)代開(kāi)源.NET庫(kù):QuestPDF,本文將介紹QuestPDF并使用它快速實(shí)現(xiàn)發(fā)票P(pán)DF文檔生成功能。

QuestPDF介紹
QuestPDF 是一個(gè)用于生成 PDF 文檔的現(xiàn)代開(kāi)源 .NET 庫(kù)。QuestPDF 由簡(jiǎn)潔易用的 C# Fluent API 提供全面的布局引擎。輕松生成 PDF 報(bào)告、發(fā)票、導(dǎo)出等。QuestPDF它提供了一個(gè)布局引擎,在設(shè)計(jì)時(shí)考慮了完整的分頁(yè)支持。與其他庫(kù)不同,它不依賴于 HTML 到 PDF 的轉(zhuǎn)換,這在許多情況下是不可靠的。相反,它實(shí)現(xiàn)了自己的布局引擎,該引擎經(jīng)過(guò)優(yōu)化,可以滿足所有與分頁(yè)相關(guān)的要求。
QuestPDF License
分為社區(qū)版、專業(yè)版、和企業(yè)版。

項(xiàng)目源代碼

創(chuàng)建一個(gè)控制臺(tái)應(yīng)用
創(chuàng)建一個(gè)名為QuestPDFTest的控制臺(tái)應(yīng)用。


安裝QuestPDF Nuget包
搜索:QuestPDF包進(jìn)行安裝。

快速實(shí)現(xiàn)發(fā)票P(pán)DF文檔生成
創(chuàng)建InvoiceModel
namespace QuestPDFTest
{
public class InvoiceModel
{
/// <summary>
/// 發(fā)票號(hào)碼
/// </summary>
public int InvoiceNumber { get; set; }
/// <summary>
/// 發(fā)票開(kāi)具日期
/// </summary>
public DateTime IssueDate { get; set; }
/// <summary>
/// 發(fā)票到期日期
/// </summary>
public DateTime DueDate { get; set; }
/// <summary>
/// 賣(mài)方公司名稱
/// </summary>
public string SellerCompanyName { get; set; }
/// <summary>
/// 買(mǎi)方公司名稱
/// </summary>
public string CustomerCompanyName { get; set; }
/// <summary>
/// 訂單消費(fèi)列表
/// </summary>
public List<OrderItem> OrderItems { get; set; }
/// <summary>
/// 備注
/// </summary>
public string Comments { get; set; }
}
public class OrderItem
{
/// <summary>
/// 消費(fèi)類型
/// </summary>
public string Name { get; set; }
/// <summary>
/// 消費(fèi)金額
/// </summary>
public decimal Price { get; set; }
/// <summary>
/// 消費(fèi)數(shù)量
/// </summary>
public int Quantity { get; set; }
}
}
CreateInvoiceDetails
namespace QuestPDFTest
{
public class CreateInvoiceDetails
{
private static readonly Random _random = new Random();
public enum InvoiceType
{
餐飲費(fèi),
交通費(fèi),
住宿費(fèi),
日用品,
娛樂(lè)費(fèi),
醫(yī)療費(fèi),
通訊費(fèi),
教育費(fèi),
裝修費(fèi),
旅游費(fèi)
}
/// <summary>
/// 獲取發(fā)票詳情數(shù)據(jù)
/// </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 = "追逐時(shí)光者",
CustomerCompanyName = "DotNetGuide技術(shù)社區(qū)",
OrderItems = Enumerable
.Range(1, 20)
.Select(_ => GenerateRandomOrderItemInfo())
.ToList(),
Comments = "DotNetGuide技術(shù)社區(qū)是一個(gè)面向.NET開(kāi)發(fā)者的開(kāi)源技術(shù)社區(qū),旨在為開(kāi)發(fā)者們提供全面的C#/.NET/.NET Core相關(guān)學(xué)習(xí)資料、技術(shù)分享和咨詢、項(xiàng)目推薦、招聘資訊和解決問(wèn)題的平臺(tái)。在這個(gè)社區(qū)中,開(kāi)發(fā)者們可以分享自己的技術(shù)文章、項(xiàng)目經(jīng)驗(yàn)、遇到的疑難技術(shù)問(wèn)題以及解決方案,并且還有機(jī)會(huì)結(jié)識(shí)志同道合的開(kāi)發(fā)者。我們致力于構(gòu)建一個(gè)積極向上、和諧友善的.NET技術(shù)交流平臺(tái),為廣大.NET開(kāi)發(fā)者帶來(lái)更多的價(jià)值和成長(zhǎng)機(jī)會(huì)。"
};
}
/// <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對(duì)象
/// </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 =>
{
//設(shè)置頁(yè)面的邊距
page.Margin(50);
//字體默認(rèn)大小18號(hào)字體
page.DefaultTextStyle(x => x.FontSize(18));
//頁(yè)眉部分
page.Header().Element(BuildHeaderInfo);
//內(nèi)容部分
page.Content().Element(BuildContentInfo);
//頁(yè)腳部分
page.Footer().AlignCenter().Text(text =>
{
text.CurrentPageNumber();
text.Span(" / ");
text.TotalPages();
});
});
}
#region 構(gòu)建頁(yè)眉部分
void BuildHeaderInfo(IContainer container)
{
container.Row(row =>
{
row.RelativeItem().Column(column =>
{
column.Item().Text($"發(fā)票編號(hào) #{Model.InvoiceNumber}").FontFamily("fangsong").FontSize(20).SemiBold().FontColor(Colors.Blue.Medium);
column.Item().Text(text =>
{
text.Span("發(fā)行日期: ").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}");
});
});
//在當(dāng)前行的常量項(xiàng)中插入一個(gè)圖像
row.ConstantItem(130).Image(LogoImage);
});
}
#endregion
#region 構(gòu)建內(nèi)容部分
void BuildContentInfo(IContainer container)
{
container.PaddingVertical(40).Column(column =>
{
column.Spacing(20);
column.Item().Row(row =>
{
row.RelativeItem().Component(new AddressComponent("賣(mài)方公司名稱", 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($"總計(jì): {totalPrice}").FontFamily("fangsong").SemiBold();
if (!string.IsNullOrWhiteSpace(Model.Comments))
column.Item().PaddingTop(25).Element(BuildComments);
});
}
/// <summary>
/// 創(chuàng)建表格
/// </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("消費(fèi)類型").Style(headerStyle).FontFamily("fangsong");
header.Cell().AlignRight().Text("花費(fèi)金額").Style(headerStyle).FontFamily("fangsong");
header.Cell().AlignRight().Text("數(shù)量").Style(headerStyle).FontFamily("fangsong");
header.Cell().AlignRight().Text("總金額").Style(headerStyle).FontFamily("fangsong");
//設(shè)置了表頭單元格的屬性
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 構(gòu)建頁(yè)腳部分
void BuildComments(IContainer container)
{
container.ShowEntire().Background(Colors.Grey.Lighten3).Padding(10).Column(column =>
{
column.Spacing(5);
column.Item().Text("DotNetGuide技術(shù)社區(qū)介紹").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、請(qǐng)確保您有資格使用社區(qū)許可證,不設(shè)置的話會(huì)報(bào)異常。
Settings.License = LicenseType.Community;
// 2、禁用QuestPDF庫(kù)中文本字符可用性的檢查
Settings.CheckIfAllTextGlyphsAreAvailable = false;
// 3、PDF Document 創(chuàng)建
var invoiceSourceData = CreateInvoiceDetails.GetInvoiceDetails();
var document = new CreateInvoiceDocument(invoiceSourceData);
// 4、生成 PDF 文件并在默認(rèn)的查看器中顯示
document.GeneratePdfAndShow();
}
}
}
完整示例源代碼
https://github.com/YSGStudyHards/QuestPDFTest

示例運(yùn)行效果圖

注意問(wèn)題
中文報(bào)異常
QuestPDF.Drawing.Exceptions.DocumentDrawingException:“Could not find an appropriate font fallback for glyph: U-53D1 '發(fā)'. 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.”
加上這段代碼:

// 2、禁用QuestPDF庫(kù)中文本字符可用性的檢查 Settings.CheckIfAllTextGlyphsAreAvailable = false;
原因:
默認(rèn)情況下,使用 QuestPDF 生成 PDF 文檔時(shí),它會(huì)檢查所使用的字體是否支持文本中的所有字符,并在發(fā)現(xiàn)不能顯示的字符時(shí)輸出一條警告消息。這個(gè)選項(xiàng)可以確保文本中的所有字符都能正確地顯示在生成的 PDF 文件中。
中文亂碼問(wèn)題

解決方案:
假如Text("")中為漢字一定要在后面加上FontFamily("fangsong")[仿宋字體]或FontFamily("simhei")[黑體字體],否則中文無(wú)法正常顯示。

以上就是.NET使用QuestPDF高效地生成PDF文檔的詳細(xì)內(nèi)容,更多關(guān)于.NET QuestPDF生成PDF的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
云服務(wù)器下搭建ASP.NET Core環(huán)境
本文給大家分享的是在云服務(wù)器上搭建ASP.NET Core環(huán)境以及成功運(yùn)行官網(wǎng)DEMO的教程,十分的細(xì)致全面,有需要的小伙伴可以參考下。2016-07-07
.net?core?api接口JWT方式認(rèn)證Token
本文詳細(xì)講解了.net?core?api接口JWT方式認(rèn)證Token,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12
ASP.NET 水晶報(bào)表打印功能實(shí)現(xiàn)代碼
ASP.NET下的水晶報(bào)表打印,據(jù)我所知有以下幾種辦法可以打印2008-11-11
asp.net微信開(kāi)發(fā)(自定義會(huì)話管理)
這篇文章主要介紹了asp.net微信開(kāi)發(fā)中有關(guān)自定義會(huì)話管理的相關(guān)內(nèi)容,需要的朋友可以參考下2015-11-11
asp.net Context.Handler 頁(yè)面間傳值方法
很有用的頁(yè)面間傳值方法(Context.Handler),使用說(shuō)明2008-08-08
ASP.NET中RadioButtonList綁定后臺(tái)數(shù)據(jù)后觸發(fā)點(diǎn)擊事件
這篇文章主要介紹了ASP.NET中RadioButtonList綁定后臺(tái)數(shù)據(jù)后觸發(fā)點(diǎn)擊事件的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-05-05
解決.Net Core項(xiàng)目發(fā)布在IIS上訪問(wèn)404的問(wèn)題
這篇文章介紹了解決.Net Core項(xiàng)目發(fā)布在IIS上訪問(wèn)404的問(wèn)題,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12

