c# 数据保存为PDF(三) (PdfSharp篇)

news/2024/10/16 23:25:36/

PdfSharp 使用,创建PDF文档

    • 前言
    • 关于 PdfSharp
    • 下载 PdfSharp
    • 使用PdfSharp
      • 常用命名空间和类
      • 1 创建一个简单的PDF文档
      • 2 创建一个带页脚的PDF文档
        • 2.1 创建临时数据表
        • 2.2 创建页脚
        • 2.3 完整代码
    • 小结
    • 附录:

在这里插入图片描述
往期文章:
1、 c# 数据保存为PDF(一) (Spire.PDF篇)
2、c# 数据保存为PDF(二) (Apose.Pdf篇)

前言

因为项目需求需要将数据导出存为PDF文档,方便客户使用。相比于TXT ,EXCEL等格式,导出PDF时可以减少字体乱码,文体格式不一样的痛点。在前面试用了Spire.PDFAspose.Pdf都可以导出我想要的格式的PDF,但这两个没有开源,正版都是要收费的,只好另寻他欢罢了。之后,寻寻觅觅,终于遇见PdfSharp,相处一段时间后,那便是日久生情了,陷进去了,从此就是它啦。

关于 PdfSharp

PdfSharp是一个开源免费的.NET开发包,可以生成PDF格式的文档。对于第三个特点真的特好使,深有体会,后面会有一篇博文介绍的。
PdfSharp组件主要特点有:

1.全部用C#重写设计和编写代码;
2.很容易使用对象模型来构建文档;
3. 可以生成PDF文件和显示在窗体或者打印,都使用同一个源文件;
4.可以修改、合并或者分割PDF文件;
5. 可以控制图片的透明度,嵌入字体。

下载 PdfSharp

通过VS自带的Nuget包管理器直接搜索PdfSharp并下载。路径在工具-----Nuget包管理器-----管理解决方案的NuGet程序包…
在这里插入图片描述

使用PdfSharp

常用命名空间和类

命名空间

using PdfSharp;
using PdfSharp.Drawing;
using PdfSharp.Pdf;

常用类库

类库描述
PdfSharp.Pdf.PdfDocumentPDF文档
PdfSharp.Pdf.PdfPagePDF 页面
PdfSharp.Drawing.XGraphics画布
PdfSharp.Drawing.XBrushes画刷
PdfSharp.Drawing.XPen画笔
PdfSharp.Drawing.XFont字体
PdfSharp.Drawing.XStringFormat字符串格式
PdfSharp.Drawing.XSize大小

1 创建一个简单的PDF文档

创建一个简单的HelloWorld.pdf文档。

       /// <summary>/// PdfSharp 创建一个简单的PDF/// </summary>public static void CreateSinglePdf(){// 创建一个PDF文档对象PdfDocument document = new PdfDocument();document.Info.Title = "Created with PDFsharp";// 创建一个空的PagePdfPage page = document.AddPage();// 创建一个画布对象,用于绘制XGraphics gfx = XGraphics.FromPdfPage(page);// 创建字体XFont font = new XFont("Verdana", 20, XFontStyle.BoldItalic);// 绘制文本gfx.DrawString("Hello, World!", font, XBrushes.Black,new XRect(0, 0, page.Width, page.Height),XStringFormats.Center);// 保存文档const string filename = "HelloWorld.pdf";document.Save(filename);}

在这里插入图片描述

2 创建一个带页脚的PDF文档

类似下图的模板:列数可以减少
分析需求:
1 有项目的头部介绍信息。
2 有页脚,页脚信息包括公司信息和页码。
3 表格内容有标题,换页到下页时标题也要有。
4 KK00 KK11 KK22等加粗有下划线,凸显出分类隔开的特点。
在这里插入图片描述

2.1 创建临时数据表

