简介:这份资源是面向C# WinForm初学者与.NET桌面开发者的ItextSharp PDF导出实战示例,重点解决在WinForm应用中把文本框数据快速生成PDF文件的入门问题。压缩包共53个文件,约4.17MB,以cs源码、exe可执行程序、dll依赖库、config配置、resources资源及resx设计文件为主,另含sln解决方案、csproj工程文件与一份示例pdf,可直接在Visual Studio中打开运行,便于对照调试。示例围绕Document、PdfWriter、Paragraph与Chunk等核心API展开,演示了字体设置、内容写入与文档关闭的完整流程,并指出导入已有PDF与新建PDF的常见误区,帮助读者理解ItextSharp的基本用法。目前已有1226人学习下载,适合希望快速掌握C#导出PDF基础概念、并在此基础上扩展图片、表格、页眉页脚等复杂元素的开发者参考。
1. 从一份 C# WinForm 的 PDF 导出 Demo 说起:iText 到底能帮你省下多少事
做过 WinForm 桌面项目的人大概率都遇到过这个需求:界面上跑完一轮业务,用户点一下按钮,要把结果导成一份 PDF 存档或者发给别人。有人第一反应是调 Office 组件,结果部署到客户机器上因为没装 Office 直接翻车;有人用系统自带的打印组件虚拟打印,排版一塌糊涂还依赖打印机驱动。这份 C# WinForm + iText 实现 PDF 导出的简单 Demo,走的就是另一条路——用纯托管库在代码里直接拼出 PDF,不依赖任何外部办公软件,也不吃打印机驱动。它解决的核心问题就一个:在 WinForm 里把内存中的数据或界面上的内容,稳定地落成一份能打开、能打印、排版可控的 PDF 文件。适合谁?适合正在做桌面端报表导出、单据打印、数据存档的 C# 开发者,尤其是那些被 Office 依赖和打印驱动折腾过的人。iText 这套东西在 Java 圈早就烂大街,搬到 .NET 这边,思路完全一致,只是 API 名字换了个皮。
2. iText 在 .NET 里的选型与最小可跑环境:别在版本上栽跟头
2.1 为什么是 iText,而不是 PrintDocument 或 Office Interop
先把选型这件事说清楚,不然写到一半发现方向错了,返工成本很高。WinForm 里导出 PDF,常见做法无非三条路:一是用System.Drawing.Printing.PrintDocument配合虚拟打印机,二是调 Office Interop 走 Word/Excel 再另存为 PDF,三是用第三方 PDF 库直接生成。第一条路的问题在于它本质是"打印",排版受打印机驱动和纸张设置影响,换台机器效果可能就不一样,而且用户机器上不一定有 PDF 虚拟打印机。第二条路更重,要求目标机器装了 Office,版本还得对得上,部署和授权都是坑。第三条路就是 iText 这类库的战场——纯代码控制,输出确定,不依赖外部软件。
iText 在 .NET 生态里有两个主要分支需要区分。早期的是 iTextSharp,命名空间是iTextSharp.text,对应 iText 5.x 的 .NET 移植;后来官方统一成 iText 7,包名变成itext7,命名空间是iText.Kernel.Pdf、iText.Layout这一套,API 重新设计过,链式调用更顺手。这份 Demo 标题里写的是 "Itext",没有明确版本,但从"简单 demo"的定位看,两种写法都值得知道。我一般建议新项目直接上 iText 7,老项目维护就沿用 iTextSharp,别混着来,因为两者的类名和方法签名差异不小,混用会编译不过。
提示:iText 7 的社区版是 AGPL 协议,商业闭源项目使用需要留意授权问题,这一点在选型阶段就要确认,别等上线了才发现。
2.2 用 NuGet 把依赖装进来,跑通第一个 PDF
环境准备没什么玄学,Visual Studio 建一个 WinForm 项目,然后通过 NuGet 装包。iText 7 的核心包是itext7,如果要处理中文还得考虑字体包。命令行方式如下:
# 在项目目录下执行,安装 iText 7 核心库 dotnet add package itext7 # 如果项目还是老的 packages.config 管理方式,用这行 Install-Package itext7装完之后,先别急着往 WinForm 按钮里塞逻辑,写一个控制台能跑的最小例子验证环境,这一步能帮你排除掉大部分"库没装对"的问题:
using iText.Kernel.Pdf; using iText.Layout; using iText.Layout.Element; // 最简单的 PDF 生成:创建文档、写一段文字、关闭 using (var writer = new PdfWriter("test.pdf")) using (var pdf = new PdfDocument(writer)) using (var document = new Document(pdf)) { // Add 一个段落,iText 7 里 Paragraph 属于 Layout 层 document.Add(new Paragraph("Hello iText 7")); }这段代码的逻辑很直白:PdfWriter负责把字节写到文件,PdfDocument代表 PDF 文档对象,Document是更高层的排版容器。三层各司其职,using保证资源释放,尤其是PdfDocument不关闭的话文件会损坏打不开,这是新手最容易踩的坑之一。参数上,PdfWriter的构造函数接收文件路径或Stream,如果你要导出到内存再让用户下载,就传MemoryStream。跑通这一步,说明库和环境都没问题,接下来才是往 WinForm 里集成。
3. 把数据落到 PDF 上:段落、表格与中文字体的实战写法
3.1 段落与表格:把界面数据组织成 PDF 内容
Demo 级别的导出,内容无非是标题、几行文字、一张数据表格。iText 7 里对应的元素是Paragraph、Table、Cell。假设 WinForm 上有个DataGridView,要把它的数据导成表格,常见写法是这样:
using iText.Kernel.Pdf; using iText.Layout; using iText.Layout.Element; using iText.Layout.Properties; using (var writer = new PdfWriter("export.pdf")) using (var pdf = new PdfDocument(writer)) using (var document = new Document(pdf)) { // 标题段落,设置居中和字号 var title = new Paragraph("数据导出报表") .SetTextAlignment(TextAlignment.CENTER) .SetFontSize(18); document.Add(title); // 创建表格,列数取 DataGridView 的列数 int colCount = dataGridView1.Columns.Count; var table = new Table(UnitValue.CreatePercentArray(colCount)) .UseAllAvailableWidth(); // 表头 foreach (DataGridViewColumn col in dataGridView1.Columns) { table.AddHeaderCell(new Cell().Add(new Paragraph(col.HeaderText))); } // 数据行 foreach (DataGridViewRow row in dataGridView1.Rows) { if (row.IsNewRow) continue; // 跳过 DataGridView 末尾的空行 foreach (DataGridViewCell cell in row.Cells) { string text = cell.Value?.ToString() ?? ""; table.AddCell(new Cell().Add(new Paragraph(text))); } } document.Add(table); }逻辑说明:UnitValue.CreatePercentArray让每列按百分比平分宽度,UseAllAvailableWidth让表格撑满页面可用宽度,这两个搭配起来表格不会缩成一小坨。AddHeaderCell和AddCell的区别在于表头会在跨页时重复,数据行不会。参数上,SetFontSize、SetTextAlignment都是链式返回自身,可以连着写。这里有个容易忽略的点:DataGridView.Rows在允许用户新增行时会包含一个IsNewRow为 true 的空行,不跳过的话 PDF 末尾会多一行空白,这种细节在 Demo 里不写,实际用起来就会被用户反馈"导出的表多了一行"。
3.2 中文字体:不设置就是一堆方块
上面那段代码如果直接跑,中文大概率显示成方块或者干脆不显示。原因是 iText 默认用的字体不含中文字形。解决办法是显式指定一个支持中文的字体文件,常见做法是用系统里的宋体或黑体:
using iText.Kernel.Font; using iText.IO.Font; // 从系统字体目录加载中文字体,注意路径要真实存在 string fontPath = @"C:\Windows\Fonts\simsun.ttc"; PdfFont font = PdfFontFactory.CreateFont(fontPath, PdfEncodings.IDENTITY_H); // 把这个字体应用到文档默认样式上 document.SetFont(font);PdfEncodings.IDENTITY_H这个编码参数是关键,它支持 Unicode 字符集,中文才能正确映射。如果换成默认编码,中文会丢。字体路径这块有个部署坑:开发机上有simsun.ttc,客户机器上不一定有,或者路径不一样。稳妥的做法是把字体文件随程序一起打包,用相对路径加载,或者用Environment.GetFolderPath拼系统字体目录。另外.ttc是字体集合文件,iText 加载时可能需要指定索引,如果报字体解析错误,换成.ttf单字体文件通常能绕过去。这一步做完,中文才能正常显示,否则前面表格排得再漂亮也是白搭。
4. 导出流程里的避坑清单:五个真实翻车现场
4.1 文件被占用,第二次导出直接抛异常
现象:第一次导出成功,第二次点导出按钮报IOException,提示文件正被另一进程使用。原因:PdfDocument或PdfWriter没有正确释放,文件句柄还开着。解决:严格用using包裹,或者手动在finally里调Close()。如果导出目标是用户指定的路径,还要考虑用户可能正开着这个 PDF 在看,这时候应该先写临时文件再替换,或者提示用户关闭文件。
4.2 中文变方块或乱码
现象:PDF 打开后中文全是方块、问号或者空白。原因:没有设置支持中文的字体,或者字体路径在目标机器上不存在。解决:显式加载中文字体并设置IDENTITY_H编码,字体文件随程序分发,加载前用File.Exists判断,不存在就给个友好提示而不是直接崩。
4.3 表格跨页时表头丢失、行被截断
现象:数据多的时候表格翻到第二页,表头没了,或者某一行被从中间切开。原因:iText 默认的表格跨页行为需要手动配置。解决:用AddHeaderCell添加表头让它自动重复,对于不想被截断的行,可以设置cell.SetKeepTogether(true),让整行保持在同一页。数据量特别大时还要考虑分页策略,别指望一个表格自动处理所有情况。
4.4 内存流导出后文件大小为 0
现象:用MemoryStream导出,写文件时发现长度是 0。原因:Document没关闭,内容还在缓冲区没刷出去。解决:确保Document和PdfDocument都释放之后再读MemoryStream,顺序不能反。常见写法是把MemoryStream的读取放在using块外面,或者手动调document.Close()后再stream.ToArray()。
4.5 部署到客户机器报找不到字体或依赖
现象:开发机跑得好好的,拷到客户机器上启动就报错。原因:字体路径写死成开发机的绝对路径,或者缺少 iText 相关的依赖 DLL。解决:字体用相对路径或系统目录动态拼接,依赖用 NuGet 管理并确保发布时把相关 DLL 一起带上。发布前在一台干净的机器上跑一遍,这个习惯能省掉大量售后沟通。
5. 进阶一点:用模板和流式输出把 Demo 变成能用的工具
Demo 跑通之后,真正落地时往往会遇到两个新需求:一是排版要固定,不能每次都用代码拼;二是导出要快,数据量大时不能卡界面。这两个需求对应两个进阶方向。
第一个方向是模板填充。与其用代码一行行拼段落和表格,不如先用工具做一个 PDF 模板,在需要填数据的地方留占位符,导出时用 iText 的PdfReader读模板,再用PdfStamper(iText 5)或PdfDocument配合表单域(iText 7)把数据填进去。这样做的好处是排版交给设计工具,代码只负责数据映射,改版式不用改代码。iText 7 里处理 AcroForm 表单域的写法大致是读入模板、拿到PdfAcroForm、按字段名SetValue,最后FlattenFields把表单压平,避免用户还能编辑。
第二个方向是异步导出加进度反馈。WinForm 是单线程 UI 模型,导出大文件时如果直接在按钮事件里跑,界面会假死。常见做法是把导出逻辑放到Task.Run里,通过IProgress<int>回传进度更新进度条,导出完成后再用Invoke回到 UI 线程提示成功。这里要注意 iText 的对象不是线程安全的,别在多个线程里共享同一个Document实例,每个导出任务独立创建自己的文档对象。
验证导出结果是否可靠,我一般会走三步:先用 PDF 阅读器打开看排版,再用"另存为文本"检查中文有没有丢,最后打印预览确认分页和边距。这三步走完,基本能覆盖 90% 的显示问题。至于性能,几千行数据的表格导出通常在秒级,如果明显变慢,检查是不是在循环里反复创建字体对象——字体应该创建一次复用,而不是每个单元格都CreateFont一遍。
从那以后我每次做 PDF 导出,都会先把字体加载和资源释放这两件事单独拎出来测一遍,再往上堆业务逻辑。这两个地方不出问题,后面的排版和性能才有意义。希望帮到你。
本文还有配套的精品资源,点击获取