C#/.Net 使用QuestPDF操作生成PDF更快更高效

QuestPDF

QuestPDF是一个开源的工具库,可以在.NET或者.Net Core中生成pdf文档

它提供了一个布局引擎,设计时考虑到了完整的分页支持以及灵活性要求!比市面上常见的Aspose和iTextSharp好用太多了!GitHub地址

安装

Install-Package QuestPDF

例子

  • 简单例子

生成Pdf文档一共分为三部分,Header(页眉),Content(内容),Footer(页脚)

Document.Create(container =>
{
    container.Page(page =>
    {
        page.Size(PageSizes.A4);
        page.Margin(2, Unit.Centimetre);
        page.Background(Colors.White);
        page.DefaultTextStyle(x => x.FontSize(20));
        
        page.Header()
            .Text("Hello PDF!")
            .SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
        
        page.Content()
            .PaddingVertical(1, Unit.Centimetre)
            .Column(x =>
            {
                x.Spacing(20);
                
                x.Item().Text(Placeholders.LoremIpsum());
                x.Item().Image(Placeholders.Image(200, 100));
            });
        
        page.Footer()
            .AlignCenter()
            .Text(x =>
            {
                x.Span("Page ");
                x.CurrentPageNumber();
            });
    });
})
.GeneratePdf("hello.pdf");

  • 模板生成

使用模板生成一共设计三个应用层的工作:

  • 文档Model(一组描述 PDF 文档内容的类)

  • 数据源(将域实体映射到文档模型的层)

  • 模板(描述如何可视化信息并将其转换为 PDF 文件的表示层)