创建100*5的数据存放在DataTable中,仅用作测试。实际项目中,可以换成需要保存的数据内容。

        /// <summary>/// 数据表格/// </summary>/// <returns></returns>private static DataTable CreateData(){DataTable dt = new DataTable();DataColumn col1 = new DataColumn("Num", typeof(string));DataColumn col2 = new DataColumn("Name", typeof(string));DataColumn col3 = new DataColumn("Val", typeof(string));DataColumn col4 = new DataColumn("Des", typeof(string));DataColumn col5 = new DataColumn("Set", typeof(string));dt.Columns.Add(col1);dt.Columns.Add(col2);dt.Columns.Add(col3);dt.Columns.Add(col4);dt.Columns.Add(col5);Random random = new Random();List<string> nameList = new List<string>{"A", "BB", "CCC", "D","E", "F", "G","H","II","JJ", "LL", "M"};List<string> tempList = new List<string>{"dsd", "sdfdgvre", "Hello", "Gilrs","Today", "YYYY", "dfgre","GSD","fdgfer","Wesd", "DLG", "fsdahfi;o"};for (int i = 0; i < 10; i++){for (int j = 0; j < 10; j++){DataRow dr = dt.NewRow();dr[0] = "KK" + i.ToString("d2") + "." + j.ToString("d2");dr[1] = nameList[j];if (j % 3 == 0){dr[2] = random.NextDouble().ToString("f3");}else{dr[2] = i * j - random.Next(0, 30);}dr[3] = tempList[j];dr[4] = random.NextDouble().ToString("f2");//添加新行dt.Rows.Add(dr);}}return dt;}

2.2 创建页脚

一般包含公司信息和页码数。
在这里插入图片描述

        /// <summary>/// 绘制页脚/// </summary>/// <param name="page">页面</param>/// <param name="gfx">画布</param>/// <param name="cur">当前页序号</param>/// <param name="total">总的页码数</param>private static void DrawPDFFooter(PdfPage page, XGraphics gfx, int cur, int  total){//计算高度float endY = (float)page.Height - padding_topBottom;XFont Timefont = new XFont(fontName, 10, XFontStyle.Bold);//测量字符串大小XSize size = gfx.MeasureString("Drive Type/", Timefont);//绘制页脚gfx.DrawLine(new XPen(XColors.Black, 0.2f),padding_leftRight,endY,page.Width - padding_leftRight,endY);endY += 2 + (float)size.Height;//绘制信息String footerText = "Test for Windows(C) by 唠嗑一夏 Electric Corporation";gfx.DrawString(footerText, Timefont, XBrushes.Black, padding_leftRight,endY);Timefont = new XFont(fontName, 10, XFontStyle.Regular);//绘制页码数footerText = cur.ToString() + "/" + total.ToString();gfx.DrawString(footerText, Timefont, XBrushes.Black,(page.Width * 0.9), endY);}

2.3 完整代码

在这里调用测试数据、页脚函数,项目头和数据内容整合在一起了。

