拓冰建站拓冰建站
首页 / 资讯中心 / 正文

C# 实现 PDF 转 Word/Excel:开源库选型与坐标还原实战

简介面向.NET开发者的C# PDF转换示例工程演示如何借助Spire.PDF组件将PDF文件转换为Word和Excel格式适用于文档管理系统、批量报表处理或办公自动化场景。资源包共45个文件压缩包约86.24MB内容覆盖C#源码工程、Visual Studio解决方案与项目配置、DLL依赖库、XML运行配置、程序调试缓存及文档说明等各类型文件分工明确便于定位代码和依赖。工程采用Windows窗体程序结构已集成第三方PDF处理组件Spire.PDF 8.4.15包含界面布局、程序入口及包引用管理编译运行后可直接体验转换效果。读者可以基于此工程掌握PDF解析、文本/表格提取及格式重排的关键思路也能直接复用其窗体逻辑与转换接口缩短后续开发周期。目前已有370人学习/下载适合C#入门到中级开发者作为文档处理类项目的参考模板。1. 把 PDF 转成 Word、ExcelC# 程序员通常从哪下手办公室里最常遇到的情况是系统导出了几百份 PDF 报表领导却要求改几个字段后回传 Word或者设备日志是 PDF财务那边非要 Excel 才能算数。很多 C# 开发第一次接到这个需求会以为「PDF 解析库 Word 生成库」拼起来就能跑实际上手才发现PDF 只是一个版面描述格式它记录的是「哪个位置画了什么字符」而不是「哪里是标题、哪里是表格、这一段属于哪一列」。换句话说PDF 里没有语义结构转 Word 和 Excel 时需要自己从坐标、字体、间距里反推段落与表格。这篇内容面向想用 C# 做办公自动化的开发者会依次说清库怎么选、文本怎么提、表格怎么还原、Word 与 Excel 怎么写以及最后怎么验证转换结果是否可交付。2. 转换前先选库C# 处理 PDF 与 Word/Excel 的常见组合2.1 PDF 解析库怎么选PdfPig 与 iTextSharp 的取舍C# 圈子里的 PDF 解析库常见的有三个方向PdfPig、iTextSharpiText 7 的 .NET 版本、Aspose.Pdf。从开源协议和可控性来说我一般优先推荐 PdfPig它是 MIT 协议的开源库API 设计比较现代能读取页面里的字符、字形和坐标信息适合做文本提取和坐标分析。iTextSharp 的 AGPL 协议对很多企业不友好如果公司内部工具不开源用起来有法律风险Aspose.Pdf 转换保真度最好但授权费不低适合预算充足、对格式要求极高的项目。库名特点适合场景备注PdfPig轻量、MIT 协议、可获取字符坐标文本提取、表格还原、数据分析不直接支持 PDF 生成iTextSharp老牌、功能全面、支持生成和解析需要同时创建 PDF 的场景AGPL 协议需谨慎Aspose.Pdf转换质量高、API 丰富、可转 Word/Excel格式要求高的商业项目价格高CPU 内存占用大PdfiumViewer基于 Google PDFium可渲染页面为图片扫描件预处理、页面截图结合 OCR 使用从「既能拿到文本又能拿到坐标」这个角度PdfPig 的Word对象自带BoundingBox后面还原表格结构时会非常有用。iTextSharp 在旧版本上虽然也能做到但坐标提取的 API 没有 PdfPig 直观。2.2 生成 Word 和 ExcelNPOI、EPPlus 与 ClosedXML 的边界在 .NET 项目里生成 Word 和 Excel开源方案有几个NPOI 同时支持 .docx 和 .xlsxAPI 模仿 Java POI功能全但写法偏重EPPlus 专攻 ExcelAPI 清爽支持透视表、图表、条件格式5.0 之后引入了许可证上下文商业使用时需要付费ClosedXML 也做 Excel底层基于 OpenXML上手简单但对大文件性能一般。如果同一个项目里既要 Word 又要 Excel我会选 NPOI 做 WordEPPlus 做 Excel——这样每块都用各自最擅长的库代码写起来也不别扭。注意一点很多 Excel 的「转换」场景其实不要求复杂公式只需要把表格数据按行列写进去。所以选库时先确认需求如果只是数值和文本ClosedXML 就够了如果要做数据透视表或图表EPPlus 更合适如果还要生成 Word 里的目录、页眉页脚NPOI 能覆盖。2.3 最小依赖组合一个控制台项目解决 C# PDF 转换需求准备一个最小可跑的 .NET 控制台项目命令如下dotnet new console -n PdfConverter cd PdfConverter dotnet add package UglyToad.PdfPig dotnet add package NPOI dotnet add package EPPlus说明dotnet new console创建默认控制台模板-n指定项目名称dotnet add package会拉取 NuGet 上的最新稳定版如果公司内部有私有 NuGet 源需要自行配置。这里没有指定版本号因为 API 在主要版本之间变化较大建议以项目实际引用的版本为准。3. 用 PdfPig 解析 PDF拿到文本还要拿到坐标3.1 从页面提取文本的两种方式Text 属性与 GetWords()新建一个控制台应用把 PDF 文件路径传进来先尝试读取整页文本using UglyToad.PdfPig; using (var document PdfDocument.Open(input.pdf)) { foreach (var page in document.GetPages()) { Console.WriteLine($Page {page.Number}); Console.WriteLine(page.Text); } }这段代码的关键点是PdfDocument.Open负责打开 PDF 文件并解析内部对象GetPages()返回页面集合page.Text会把当前页所有字符按阅读顺序拼成一个长字符串。这样做的优点是简单缺点也很明显换行、分栏、表格线、单元格边界全部丢失一个三列表格会被拼成一行一段文字。所以当目标格式是 Excel 时page.Text只能作为粗略预览不能直接入库。改用GetWords()可以拿到每个词的坐标信息using UglyToad.PdfPig; using (var document PdfDocument.Open(input.pdf)) { foreach (var page in document.GetPages()) { var words page.GetWords().ToList(); foreach (var word in words) { Console.WriteLine($Text: {word.Text}); Console.WriteLine($BoundingBox: {word.BoundingBox}); Console.WriteLine($BottomLeft: {word.BoundingBox.BottomLeft}); Console.WriteLine($TopRight: {word.BoundingBox.TopRight}); } } }这里BoundingBox返回的是一个矩形区域包含四个角点坐标。PdfPig 的坐标系统以页面左下角为原点X 轴向右、Y 轴向上单位是 PDF 的点point1 点约等于 1/72 英寸。如果 PDF 页面是 A4 纵向宽度约为 595 点高度约为 842 点。理解坐标系很重要因为后面做表格行、列聚类时要基于这些坐标值计算行间距离和列间距离。3.2 用坐标恢复表格结构按 Y 分行的简单算法拿到坐标后最常见的需求是把一页里的词重新组织成「行 × 列」的表格结构。核心思路是同一行里的词它们的 Y 坐标中心点应该接近同一列里的词X 坐标的范围应该重叠。可以按下面步骤写一个简单版本。public class PdfCell { public string Text { get; set; } public double X { get; set; } public double Y { get; set; } } public static ListListPdfCell GroupWordsIntoRows( ListPdfCell cells, double yTolerance) { var sorted cells.OrderByDescending(c c.Y).ToList(); var rows new ListListPdfCell(); ListPdfCell currentRow null; double currentRowY 0; foreach (var cell in sorted) { if (currentRow null || Math.Abs(cell.Y - currentRowY) yTolerance) { currentRow new ListPdfCell { cell }; rows.Add(currentRow); currentRowY cell.Y; } else { currentRow.Add(cell); } } foreach (var row in rows) { row.Sort((a, b) a.X.CompareTo(b.X)); } return rows; }逻辑说明OrderByDescending(c c.Y)让 Y 值大即页面顶部的词排在前面yTolerance是行间容差当两个词的 Y 中心点距离小于这个值时认为它们属于同一行。行成型后再按 X 坐标从左到右排序得到列顺序。参数yTolerance需要根据实际 PDF 的行距调整一般取 35 点如果表格中有跨行单元格这个算法会出现多余的空行后续需要对空行做合并处理。3.3 处理中文字体与乱码PdfPig 里字体映射的基本逻辑PdfPig 的文本提取依赖 PDF 文件里的 ToUnicode 映射表。绝大多数由 Word、WPS、LaTeX 生成的 PDF 都带这个映射所以中文能正常读出来。但有些 PDF 生成工具为了压缩体积会省略 ToUnicode此时 PdfPig 提取出来的是乱码字符甚至是空白。遇到这种情况可以先检查 PDF 是否属于扫描件把页面渲染成图片看是否是一张静态图。如果是扫描件需要 OCR如果不是扫描件但提取乱码可以尝试更换 PDF 生成源或者用 iTextSharp 做二次解析。补充一点如果 PDF 里使用了嵌入子集字体字体名称会变成类似ABCDEFSimSun这种带前缀的形式这不影响文本提取只影响后续字体属性的判断。4. 把解析结果变成 Word、ExcelNPOI 与 EPPlus 的代码骨架4.1 用 NPOI 生成 Word段落、标题和表格的最小流程假设已经从 PdfPig 里拿到了文本和表格行列数据现在要生成一个 .docx。NPOI 的XWPFDocument代表 Word 文档CreateParagraph()创建段落CreateTable()创建表格。示例代码如下using NPOI.XWPF.UserModel; var doc new XWPFDocument(); // 创建标题段落 var titlePara doc.CreateParagraph(); titlePara.Alignment ParagraphAlignment.CENTER; var titleRun titlePara.CreateRun(); titleRun.SetText(季度报表); titleRun.FontFamily 微软雅黑; titleRun.FontSize 16; titleRun.SetBold(true); // 创建表格 var table doc.CreateTable(3, 3); table.GetRow(0).GetCell(0).SetText(部门); table.GetRow(0).GetCell(1).SetText(收入); table.GetRow(0).GetCell(2).SetText(支出); table.GetRow(1).GetCell(0).SetText(研发部); table.GetRow(1).GetCell(1).SetText(120000); table.GetRow(1).GetCell(2).SetText(80000); using (var fs File.Create(output.docx)) { doc.Write(fs); }说明CreateRun()创建文本片段SetText()赋值FontFamily在中文环境下必须设置否则 Word 可能用默认字体显示中文出现乱码或字体不一致CreateTable(rows, cols)会生成一个带边框的表格GetRow和GetCell用来定位单元格SetText直接写入内容。如果 PDF 里提取到了单元格的宽度信息可以通过table.GetRow(0).GetCell(0).SetWidth(2000)设置宽度单位是 twip1/20 点。4.2 用 EPPlus 生成 Excel单元格赋值与样式设置Excel 部分用 EPPlus 写起来更顺手。下面的代码演示如何创建工作簿、写入表头、填充数据并保存using OfficeOpenXml; ExcelPackage.LicenseContext LicenseContext.NonCommercial; using (var pkg new ExcelPackage()) { var ws pkg.Workbook.Worksheets.Add(Sheet1); // 写表头 ws.Cells[1, 1].Value 部门; ws.Cells[1, 2].Value 收入; ws.Cells[1, 3].Value 支出; // 表头加粗 using (var range ws.Cells[A1:C1]) { range.Style.Font.Bold true; range.Style.Fill.PatternType OfficeOpenXml.Style.ExcelFillStyle.Solid; range.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightGray); } // 填充数据 var data new Listobject[] { new object[] { 研发部, 120000, 80000 }, new object[] { 市场部, 200000, 150000 } }; for (int i 0; i data.Count; i) { ws.Cells[i 2, 1].Value data[i][0]; ws.Cells[i 2, 2].Value data[i][1]; ws.Cells[i 2, 3].Value data[i][2]; } ws.Cells.AutoFitColumns(); pkg.SaveAs(new FileInfo(output.xlsx)); }ExcelPackage.LicenseContext LicenseContext.NonCommercial在 EPPlus 5.0 以上是必须写的否则运行时会抛异常Cells[row, col]从 1 开始计数AutoFitColumns()会根据内容长度自动调整列宽但如果内容里有中英文混排自动列宽偶尔会偏窄可以手动指定ws.Column(1).Width 20。如果 PDF 表格有合并单元格需求使用ws.Cells[A1:B2].Merge true。4.3 样式与格式之间如何取舍什么时候直接复制 PDF 版式什么时候放弃PDF 转 Word最理想的结果是每一段文字、每一个表格边框、每一种字体大小都原样复现。但现实是PDF 里的空心文字、艺术字、特殊排版、跨页表格用开源方案很难完美还原。我的做法是文字段落尽量保留标题层级和字体表格保留行列数据但不强制要求单元格合并逻辑 100% 一致图片单独提取后按原位置重新插入位置用坐标换算。如果客户说「必须一模一样」那通常要考虑 Aspose 这类商业库或者把 PDF 渲染成图片直接嵌入 Word而不是做文本转换。5. 落地技巧命令批处理、日志和逐个文件的验证方法5.1 写一个可批量的转换脚本通配符与输出目录实际项目里很少只转一个文件。控制台程序的Main函数接收参数用输入目录作为第一个参数输出目录作为第二个参数然后遍历所有 PDF 文件static void Main(string[] args) { if (args.Length 2) { Console.WriteLine(用法: PdfConverter 输入目录 输出目录); return; } var inputDir args[0]; var outputDir args[1]; Directory.CreateDirectory(outputDir); var files Directory.GetFiles(inputDir, *.pdf); foreach (var file in files) { try { ConvertPdf(file, outputDir); Console.WriteLine($[OK] {Path.GetFileName(file)}); } catch (Exception ex) { Console.WriteLine($[FAIL] {Path.GetFileName(file)}: {ex.Message}); } } }这段代码强调了批处理中的两条原则单个文件失败不能中断整个任务所以要 catch 异常并记日志输出目录最好按日期或批次创建子目录避免同名文件互相覆盖。5.2 转换质量如何验证看文本顺序、表格行数、字符统计转换完成后手动打开几个样本文件检查是一种方法但数量多了就不现实。我一般会在程序里嵌入一个简单的验证步骤用 PdfPig 读取原文的字符总数再读取 Word/Excel 里的文本总量计算差值。如果两者差异超过 10%说明有内容丢失如果 Excel 的行数和原 PDF 表格行数不一致说明行列聚类参数有问题。还可以用正则去匹配关键字段比如合同编号、日期、金额确认这些字段在转换结果里出现的位置。验证项方法通过标准文本完整性对比原文与转换后字符总数差值小于 10%表格行数按坐标聚类行数与 Excel 行数对比完全一致关键字段正则匹配合同编号、日期等至少出现一次图片数量统计 PDF 内嵌图片与 Word 内嵌图片数量一致5.3 遇到乱码与歪斜文本时的三招改容差、换库、转图片乱码的常见原因有三个PDF 没有 ToUnicode 映射、字体子集损坏、文本被转成曲线。第一个原因可以尝试用PdfDocument.Open之后的document.GetLetters()手动查看字符编码如果能拿到 Unicode 码点但显示乱码说明控制台编码问题改用文件输出就能解决第二个原因只能换原件第三个原因的本质是文字已经变成矢量图形PdfPig 无能为力需要将页面渲染成图片再走 OCR。歪斜文本在大批量扫描件里很常见解决方法是调大yTolerance或者根据BoundingBox的旋转角度做坐标矫正。角度信息可以从word.BoundingBox.BottomLeft与BottomRight的 Y 值差异计算差异为 0 说明文本水平。处理扫描件 PDF 的一个常见路线是先用 PdfiumViewer 把页面转成 PNG再接入 Tesseract OCR 引擎用简体中文语言包识别最后把识别文本按原坐标写入 Excel。这样虽然不能保证 100% 准确但对无法提供电子版原件的场景已经是成本最低的方案。最后再提醒一句任何转换方案上线前必须拿三类样本测试标准文档、扫描档、带复杂表格的 PDF不要拿单一文件跑通就交付。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门