比如我们设计一个基本的发票信息 要设计一个购物清单,一级卖家买家的地址,以及发票编号等等 我们设计这样的3个Model类

   public class InvoiceModel
    {
        public int InvoiceNumber { get; set; }
        public DateTime IssueDate { get; set; }
        public DateTime DueDate { get; set; }

        public Address SellerAddress { get; set; }
        public Address CustomerAddress { get; set; }

        public List<OrderItem> Items { get; set; }
        public string Comments { get; set; }
    }

    public class OrderItem
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
        public int Quantity { get; set; }
    }

    public class Address
    {
        public string CompanyName { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public object Email { get; set; }
        public string Phone { get; set; }
    }

Model定义好了之后我们就定义一些假数据来填充pdf

 public static class InvoiceDocumentDataSource
    {
        private static Random Random = new Random();

        public static InvoiceModel GetInvoiceDetails()
        {
            var items = Enumerable
                .Range(1, 8)
                .Select(i => GenerateRandomOrderItem())
                .ToList();

            return new InvoiceModel
            {
                InvoiceNumber = Random.Next(1_000, 10_000),
                IssueDate = DateTime.Now,
                DueDate = DateTime.Now + TimeSpan.FromDays(14),

                SellerAddress = GenerateRandomAddress(),
                CustomerAddress = GenerateRandomAddress(),

                Items = items,
                Comments ="测试备注"
            };
        }

        private static OrderItem GenerateRandomOrderItem()
        {
            return new OrderItem
            {
                Name = "商品",
                Price = (decimal)Math.Round(Random.NextDouble() * 100, 2),
                Quantity = Random.Next(1, 10)
            };
        }

        private static Address GenerateRandomAddress()
        {
            return new Address
            {
                CompanyName = "测试商店",
                Street = "测试街道",
                City = "测试城市",
                State = "测试状态",
                Email = "测试邮件",
                Phone = "测试电话"
            };
        }
    }

然后搭建我们的模板脚手架 我们要使用模板脚手架,就要定义一个实现IDocument接口的新类开始。该接口包含两个方法

  • DocumentMetadata GetMetadata();

  • void Compose(IDocumentContainer container);

第一个是模板文档的一些基础信息 第二个是模板的容器 基于这些原则我们设计一个模板层类

 public class InvoiceDocument : IDocument
    {
        public InvoiceModel Model { get; }

        public InvoiceDocument(InvoiceModel model)
        {
            Model = model;
        }

        public DocumentMetadata GetMetadata() => DocumentMetadata.Default;

        public void Compose(IDocumentContainer container)
        {
            container
                .Page(page =>
                {
                    page.PageColor(Colors.Red.Lighten1);
                    page.Size(PageSizes.A4);
                    page.Margin(10);//外边距

      
                    page.Header().Height(100).Background(Colors.LightBlue.Lighten1);
                    page.Content().Background(Colors.Grey.Lighten3);
                    page.Footer().Height(50).Background(Colors.Grey.Lighten1);
                });
        }
}

pdf的page页面总是有三个元素:页眉,页脚,内容。查看一下我们生成的文档

到目前为止,我们已经搭建了一个非常简单的页面,其中每个部分都有不同的颜色或大小

接下来我们来填充他的页眉,我们把数据源整理好了之后,就可以调用Element方法填充

 public void Compose(IDocumentContainer container)
        {
            container
                .Page(page =>
                {
                    page.PageColor(Colors.Red.Lighten1);
                    page.Size(PageSizes.A4);
                    page.Margin(10);//外边距

      
                    page.Header().Height(100).Background(Colors.LightBlue.Lighten1).Element(ComposeHeader);
                    page.Content().Background(Colors.Grey.Lighten3);
                    page.Footer().Height(50).Background(Colors.Grey.Lighten1);
                });
        }


        void ComposeHeader(IContainer container)
        {
            var titleStyle = TextStyle.Default.FontSize(20).SemiBold().FontColor(Colors.Blue.Medium);

            container.Row(row =>
            {
                row.RelativeItem().Column(column =>
                {
                    column.Item().Text($"发票 #{Model.InvoiceNumber}").FontFamily("simhei").Style(titleStyle);

                    column.Item().Text(text =>
                    {
                        text.Span("发行日期: ").SemiBold().FontFamily("simhei");
                        text.Span($"{Model.IssueDate:d}").FontFamily("simhei");
                    });

                    column.Item().Text(text =>
                    {
                        text.Span("支付日期: ").FontFamily("simhei").SemiBold();
                        text.Span($"{Model.DueDate:d}").FontFamily("simhei");
                    });

                })
                ;

            });
        }


最后我们来实现内容,

  public void Compose(IDocumentContainer container)
        {
            container
                .Page(page =>
                {
                    page.PageColor(Colors.Red.Lighten1);
                    page.Size(PageSizes.A4);
                    page.Margin(10);//外边距

      
                    page.Header().Height(100).Background(Colors.LightBlue.Lighten1).Element(ComposeHeader);
                    page.Content().Background(Colors.Grey.Lighten3).Element(ComposeContent);
                    page.Footer().Height(50).Background(Colors.Grey.Lighten1);
                });
        }


        void ComposeHeader(IContainer container)
        {
            var titleStyle = TextStyle.Default.FontSize(20).SemiBold().FontColor(Colors.Blue.Medium);

            container.Row(row =>
            {
                row.RelativeItem().Column(column =>
                {
                    column.Item().Text($"发票 #{Model.InvoiceNumber}").FontFamily("simhei").Style(titleStyle);

                    column.Item().Text(text =>
                    {
                        text.Span("发行日期: ").SemiBold().FontFamily("simhei");
                        text.Span($"{Model.IssueDate:d}").FontFamily("simhei");
                    });

                    column.Item().Text(text =>
                    {
                        text.Span("支付日期: ").FontFamily("simhei").SemiBold();
                        text.Span($"{Model.DueDate:d}").FontFamily("simhei");
                    });

                })
                ;

            });
        }

        void ComposeContent(IContainer container)
        {
            container.Table(table =>
            {
                // step 1
                table.ColumnsDefinition(columns =>
                {
                    columns.ConstantColumn(25);
                    columns.RelativeColumn(3);
                    columns.RelativeColumn();
                    columns.RelativeColumn();
                    columns.RelativeColumn();
                });

                // step 2
                table.Header(header =>
                {
                    header.Cell().Text("#").FontFamily("simhei");
                    header.Cell().Text("商品").FontFamily("simhei");
                    header.Cell().AlignRight().Text("价格").FontFamily("simhei");
                    header.Cell().AlignRight().Text("数量").FontFamily("simhei");
                    header.Cell().AlignRight().Text("总价").FontFamily("simhei");

                    header.Cell().ColumnSpan(5)
                        .PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black);
                });

                // step 3
                foreach (var item in Model.Items)
                {
                    table.Cell().Element(CellStyle).Text(Model.Items.IndexOf(item) + 1).FontFamily("simhei");
                    table.Cell().Element(CellStyle).Text(item.Name).FontFamily("simhei");
                    table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price}$").FontFamily("simhei");
                    table.Cell().Element(CellStyle).AlignRight().Text(item.Quantity).FontFamily("simhei");
                    table.Cell().Element(CellStyle).AlignRight().Text($"{item.Price * item.Quantity}$").FontFamily("simhei");

                    static IContainer CellStyle(IContainer container)
                    {
                        return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5);
                    }
                }
            });

        }
       