主要使用到GDI的绘制,一直在使用XGraphics 的绘制文本DrawString,绘制直线DrawLine,绘制矩形框DrawRectangle功能。涉及到页面高度,文本高度,文本宽度的计算,计算好了,绘制出来的效果才会好看,需要多尝试一下。
这里需要特别注意这个PdfSharp控件支持的字体,并不是所有字体都支持,如果字体不存在则内容会显示乱码。

        /// <summary>/// 创建PDF/// </summary>public static void CreatePdfSharpPDF( ){try{//获取测试数据DataTable dataTable = CreateData();//创建文档对象PdfDocument doc = new PdfDocument();//创建空页PdfPage page = doc.AddPage();//设置纸张大小page.Size = PageSize.A4;List<XGraphics> gfxList = new List<XGraphics>();//设置一个画布XGraphics gfx = XGraphics.FromPdfPage(page);gfxList.Add(gfx);const string fontName = "华文宋体";//设置字体XFont Titlefont = new XFont(fontName, 14, XFontStyle.Bold);XFont Timefont = new XFont(fontName, 12, XFontStyle.Regular);//绘制标题gfx.DrawString(" Parameter Settings Report(Program)", Titlefont, XBrushes.Black,new XRect(padding_leftRight, 30, page.Width - padding_leftRight, 30),XStringFormats.CenterLeft);//日期gfx.DrawString(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), Timefont, XBrushes.Black,new XRect(page.Width * 0.7, 30, page.Width * 0.3 - padding_leftRight, 30),XStringFormats.BottomRight);XPen linePen = new XPen(XColor.FromKnownColor(XKnownColor.Black), 0.1);//绘制线gfx.DrawLine(linePen,new XPoint(padding_leftRight, 60), new XPoint(page.Width - padding_leftRight, 60));//设置字体Titlefont = new XFont(fontName, 12, XFontStyle.Bold);Timefont = new XFont(fontName, 10, XFontStyle.Regular);//测量字体的大小XSize size = gfx.MeasureString("Program", Titlefont);XSize size2 = gfx.MeasureString("Drive Type/", Timefont);float y = 62;float endY = (float)size.Height + 4 + (float)size2.Height;//绘制矩形框和字符串gfx.DrawRectangle(XBrushes.LightCyan, new XRect(padding_leftRight, y, page.Width - 2 * padding_leftRight, endY));gfx.DrawString(" Program(Drive Selected / Connected)", Titlefont,XBrushes.Black, padding_leftRight, y, XStringFormat.TopLeft);y += 4 + (float)size.Height;gfx.DrawString(" Drive Type/Model: ", Timefont,XBrushes.Black, padding_leftRight, y, XStringFormat.TopLeft);y += 4 + (float)size2.Height;gfx.DrawLine(linePen,new XPoint(padding_leftRight, y), new XPoint(page.Width - padding_leftRight, y));y += 4;gfx.DrawString(" Project: ", Titlefont,XBrushes.Black, padding_leftRight, y, XStringFormat.TopLeft);y += 4 + (float)size.Height;gfx.DrawLine(linePen,new XPoint(padding_leftRight, y), new XPoint(page.Width - padding_leftRight, y));y += 4;Titlefont = new XFont(fontName, 11, XFontStyle.Bold);gfx.DrawString(" Information ", Titlefont,XBrushes.Black, padding_leftRight, y, XStringFormat.TopLeft);y += 100;//项目的标题y = DrawPDFCodeTitle(page, gfx, y) + 10;//绘制功能码的字体XFont funFont = new XFont(fontName, 9);XFont FunBoldFont = new XFont(fontName, 9, XFontStyle.Underline | XFontStyle.Bold);//字体的高度XSize funcSize = gfx.MeasureString("KK00.00", funFont);XSize funcBoldSize = gfx.MeasureString("KK00.00", FunBoldFont);int count = dataTable.Rows.Count;string str = "";string strBak = "";int j = 0;for (int i = 0; i < count; i++){//遍历数据if ((y + funcSize.Height + 3) > (page.Height - padding_topBottom)){//换页page = doc.Pages.Add();page.Size = PageSize.A4;gfx = XGraphics.FromPdfPage(page);gfxList.Add(gfx);//绘制内容表头y = DrawPDFCodeTitle(page, gfx, 10) + 10;}DataRow dataRow = dataTable.Rows[i];strBak = dataRow[0].ToString().Substring(0, 4);if (strBak != str){//绘制分组str = strBak;string converStr = strBak ;j++;funcBoldSize = gfx.MeasureString(converStr, FunBoldFont);y += (float)funcBoldSize.Height + 6;gfx.DrawString(converStr, FunBoldFont, XBrushes.Black, padding_leftRight, y);y += 2;i--;continue;}// else{//绘制内容string tempStr = dataRow[0].ToString() + " " + dataRow[1].ToString();funcSize = gfx.MeasureString(tempStr, funFont);y += (float)funcSize.Height + 3;if ((y + funcSize.Height + 3) > (page.Height - padding_topBottom)){//换页page = doc.Pages.Add();page.Size = PageSize.A4;gfx = XGraphics.FromPdfPage(page);gfxList.Add(gfx);//绘制内容表头y = DrawPDFCodeTitle(page, gfx, 10) + 10;}//序号+描述float widthX = padding_leftRight;gfx.DrawString(tempStr, funFont, XBrushes.Black, widthX, y);//当前值widthX += (float)(page.Width * 0.32);gfx.DrawString(dataRow[2].ToString(), funFont, XBrushes.Black, widthX, y);//设置值widthX += (float)(page.Width * 0.31);gfx.DrawString(dataRow[4].ToString(), funFont, XBrushes.Black, widthX, y);}}gfx = null;int Total = doc.PageCount;for (int i = 0; i < Total; i++){//绘制页脚DrawPDFFooter(page, gfxList[i], i + 1, Total);}string path = "PdfSharpDemo.pdf";doc.Save(path);}catch (Exception ex){}          }

效果图:也可以看文章最前面的动图
在这里插入图片描述

小结

PdfSharp创建PDF文档使用的是Drawing方式,用户可以通过绘制来实现自己各式各样的需求
而且PdfSharp是免费开源的,这就秒杀了一大波其它付费的控件了。与Aspose.Pdf相比,PdfSharp生成PDF文件的速度快多了。

附录:

PdfSharp官网链接:http://www.pdfsharp.com/PDFsharp/
PdfSharp的使用Demo:http://www.pdfsharp.net/wiki/PDFsharpSamples.ashx?AspxAutoDetectCookieSupport=1


http://www.ppmy.cn/news/59446.html

相关文章

C++---区间DP---棋盘分割(每日一道算法2023.5.2)

注意事项&#xff1a; 涉及到"矩阵/二维前缀和"的一些知识&#xff0c;建议先理解那篇文章。 题目&#xff1a; 将一个 88 的棋盘进行如下分割&#xff1a;将原棋盘割下一块矩形棋盘并使剩下部分也是矩形&#xff0c;再将剩下的部分继续如此分割&#xff0c;这样割了…

Jmeter接口自动化测试系列之Http接口自动化实战

以下主要介绍Jmeter接口自动化需要哪些控件、接口自动化实战及总结。 前面的系列文章&#xff0c;介绍了常用组件、参数化、接口依赖、断言等知识点&#xff0c;今天我们要将这些结合起来&#xff0c;进行综合实战。 2023年B站最新Jmeter接口测试实战教程&#xff0c;精通接口…

一篇SEO指南:新手如何从零开始优化自己的网站

在如今的数字时代&#xff0c;拥有一个优化良好的网站对于任何企业或个人来说都是至关重要的。但是&#xff0c;对于SEO新手来说&#xff0c;如何从零开始优化自己的网站可能是一项看似艰巨的任务。在本文中&#xff0c;我们将为您提供一份SEO指南&#xff0c;帮助您了解从零开…

C# 开发中易忽略的错误细节及解决方案

在C#编程过程中&#xff0c;可能会遇到一些容易被忽略的错误细节&#xff0c;这些错误可能会导致程序出现问题或者难以调试。下面列举了一些常见的容易被忽略的错误细节&#xff0c;并提供了相应的例子&#xff0c;希望对读者有所帮助。 1.对象的空引用 在C#中&#xff0c;如…

谷粒商城二十四Sentinel限流熔断降级

我们在秒杀服务加的以上所有手段都是为了快&#xff0c;除了快之外&#xff0c;我们还需要保证稳定。 我们即使再快也会有一个极限值&#xff0c;现在假设单机下每秒处理一万个单&#xff0c;这已经是超高的处理能力了&#xff0c;秒杀服务上了五台服务器&#xff0c;有三台掉…

整理张量(tensor)中常用的操作

维度变换 1、创建张量 torch.zeros(*sizes, outNone, …)# 返回大小为sizes的零矩阵 torch.ones(*sizes, outNone, …) #f返回大小为sizes的单位矩阵 2、张量在某个维度上的的加减乘除 加&#xff1a;torch.sum() 用于计算张量在某个维度上的元素之和&#xff0c;返回一个新…

Linux 动态/静态配置ip网卡信息

配置网卡 第一步&#xff1a;查看网卡信息 查看网卡信息 在配置网卡之前&#xff0c;首先需要查看网卡信息。以下是在不同Linux发行版上查看网卡信息的方法&#xff1a; 方法一&#xff1a;使用ifconfig命令 输入ifconfig命令查看网卡信息。此命令适用于大多数Linux发行版&…

浙大数据结构第三周初识二叉树

03-树1 树的同构 (25分) 给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2&#xff0c;则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的&#xff0c;因为我们把其中一棵树的结点A、B、G的左右孩子互换后&#xff0c;就得到另外一棵树。而图2就不是同构…