在这些准备工作做完了之后我们就可以生成Pdf文档了

     var filePath = "invoice.pdf";

            var model = InvoiceDocumentDataSource.GetInvoiceDetails();
            var document = new InvoiceDocument(model);
            document.GeneratePdf(filePath);

当然还有很多好玩的功能,今天就给大家讲个概念,让大家对这个东西有个印象,后面我会继续输出该库的相关功能。如果你们对该库感兴趣,可以持续关注我!

微信公众号【黑哥聊dotNet】

B站:【哥聊编程】

C#实现生成PDF文档(附源码) 收藏 //write by wenhui.org using System; using System.IO; using System.Text; using System.Collections; namespace PDFGenerator { public class PDFGenerator { static float pageWidth = 594.0f; static float pageDepth = 828.0f; static float pageMargin = 30.0f; static float fontSize = 20.0f; static float leadSize = 10.0f; static StreamWriter pPDF=new StreamWriter("E:\myPDF.pdf"); static MemoryStream mPDF= new MemoryStream(); static void ConvertToByteAndAddtoStream(string strMsg) { Byte[] buffer=null; buffer=ASCIIEncoding.ASCII.GetBytes(strMsg); mPDF.Write(buffer,0,buffer.Length); buffer=null; } static string xRefFormatting(long xValue) { string strMsg =xValue.ToString(); int iLen=strMsg.Length; if (iLen<10) { StringBuilder s=new StringBuilder(); int i=10-iLen; s.Append('0',i); strMsg=s.ToString() + strMsg; } return strMsg; } static void Main(string[] args) { ArrayList xRefs=new ArrayList(); //Byte[] buffer=null; float yPos =0f; long streamStart=0; long streamEnd=0; long streamLen =0; string strPDFMessage=null; //PDF文档头信息 strPDFMessage="%PDF-1.1 "; ConvertToByteAndAddtoStream(strPDFMessage); xRefs.Add(mPDF.Length); strPDFMessage="1 0 obj "; ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage="<> "; ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage="stream "; ConvertToByteAndAddtoStream(strPDFMessage); ////////PDF文档描述 streamStart=mPDF.Length; //字体 strPDFMessage="BT /F0 " + fontSize +" Tf "; ConvertToByteAndAddtoStream(strPDFMessage); //PDF文档实体高度 yPos = pageDepth - pageMargin; strPDFMessage=pageMargin + " " + yPos +" Td " ; ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage= leadSize+" TL " ; ConvertToByteAndAddtoStream(strPDFMessage); //实体内容 strPDFMessage= "(http://www.wenhui.org)Tj " ; ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage= "ET "; ConvertToByteAndAddtoStream(strPDFMessage); streamEnd=mPDF.Length; streamLen=streamEnd-streamStart; strPDFMessage= "endstream endobj "; ConvertToByteAndAddtoStream(strPDFMessage); //PDF文档的版本信息 xRefs.Add(mPDF.Length); strPDFMessage="2 0 obj "+ streamLen + " endobj "; ConvertToByteAndAddtoStream(strPDFMessage); xRefs.Add(mPDF.Length); strPDFMessage="3 0 obj <> endobj "; ConvertToByteAndAddtoStream(strPDFMessage); xRefs.Add(mPDF.Length); strPDFMessage="4 0 obj <</Type /Pages /Count 1 "; ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage="/Kids[ 3 0 R ] "; ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage="/Resources<</ProcSet[/PDF/Text]/Font<> >> "; ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage="/MediaBox [ 0 0 "+ pageWidth + " " + pageDepth + " ] >> endobj "; ConvertToByteAndAddtoStream(strPDFMessage); xRefs.Add(mPDF.Length); strPDFMessage="5 0 obj <> endobj "; ConvertToByteAndAddtoStream(strPDFMessage); xRefs.Add(mPDF.Length); strPDFMessage="6 0 obj <> endobj "; ConvertToByteAndAddtoStream(strPDFMessage); streamStart=mPDF.Length; strPDFMessage="xref 0 7 0000000000 65535 f "; for(int i=0;i<xRefs.Count;i++) { strPDFMessage+=xRefFormatting((long) xRefs[i])+" 00000 n "; } ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage="trailer <> "; ConvertToByteAndAddtoStream(strPDFMessage); strPDFMessage="startxref " + streamStart+" %%EOF "; ConvertToByteAndAddtoStream(strPDFMessage); mPDF.WriteTo(pPDF.BaseStream); mPDF.Close(); pPDF.Close(); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值