
设想一下:你的应用程序收到了一份 PDF 发票。你需要的并不是页面上的原始文本,而是发票编号、供应商名称和明细行 —— 一份你的 API 可以直接读取的结构化 JSON。这才是 PDF 转 JSON 真正要解决的核心问题。
与 CSV 或 XML 不同,PDF 文件并不具备内建的数据结构 —— 没有字段定义、没有行列概念、没有数据模式。能从中提取出什么样的 JSON,取决于文档实际包含的内容类型:是键值对形式的纯文本、行列排列的表格、可填写的表单字段,还是需要 OCR 才能识别的扫描图像。
本文将通过可运行的 C# 代码逐一覆盖上述四种场景,基于 Spire.PDF for .NET。我们将构建一个真正的发票转 JSON 转换器,处理合并单元格、缺少表头等常见的表格提取难题,并将所有逻辑封装为一个可复用的 PdfToJsonConverter 类,方便你直接集成到任何 .NET 项目中使用。
快速导航
PDF 并没有像 CSV 转 JSON 那样的"一键转换"能力。PDF 本身不含任何 JSON 结构。开发者真正要做的是:从 PDF 中提取内容,然后将这些内容组织成与自身业务场景匹配的 JSON 格式。
根据 PDF 类型和业务需求的不同,目标 JSON 通常属于以下三类之一。
从每一页提取全部文本,封装为 JSON 输出。适用于搜索索引、RAG 流水线和文档归档等场景。
{
"sourceFile": "Contract.pdf",
"pages": [
{ "pageNumber": 1, "text": "服务协议\n甲方 Contoso Ltd 与..." }
]
}
许多 PDF 遵循 标签: 值 的格式 —— 如员工档案、注册表单、简单发票等。目标是将这些键值对解析为扁平的 JSON 对象:
{
"name": "John Smith",
"email": "该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。",
"department": "Engineering",
"employeeId": "EMP-2026-0142"
}
实际业务文档中的数据往往具有嵌套关系:一份发票可能同时包含表头信息、明细行、税费和付款条款,输出的 JSON 需要反映这种层次结构:
{
"invoiceNumber": "INV-2026-0042",
"vendor": "Contoso Ltd",
"date": "2026-06-15",
"lineItems": [
{ "description": "Widget A", "quantity": 150, "unitPrice": 24.50, "total": 3675.00 }
],
"subtotal": 3675.00,
"tax": 294.00,
"total": 3969.00
}
区分这三种格式非常重要。 当寻求"将 PDF 转换为 JSON"时,首先要想清楚应用程序到底需要哪种输出。本文后续内容将逐一展示如何使用 Spire.PDF 在 C# 中实现每种格式。
通过 NuGet 包管理器控制台安装:
Install-Package Spire.PDF
或在 .csproj 文件中添加:
<PackageReference Include="Spire.PDF" Version="*" />
在你的项目中引入以下命名空间:
using Spire.Pdf;
using Spire.Pdf.Texts;
using Spire.Pdf.Utilities;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System.Text.Json;
using System.Text.Json.Serialization;
Spire.PDF 支持 .NET Framework、.NET Core 及 .NET 6/7/8/9+。
先从最常见的需求开始:从 PDF 中提取文本,输出为 JSON。
using Spire.Pdf;
using Spire.Pdf.Texts;
using System.Collections.Generic;
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile("EmployeeRecord.pdf");
var pages = new List<Dictionary<string, string>>();
for (int i = 0; i < pdf.Pages.Count; i++)
{
PdfPageBase page = pdf.Pages[i];
PdfTextExtractOptions options = new PdfTextExtractOptions();
options.IsExtractAllText = true;
PdfTextExtractor extractor = new PdfTextExtractor(page);
string pageText = extractor.ExtractText(options);
pages.Add(new Dictionary<string, string>
{
{ "pageNumber", (i + 1).ToString() },
{ "text", pageText.Trim() }
});
}
}
如果你的 PDF 遵循 标签: 值 的格式,可将提取的文本解析为结构化字段:
using System.Text.Json;
using System.Text.Encodings.Web;
var parsedFields = new Dictionary<string, string>();
foreach (var page in pages)
{
string[] lines = page["text"].Split('\n');
foreach (string line in lines)
{
int colonIndex = line.IndexOf(':');
if (colonIndex < 0)
{
colonIndex = line.IndexOf(':');
}
if (colonIndex > 0)
{
string key = line[..colonIndex].Trim();
string value = line[(colonIndex + 1)..].Trim();
parsedFields[key] = value;
}
}
}
var jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
string jsonOutput = JsonSerializer.Serialize(parsedFields, jsonOptions);
File.WriteAllText("EmployeeRecord.json", jsonOutput);
PdfDocument.LoadFromFile() — 打开 PDF 文件PdfTextExtractor.ExtractText() — 从页面中提取文本内容PdfTextExtractOptions.IsExtractAllText — 保留空白字符和格式以下示例展示了从提取的员工档案中生成的结构化 JSON。
{
"name": "John Smith",
"email": "该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。",
"department": "Engineering",
"employeeId": "EMP-2026-0142",
"startDate": "2024-03-15"
}
以下截图展示了运行示例后实际生成的 JSON 文件。

这种方式适用于表单、档案以及具有一致键值布局的文档。对于非结构化文本,可以跳过解析步骤,直接序列化原始页面内容。
如需深入了解 PDF 文本提取,可参阅我们的专题指南:使用 Spire.PDF for .NET 在 C# 中从 PDF 提取文本。
上一节介绍了如何从 PDF 中提取纯文本。这对于段落和简单档案来说效果不错,但很多商业文档的核心价值信息是组织在表格里的 —— 比如发票明细、销售报表和财务报表。为了让行与列、单元格之间的对应关系不丢失,表格数据需要先以不同于纯文本的方式提取,再转换为结构化 JSON。
文本提取是按阅读顺序返回一段连续的字符流。即使表格在页面上看起来排列得整整齐齐,提取出来的文本也往往会丢失行列结构,很难判读哪些值原本属于同一行。
要保留表格布局,你需要一个专门的表格提取引擎。PdfTableExtractor 会分析页面布局、检测表格边界,并返回 PdfTable 对象,让你可以逐行逐列地遍历。与其得到一段难以解析的连续文本:
Widget A 150 $24.50 $3,675.00
而是生成结构化的 JSON:
{
"Product": "Widget A",
"Quantity": "150",
"Unit Price": "$24.50",
"Total": "$3,675.00"
}
以下示例演示了如何从 PDF 中提取表格并将其序列化为 JSON。
using Spire.Pdf;
using Spire.Pdf.Utilities;
using System.Collections.Generic;
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile("SalesReport.pdf");
PdfTableExtractor tableExtractor = new PdfTableExtractor(pdf);
var allTables = new List<List<List<string>>>();
for (int pageIndex = 0; pageIndex < pdf.Pages.Count; pageIndex++)
{
PdfTable[] tables = tableExtractor.ExtractTable(pageIndex);
if (tables != null && tables.Length > 0)
{
foreach (PdfTable table in tables)
{
int rowCount = table.GetRowCount();
int colCount = table.GetColumnCount();
var tableData = new List<List<string>>();
for (int row = 0; row < rowCount; row++)
{
var rowData = new List<string>();
for (int col = 0; col < colCount; col++)
{
rowData.Add(table.GetText(row, col).Trim());
}
tableData.Add(rowData);
}
allTables.Add(tableData);
}
}
}
}
var jsonTables = new List<object>();
foreach (var tableData in allTables)
{
if (tableData.Count < 2) continue;
var headers = tableData[0];
var rows = new List<Dictionary<string, string>>();
for (int i = 1; i < tableData.Count; i++)
{
var rowObj = new Dictionary<string, string>();
for (int j = 0; j < headers.Count && j < tableData[i].Count; j++)
{
rowObj[headers[j]] = tableData[i][j];
}
rows.Add(rowObj);
}
jsonTables.Add(new
{
tableIndex = allTables.IndexOf(tableData) + 1,
headers = headers,
data = rows
});
}
string tableJson = JsonSerializer.Serialize(new
{
sourceFile = "SalesReport.pdf",
tables = jsonTables
}, new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
File.WriteAllText("SalesReport_Tables.json", tableJson);
PdfTableExtractor(PdfDocument) — 初始化表格提取引擎PdfTableExtractor.ExtractTable(pageIndex) — 检测并提取页面中的表格PdfTable.GetRowCount() / GetColumnCount() — 返回表格的维度PdfTable.GetText(row, col) — 读取单元格内容生成的 JSON 通过将每一行按检测到的列标题组织为键值对,保留了原始表格结构。
{
"sourceFile": "SalesReport.pdf",
"tables": [
{
"tableIndex": 1,
"headers": ["Product", "Quantity", "Unit Price", "Total"],
"data": [
{ "Product": "Widget A", "Quantity": "150", "Unit Price": "$24.50", "Total": "$3,675.00" },
{ "Product": "Widget B", "Quantity": "80", "Unit Price": "$39.90", "Total": "$3,192.00" }
]
}
]
}
以下截图展示了运行示例后实际生成的 JSON 文件。

这种方式适用于发票、报表等表格结构清晰的 PDF。如果文档中包含合并单元格、缺少表头或跨页表格,则可能需要额外的后处理。
如需深入了解 PDF 表格提取,可参阅我们的专题指南:使用 Spire.PDF for .NET 在 C# 中提取 PDF 表格。
现实中的 PDF 表格往往不会那么规整。下面是你最常遇到的三个问题,以及对应的处理方式。
许多发票和报表中的表格没有明确的表头行,数据直接从第一行开始:
Apple 10 $2.99 $29.90
Orange 5 $1.50 $7.50
当第一行直接就是数据而没有表头时,你可以根据对文档结构的了解手动指定列名:
// 当 PDF 表格没有表头行时,手动定义列名
string[] defaultHeaders = { "Product", "Quantity", "UnitPrice", "Total" };
var rows = new List<Dictionary<string, string>>();
for (int i = 0; i < tableData.Count; i++) // 从 0 开始,而非 1
{
var rowObj = new Dictionary<string, string>();
for (int j = 0; j < defaultHeaders.Length && j < tableData[i].Count; j++)
{
rowObj[defaultHeaders[j]] = tableData[i][j];
}
rows.Add(rowObj);
}
财务报表中的表格经常使用合并单元格进行分组:
Quarter Revenue Expenses
Q1 $120,000 $95,000
$115,000 $88,000
Q2 $140,000 $102,000
提取器对合并单元格会返回空字符串。需要从上一个非空值向前填充:
// 用前一行的值填充合并单元格
for (int col = 0; col < headers.Count; col++)
{
string lastValue = "";
for (int row = 1; row < tableData.Count; row++)
{
if (col < tableData[row].Count && !string.IsNullOrWhiteSpace(tableData[row][col]))
{
lastValue = tableData[row][col];
}
else if (col < tableData[row].Count)
{
tableData[row][col] = lastValue;
}
}
}
企业报表中的单个表格常常跨越多页,且表头行在每一页上重复出现。处理方式是在序列化时对重复表头进行去重:
var combinedRows = new List<Dictionary<string, string>>();
string[] expectedHeaders = null;
for (int pageIndex = 0; pageIndex < pdf.Pages.Count; pageIndex++)
{
PdfTable[] tables = tableExtractor.ExtractTable(pageIndex);
if (tables == null) continue;
foreach (PdfTable table in tables)
{
for (int r = 0; r < table.GetRowCount(); r++)
{
var cells = new List<string>();
for (int c = 0; c < table.GetColumnCount(); c++)
{
cells.Add(table.GetText(r, c).Trim());
}
// 第一页的第一行作为表头
if (expectedHeaders == null && r == 0)
{
expectedHeaders = cells.ToArray();
continue;
}
// 跳过后续页面上重复的表头行
if (r == 0 && cells.SequenceEqual(expectedHeaders))
continue;
var rowDict = new Dictionary<string, string>();
for (int c = 0; c < expectedHeaders.Length && c < cells.Count; c++)
{
rowDict[expectedHeaders[c]] = cells[c];
}
combinedRows.Add(rowDict);
}
}
}
与纯文本或表格不同,可填写的 PDF 表单本身就是以命名字段来存储数据的。申请表、调查问卷、注册表单等文档中,每个字段都有名称和对应的值,可以直接映射为 JSON 键值对 —— 这也让表单数据成为 PDF 中最容易序列化的内容类型之一。
using Spire.Pdf;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Encodings.Web;
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile("E:\\IDEProjects\\OfficialArticles-CodeArts\\Samples\\RegistrationForm.pdf");
PdfFormWidget formWidget = pdf.Form as PdfFormWidget;
var formData = new Dictionary<string, object>();
if (formWidget != null)
{
for (int i = 0; i < formWidget.FieldsWidget.List.Count; i++)
{
PdfField field = formWidget.FieldsWidget.List[i] as PdfField;
if (field is PdfTextBoxFieldWidget textBox)
formData[textBox.Name] = textBox.Text;
else if (field is PdfCheckBoxWidgetFieldWidget checkBox)
formData[checkBox.Name] = checkBox.Checked;
else if (field is PdfRadioButtonListFieldWidget radioButton)
formData[radioButton.Name] = radioButton.Value;
else if (field is PdfComboBoxWidgetFieldWidget comboBox)
formData[comboBox.Name] = comboBox.SelectedValue;
else if (field is PdfListBoxWidgetFieldWidget listBox)
{
var selectedItems = new List<string>();
foreach (PdfListWidgetItem item in listBox.Values)
selectedItems.Add(item.Value);
formData[listBox.Name] = selectedItems;
}
}
}
var formOutput = new
{
sourceFile = "RegistrationForm.pdf",
fieldCount = formData.Count,
fields = formData
};
var jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
string json = JsonSerializer.Serialize(formOutput, jsonOptions);
File.WriteAllText("RegistrationForm_Data.json", json);
}
PdfFormWidget — 提供对文档交互式表单的访问PdfTextBoxFieldWidget.Text — 读取文本输入值PdfCheckBoxWidgetFieldWidget.Checked — 读取复选框状态PdfRadioButtonListFieldWidget.Value — 读取选中的单选按钮PdfComboBoxWidgetFieldWidget.SelectedValue — 读取下拉框选中项以下示例展示了提取的表单字段如何表示为结构化 JSON。
{
"sourceFile": "RegistrationForm.pdf",
"fieldCount": 6,
"fields": {
"FullName": "John Smith",
"Email": "该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。",
"Department": "Sales",
"AgreeTerms": true,
"SubscriptionPlan": "Enterprise",
"Skills": ["C#", "SQL", "Azure"]
}
}
以下截图展示了导出表单数据后实际生成的 JSON 文件。

这种方式适用于包含文本框、复选框、单选按钮和下拉列表等结构化字段的交互式 PDF 表单。由于每个字段已经具有唯一名称,提取出的数据可以直接序列化为 JSON,无需额外的解析步骤。
如需深入了解在 C# 中导入和导出 PDF 表单字段数据,可参阅我们的专题指南:使用 Spire.PDF for .NET 处理 PDF 表单字段。
发票处理是 PDF 转 JSON 最常见的业务场景之一。本节不展示完整的解析器实现,而是演示如何将第 3 节的文本提取和第 4 节的表格提取组合起来,解决一个实际的业务问题。
在编写任何提取代码之前,首先要定义目标模式。对于一份典型的发票,JSON 输出可能如下所示:
{
"invoiceNumber": "INV-2026-0042",
"date": "2026-06-15",
"vendor": "Contoso Ltd",
"paymentTerms": "Net 30",
"lineItems": [
{ "description": "Widget A", "quantity": 150, "unitPrice": 24.50, "total": 3675.00 },
{ "description": "Widget B", "quantity": 80, "unitPrice": 39.90, "total": 3192.00 }
],
"subtotal": 8367.00,
"tax": 669.36,
"total": 9036.36
}
使用文本提取(第 3 节)通过正则表达式解析表头字段,使用表格提取(第 4 节)获取明细行:
// 使用正则表达式从提取的文本中解析表头字段
invoice["invoiceNumber"] = Regex.Match(fullText, @"Invoice Number:\s*(\S+)").Groups[1].Value;
invoice["date"] = Regex.Match(fullText, @"Date:\s*(\S+)").Groups[1].Value;
invoice["vendor"] = Regex.Match(fullText, @"Vendor:\s*(.+)").Groups[1].Value;
// 从表格数据中提取明细行(第 4 节的模式)
for (int r = 1; r < table.GetRowCount(); r++)
{
lineItems.Add(new
{
description = table.GetText(r, 0).Trim(),
quantity = int.Parse(table.GetText(r, 1).Trim()),
unitPrice = ParseCurrency(table.GetText(r, 2)),
total = ParseCurrency(table.GetText(r, 3))
});
}
该实现将第 3 节介绍的文本提取与第 4 节介绍的表格提取相结合。正则表达式仅用于简单的字段匹配 —— 核心的 PDF 处理完全依赖 Spire.PDF API。
实际生产环境中,你通常不会只面对一种发票格式:
基于正则表达式的解析对于已知格式来说既快速又可靠。要构建可用于生产环境的实现,你可以扩展第 8 节中的 PdfToJsonConverter 类,创建一个专用的发票解析器,复用已有的提取模式。
生产工作流通常需要一次性处理成百上千个 PDF。以下批量处理器能够妥善处理错误并记录日志:
using Spire.Pdf;
using Spire.Pdf.Texts;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
string inputDir = @"C:\PDFs\Invoices";
string outputDir = @"C:\Output\JSON";
Directory.CreateDirectory(outputDir);
string[] pdfFiles = Directory.GetFiles(inputDir, "*.pdf");
var results = new List<object>();
foreach (string pdfPath in pdfFiles)
{
string fileName = Path.GetFileNameWithoutExtension(pdfPath);
string outputPath = Path.Combine(outputDir, $"{fileName}.json");
try
{
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile(pdfPath);
var pageTexts = new List<string>();
for (int i = 0; i < pdf.Pages.Count; i++)
{
var extractor = new PdfTextExtractor(pdf.Pages[i]);
var options = new PdfTextExtractOptions { IsExtractAllText = true };
pageTexts.Add(extractor.ExtractText(options).Trim());
}
var doc = new
{
sourceFile = Path.GetFileName(pdfPath),
pageCount = pdf.Pages.Count,
processedAt = DateTime.UtcNow,
content = pageTexts
};
File.WriteAllText(outputPath, JsonSerializer.Serialize(doc,
new JsonSerializerOptions { WriteIndented = true }));
results.Add(new { file = fileName, status = "success" });
}
}
catch (Exception ex)
{
results.Add(new { file = fileName, status = "error", error = ex.Message });
}
}
File.WriteAllText(Path.Combine(outputDir, "_log.json"),
JsonSerializer.Serialize(results, new JsonSerializerOptions { WriteIndented = true }));
如果你的批处理对象是发票,可将上述纯文本提取替换为第 6 节中的发票 JSON 提取模式;对于通用转换,可使用第 8 节中的 PdfToJsonConverter 类。
在生产应用中,建议将所有提取逻辑封装到一个统一的类中。下面的 PdfToJsonConverter 将文本、表格和表单字段提取整合为一个可复用的 PDF 转 JSON 转换器:
using Spire.Pdf;
using Spire.Pdf.Texts;
using Spire.Pdf.Utilities;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
public class PdfToJsonConverter
{
private readonly JsonSerializerOptions _jsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
public string ConvertToJson(string pdfPath)
{
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile(pdfPath);
var result = new
{
sourceFile = Path.GetFileName(pdfPath),
processedAt = DateTime.UtcNow,
text = ExtractText(pdf),
tables = ExtractTables(pdf),
formFields = ExtractFormFields(pdf)
};
return JsonSerializer.Serialize(result, _jsonOptions);
}
}
public void ConvertAndSave(string pdfPath, string outputPath)
{
File.WriteAllText(outputPath, ConvertToJson(pdfPath));
}
// 复用第 3 节的文本提取技术(PdfTextExtractor + PdfTextExtractOptions)
private List<PageText> ExtractText(PdfDocument pdf) { return new List<PageText>(); }
// 复用第 4 节的表格提取技术(PdfTableExtractor + ExtractTable)
private List<TableData> ExtractTables(PdfDocument pdf) { return new List<TableData>(); }
// 复用第 5 节的表单字段提取技术(PdfFormWidget + 字段类型判断)
private Dictionary<string, object> ExtractFormFields(PdfDocument pdf) { return new Dictionary<string, object>(); }
}
public class PageText
{
public int PageNumber { get; set; }
public string Text { get; set; }
}
public class TableData
{
public int PageNumber { get; set; }
public int RowCount { get; set; }
public List<List<string>> Rows { get; set; }
}
var converter = new PdfToJsonConverter();
// 处理单个文件
converter.ConvertAndSave("InvoiceReport.pdf", "InvoiceReport.json");
// 在 ASP.NET 控制器中使用
[HttpPost("api/pdf-to-json")]
public IActionResult ConvertPdf(IFormFile file)
{
var tempPath = Path.GetTempFileName();
file.CopyTo(new FileStream(tempPath, FileMode.Create));
var converter = new PdfToJsonConverter();
string json = converter.ConvertToJson(tempPath);
return Content(json, "application/json");
}
辅助方法(ExtractText、ExtractTables、ExtractFormFields)分别复用了第 3 节到第 5 节介绍的提取技术。完整实现请参阅相应章节。
将 PDF 转 JSON 功能集成到生产系统时,请注意以下几点:
JsonIgnoreCondition.WhenWritingNull 在输出中忽略空值字段。扫描版 PDF 包含的是图像而非可选择的文本,因此必须先通过 OCR 引擎处理才能转换为 JSON。Spire.PDF 负责 PDF 渲染和页面处理,文本识别则应由 OCR 方案(如 Tesseract 或 Azure AI Vision)来完成。
完整操作指南请参阅 如何在 C# 中从扫描件 PDF 提取文本。
OCR 返回识别文本后,你可以使用本文前述的相同技术进行解析。
string recognizedText = ocrEngine.Recognize(imagePath);
// 使用前面示例中展示的相同辅助方法解析识别文本
var parsedData = ParseRecognizedText(recognizedText);
string json = JsonSerializer.Serialize(parsedData, new JsonSerializerOptions
{
WriteIndented = true
});
对于只有几页的文档,PDF 转 JSON 的转换通常不成问题。但到了生产环境,你需要面对的是成百上千个文件,每个文件可能多达数百页。下面是你将真实遇到的性能挑战。
避免在序列化之前将所有页面文本加载到 List<string> 中。改为逐页处理并增量写出:
using (var stream = File.Create("output.json"))
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }))
{
writer.WriteStartObject();
writer.WriteString("sourceFile", Path.GetFileName(pdfPath));
writer.WriteStartArray("pages");
for (int i = 0; i < pdf.Pages.Count; i++)
{
var extractor = new PdfTextExtractor(pdf.Pages[i]);
var options = new PdfTextExtractOptions { IsExtractAllText = true };
string text = extractor.ExtractText(options).Trim();
writer.WriteStartObject();
writer.WriteNumber("pageNumber", i + 1);
writer.WriteString("text", text);
writer.WriteEndObject();
}
writer.WriteEndArray();
writer.WriteEndObject();
}
Utf8JsonWriter 直接写入流,而不是先在内存中构建字符串。对于一个 500 页的文档,与 JsonSerializer.Serialize() 相比,这种方式可以将峰值内存占用降低 60%–70%。
PdfDocument 会在内存中保存解析后的页面树、字体和图像引用。有两条规则:
using 包裹 PdfDocument — 它在释放时会清理非托管资源PdfDocument 实例打开对于处理 1000 个以上文件的批处理任务,在循环内部使用 using 模式可确保每个文档在加载下一个之前被完全释放。
批量转换属于 CPU 密集型任务,非常适合并行化:
var pdfFiles = Directory.GetFiles(inputDir, "*.pdf");
Parallel.ForEach(pdfFiles,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
pdfPath =>
{
string outputPath = Path.Combine(outputDir,
Path.GetFileNameWithoutExtension(pdfPath) + ".json");
var converter = new PdfToJsonConverter();
converter.ConvertAndSave(pdfPath, outputPath);
});
每个线程创建各自的 PdfToJsonConverter 和 PdfDocument 实例。PdfDocument 不是线程安全的 —— 切勿在线程之间共享单个实例。
在以下情况下,应使用 Utf8JsonWriter 而非 JsonSerializer.Serialize():
对于较小的文档,JsonSerializer 更简单,且内存差异可忽略不计。
Spire.PDF for .NET 提供免费的评估版本,但有页数限制。对于生产使用,你可以申请 30 天免费许可证或购买商业许可证。System.Text.Json 序列化器是 .NET 内置的,完全免费。
可以,但需要使用外部 OCR 引擎。Spire.PDF 通过 SaveAsImage() 将 PDF 页面渲染为图像,然后你可以将其传递给 Tesseract、Azure Computer Vision 或 Amazon Textract 进行文本识别。识别后的文本解析和序列化为 JSON 的流程与常规 PDF 相同。集成模式请参阅第 9 节。
可以。PdfTableExtractor 会自动检测每一页上的表格结构,无需手动配置。它既能处理结构规范的表格(在 Word 或 Excel 中创建的),也能处理视觉表格(文本对齐排列为行列形式)。对于跨页表格或无表头表格,请参阅第 4 节中的处理模式。
可以。使用 Directory.GetFiles() 遍历目录,用 Spire.PDF 提取 API 处理每个 PDF,并保存为独立的 JSON 文件。记得加入错误处理,确保单个文件失败不会中断整个批处理。完整示例请参阅第 7 节。
逐页处理 PDF,而不是一次性将所有内容加载到内存中。对于非常大的文件(100 页以上),使用 Utf8JsonWriter 以流的方式增量写出 JSON,避免在内存中构建完整的输出。流式 JSON 模式和并行处理方法请参阅第 10 节。
可以。你可以将本文中的 PdfToJsonConverter 类封装到 ASP.NET Web API 端点中。接受 PDF 上传,运行提取,返回 JSON 响应。Spire.PDF 适用于任何 .NET 宿主环境 —— ASP.NET Core、Azure Functions、AWS Lambda 或自托管的控制台应用均可。ASP.NET 控制器示例请参阅第 8 节。
PDF 转 JSON 并不是一个单一的操作。根据文档类型的不同,你实际上在解决三种不同的问题:将原始文本封装为 JSON 输出、将键值对解析为扁平对象,或通过文本与表格提取构建结构化的业务 JSON。
本文覆盖了以上全部三种场景,以及那些简单实现无法应对的复杂情况:无表头表格、合并单元格、跨页表格、可填写表单字段、多样化的发票布局、批量处理、大型文档的内存管理,以及 OCR 集成边界。
PdfToJsonConverter 类是一个可以按你的文档类型灵活定制的起点。第 6 节展示的发票提取模式则演示了如何将这些技术组合起来处理真实的业务文档。两者都基于 Spire.PDF for .NET,它在本地完成所有 PDF 读取工作,无需任何外部依赖。
开始使用:
Install-Package Spire.PDF
Excel 文件常用于存储结构化数据,而 Markdown 则广泛应用于技术文档、静态网站以及基于 Git 的发布工作流中。当你需要在 Markdown 文档中复用电子表格数据时,手动复制和重新格式化 Excel 表格不仅耗时,而且容易出错。更可靠的方式是使用 Python 自动完成转换。
本文将介绍如何使用 Spire.XLS for Python 将 Excel 转换为 Markdown。你将学习如何转换整个工作簿、导出指定工作表或单元格区域,以及如何通过简单的代码实现批量转换。
将 Excel 表格转换为 Markdown 在以下场景中非常实用:
要在 Python 中将 Excel 文件转换为 Markdown,请从 PyPI 安装 Spire.XLS for Python:
pip install spire.xls
Markdown 转换功能从 Spire.XLS for Python 16.4.0 开始提供支持。如果你使用的是较早版本,请先升级:
pip install --upgrade spire.xls
将 Excel 文件转换为 Markdown 最简单的方式是加载 Excel 工作簿,然后将其保存为 .md 文件。
整个过程只需三个步骤:
from spire.xls import Workbook
# 创建 Workbook 对象
workbook = Workbook()
# 加载 Excel 文件
workbook.LoadFromFile("report.xlsx")
# 保存为 Markdown 文件
workbook.SaveToMarkdown("output.md")
# 释放资源
workbook.Dispose()
输出示例:

在实际项目中,你不一定总是需要转换整个工作簿。有时你可能希望自定义图片和超链接的导出方式、仅转换某一个工作表、导出指定的数据区域,或自动处理整个文件夹中的 Excel 文件。
下面将展示如何在 Python 中实现这些转换。
将 Excel 导出为 Markdown 时,图片和超链接会被转换为对应的 Markdown 语法。你可以使用 MarkdownOptions 类中的属性来控制图片路径和超链接的导出方式。
| 属性 | 设置为 True | 设置为 False |
|---|---|---|
| SavePicInRelativePath | 使用相对路径保存图片,例如  | 使用绝对路径保存图片,例如  |
| SaveHyperlinkAsRef | 使用引用式链接,例如 [Link Text][ref1] | 使用内联链接,例如 [text](https://example.com) |
对于文档项目而言,通常推荐使用相对路径保存图片,因为 Markdown 文件和图片目录可以一起移动。而对于较小的 Markdown 文件,内联链接通常更直观、易于维护。
以下示例演示如何使用自定义选项将 Excel 转换为 Markdown:
from spire.xls import Workbook, MarkdownOptions
# 创建 Workbook 对象
workbook = Workbook()
# 加载 Excel 文件
workbook.LoadFromFile("sample.xlsx")
# 创建 MarkdownOptions 对象
markdown_options = MarkdownOptions()
# 使用相对路径保存图片
markdown_options.SavePicInRelativePath = True
# 使用内联链接保存超链接
markdown_options.SaveHyperlinkAsRef = False
# 保存为 Markdown 文件
workbook.SaveToMarkdown("custom_options.md", markdown_options)
# 释放资源
workbook.Dispose()
输出效果:

如果 Excel 工作簿中包含多个工作表,而你只需要导出其中一个,可以先使用 AddCopy() 方法将目标工作表复制到新的工作簿中,然后再保存为 Markdown 文件。
这样可以避免将不相关的工作表一起导出。
from spire.xls import Workbook
def convert_specific_sheet(excel_file, sheet_name, output_md):
"""
将指定工作表转换为 Markdown
"""
workbook = Workbook()
new_workbook = None
try:
# 加载 Excel 文件
workbook.LoadFromFile(excel_file)
# 根据名称查找工作表
worksheet = None
for ws in workbook.Worksheets:
if ws.Name == sheet_name:
worksheet = ws
break
if worksheet is None:
print(f"未找到工作表 '{sheet_name}'。")
return
# 创建仅包含目标工作表的新工作簿
new_workbook = Workbook()
new_workbook.Worksheets.Clear()
new_workbook.Worksheets.AddCopy(worksheet)
# 保存为 Markdown
new_workbook.SaveToMarkdown(output_md)
print(f"工作表 '{sheet_name}' 已成功转换为 {output_md}。")
finally:
if new_workbook is not None:
new_workbook.Dispose()
workbook.Dispose()
# 使用示例
convert_specific_sheet("report.xlsx", "Sheet 1", "sheet1.md")
有时你只需要导出工作表中的某个区域,例如汇总表、数据统计区域或报表中的某个部分。这种情况下,可以将目标区域复制到新的工作簿中,再导出为 Markdown。
以下示例将特定工作表中的选定区域转换为 Markdown 文件:
from spire.xls import Workbook, CopyRangeOptions
def convert_cell_range_to_markdown(excel_file, sheet_name, cell_range, output_md):
"""
将指定单元格区域转换为 Markdown
示例区域:"A1:C5"
"""
workbook = Workbook()
new_workbook = Workbook()
try:
# 加载原始 Excel 文件
workbook.LoadFromFile(excel_file)
# 获取目标工作表
worksheet = workbook.Worksheets[sheet_name]
if worksheet is None:
print(f"未找到工作表 '{sheet_name}'。")
return
# 获取源区域
src_range = worksheet.Range[cell_range]
# 创建空白工作表
new_workbook.CreateEmptySheets(1)
new_sheet = new_workbook.Worksheets[0]
# 创建对应大小的目标区域
dest_range = new_sheet.Range[
1, 1, src_range.Rows.Count, src_range.Columns.Count
]
# 复制数据、公式和格式
src_range.Copy(dest_range, CopyRangeOptions.All)
# 保存为 Markdown
new_workbook.SaveToMarkdown(output_md)
print(
f"工作表 '{sheet_name}' 中的区域 '{cell_range}' "
f"已成功转换为 {output_md}。"
)
except Exception as e:
print(f"发生错误:{e}")
finally:
new_workbook.Dispose()
workbook.Dispose()
# 使用示例
convert_cell_range_to_markdown(
"report.xlsx", "Sheet 1", "A1:C5", "cell_range.md"
)
这种方式特别适合只提取工作表中的关键数据,而无需导出整个工作表。
对于大规模转换任务,你可以遍历文件夹,将所有 .xlsx 和 .xls 文件自动转换为 Markdown。
这在需要从多个报告生成文档、定期导出数据集或将 Excel 转 Markdown 集成到发布工作流时尤其有用。
from pathlib import Path
from spire.xls import Workbook
def batch_convert_excel_to_markdown(input_folder, output_folder):
"""
将文件夹中的所有 Excel 文件转换为 Markdown
支持格式:.xlsx、.xls
"""
input_dir = Path(input_folder)
output_dir = Path(output_folder)
# 创建输出目录
output_dir.mkdir(parents=True, exist_ok=True)
excel_extensions = {".xlsx", ".xls"}
converted_count = 0
for input_file in input_dir.iterdir():
if not input_file.is_file():
continue
if input_file.name.startswith("~$"):
continue
if input_file.suffix.lower() not in excel_extensions:
continue
output_file = output_dir / f"{input_file.stem}.md"
workbook = Workbook()
try:
workbook.LoadFromFile(str(input_file))
workbook.SaveToMarkdown(str(output_file))
converted_count += 1
print(f"已转换:{input_file.name} -> {output_file.name}")
except Exception as e:
print(f"转换 {input_file.name} 失败:{e}")
finally:
workbook.Dispose()
print(f"\n批量转换完成。共转换 {converted_count} 个文件。")
# 使用示例
batch_convert_excel_to_markdown("./excel_files", "./markdown_output")
为了获得更整洁的 Markdown 输出,建议遵循以下原则:
借助 Spire.XLS for Python,你可以通过极少量代码将 Excel 数据快速转换为 Markdown 文件。无论是文档生成、数据报表导出,还是自动化发布流程,这都是一种高效且实用的解决方案。
答:常见的 Excel 格式(如 .xlsx 和 .xls)都可以加载并保存为 Markdown 文件。
答:会。默认情况下,图片可以以 Base64 字符串形式嵌入 Markdown 输出中。你也可以配置导出选项,将图片保存为相对路径或绝对路径引用。
答:不需要。Spire.XLS for Python 独立运行,无需安装 Microsoft Excel 或 Microsoft Office。
如需完全体验 Spire.XLS for Python 的全部功能且无评估限制,您可以申请免费的 30 天试用许可证。
我们很高兴地宣布 Spire.PDF 12.6.9 正式发布。本版本优化了 PDF 文档转换为固定版式 Word 文件时的解析逻辑,并修复了多个在 PDF 转 DOCX、PDF 转 HTML 以及 XPS 转 PDF 过程中已知的问题。具体更新内容如下。
调整:
问题修复:
Spire.XLS for Java 16.6.5 现已正式发布。该版本增强了 Excel到PDF和图片的转换。同时,还修复了 Excel 转 PDF 及 Excel 转图片过程中的多个关键问题,提升了文档转换的稳定性和准确性。详情如下。
问题修复:
Spire.PDF for Java 12.6.4 现已正式发布。该版本新增了对 PDF 阅读方向与语言设置的支持,并为 PDF 签名操作提供了更灵活的格式控制能力。此外,两处与签名验证、图像渲染相关的已知问题也已成功被修复。 详情请查阅以下内容。
新功能:
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile(inputFile);
pdf.getViewerPreferences().setReadingDirection(PdfReadingDirection.LeftToRight);
pdf.setLanguage("zh-CN");
pdf.saveToFile(outputFile , FileFormat.PDF);
PdfDocument document = new PdfDocument();
PdfPageBase pdfPageBase = document.getPages().add();
pdfPageBase.getCanvas().drawString("Hello, World!",
new PdfFont(PdfFontFamily.Helvetica, 30f),
PdfBrushes.getBlack(), 10, 10);
PdfCertificate certificate = new PdfCertificate(path+"gary.pfx", "e-iceblue");
PdfPKCS7Formatter formatter = new PdfPKCS7Formatter(certificate, false);
String timeStampUrl = "https://rfc3161.ai.moda/adobe";
formatter.setTimestampService(new TSAHttpService(timeStampUrl));
formatter.setOCSPService(new OCSPHttpService(null));
PdfMDPSignatureMaker signatureMaker = new PdfMDPSignatureMaker (document, formatter, PdfMDPSignatureMaker.Level2Permissions);
signatureMaker.makeSignature("signName");
com.spire.pdf.interactive.digitalsignatures.PdfSignature signature = signatureMaker.getSignature();
signature.setName("Gary");
// signature.setReason("This is the final version.");
signature.setLocation("U.S.");
signature.setContactInfo("112554");
PdfSignatureAppearance appearance = new PdfSignatureAppearance(signature);
appearance.setNameLabel("Signer: ");
// appearance.setReasonLabel("Reason: ");
appearance.setLocationLabel("Location: ");
appearance.setContactInfoLabel("Phone: ");
PdfImage image = PdfImage.fromFile(path+"logo.png");
appearance.setSignatureImage(image);
appearance.setGraphicMode(GraphicMode.SignImageAndSignDetail);
Rectangle2D rect = new Rectangle2D.Float();
rect.setFrame(new Point2D.Float(90, 550), new Dimension(150, 80));
signatureMaker.makeSignature("Signature", pdfPageBase,
(float) rect.getMinX(), (float) rect.getMinY(),
(float) rect.getWidth(), (float) rect.getHeight(),
appearance);
String output = "signature.pdf";
document.saveToFile(path+output, FileFormat.PDF);
document.close();
问题修复:
Spire.PDF for C++ 12.6.1 现已正式发布。该版本增强了从 XPS 到 PDF 的转换功能。同时,一些在转换 PDF 到 PdfX1A2001 标准以及合并 PDF 文档时出现的问题也得以成功修复。更多详情如下。
问题修复:

CSV 作为轻量级、跨平台的通用数据格式,是 Java 开发中报表导出、数据迁移、系统间数据交互的主流选择。但看似简单的 CSV 格式,在生产级开发中往往暗藏诸多问题:逗号、换行符、双引号等特殊字符会破坏文件结构,多语言场景下编码错误会导致乱码,手动处理格式规则不仅开发成本高,还容易出现兼容性隐患。
Spire.XLS for Java 提供了简洁且高可靠性的 CSV 处理 API,可自动完成格式转义、编码适配等底层工作,大幅降低 CSV 生成的开发与测试成本。本文将系统讲解如何基于 Java 创建 CSV 文件,覆盖基础文件生成、结构化批量导出、Excel 转 CSV 转换,以及特殊字符处理、自定义分隔符等进阶用法。
对比原生 Java IO、Apache POI 及其他 Java CSV 库,Spire.XLS for Java 在 CSV 处理场景下具备以下核心优势
要开始使用 Java 写入 CSV 文件,首先需要将该库集成到项目中,支持 Maven 依赖引入和手动安装 JAR 包两种方式。
将以下仓库和依赖添加到项目的 pom.xml 文件中:
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.xls</artifactId>
<version>16.4.1</version>
</dependency>
对于非 Maven 项目可从 Spire.XLS for Java 官网下载 JAR 文件,并将其添加到项目的构建路径中。
这是最基础的使用场景,适用于数据量小、结构简单的 CSV 生成需求。以下示例将创建一个商品信息 CSV 文件,包含表头与多行数据,默认使用逗号分隔符与 UTF-8 编码。
import com.spire.xls.*;
import java.nio.charset.Charset;
public class CreateBasicCSV {
public static void main(String[] args) {
// 创建 Workbook 实例
Workbook workbook = new Workbook();
// 获取第一个工作表(索引为 0)
Worksheet sheet = workbook.getWorksheets().get(0);
// 写入表头行
sheet.getCellRange("A1").setValue("ID");
sheet.getCellRange("B1").setValue("商品名");
sheet.getCellRange("C1").setValue("价格");
sheet.getCellRange("D1").setValue("数量");
sheet.getCellRange("E1").setValue("品类");
// 写入数据行
sheet.getCellRange("A2").setNumberValue(1001);
sheet.getCellRange("B2").setValue("无线鼠标");
sheet.getCellRange("C2").setNumberValue(29.99);
sheet.getCellRange("D2").setNumberValue(150);
sheet.getCellRange("E2").setValue("电子产品");
sheet.getCellRange("A3").setNumberValue(1002);
sheet.getCellRange("B3").setValue("机械键盘");
sheet.getCellRange("C3").setNumberValue(89.99);
sheet.getCellRange("D3").setNumberValue(75);
sheet.getCellRange("E3").setValue("电子产品");
sheet.getCellRange("A4").setNumberValue(1003);
sheet.getCellRange("B4").setValue("办公椅");
sheet.getCellRange("C4").setNumberValue(199.99);
sheet.getCellRange("D4").setNumberValue(30);
sheet.getCellRange("E4").setValue("家具");
// 将工作表另存为 CSV 文件(逗号分隔符 + UTF-8 编码)
sheet.saveToFile("商品信息.csv", ",", Charset.forName("UTF-8"));
// 释放资源
workbook.dispose();
}
}
核心 API 说明:
setValue():向单元格中写入文本或字符串值。setNumberValue():向单元格中写入数值(支持整数、小数)。saveToFile(filename, separator, charset):将工作表导出为 CSV 文件,可自定义分隔符和编码。在 Excel 中打开生成的 CSV 文件:

拓展:Spire.XLS for Java 同时支持 Java 读取 CSV 文件,单一库即可完成 CSV 的读写双向处理。
在实际开发中,通常需要将业务数据(如用户列表、订单记录)批量写入 CSV 文件。本示例演示如何利用一维和二维数组,创建包含固定表头和批量结构化数据的标准化 CSV 文件。
import com.spire.xls.*;
import java.nio.charset.Charset;
public class CreateStructuredCSV {
public static void main(String[] args) {
Workbook workbook = new Workbook();
Worksheet sheet = workbook.getWorksheets().get(0);
// 定义 CSV 表头行
String[] headers = {"订单号", "顾客名", "订单金额", "订单日期", "订单状态"};
for (int i = 0; i < headers.length; i++) {
sheet.getCellRange(1, i + 1).setValue(headers[i]);
}
// 批量写入订单数据
String[][] orderData = {
{"ORD001", "张三", "299.99", "2026-06-01", "已完成"},
{"ORD002", "李四", "599.50", "2026-06-05", "已取消"},
{"ORD003", "王五", "129.00", "2026-06-08", "已发货"}
};
// 遍历并写入批量数据
int rowNum = 2;
for (String[] rowData : orderData) {
for (int col = 0; col < rowData.length; col++) {
sheet.getCellRange(rowNum, col + 1).setValue(rowData[col]);
}
rowNum++;
}
// 保存结构化 CSV 文件
sheet.saveToFile("订单信息.csv", ",", Charset.forName("UTF-8"));
workbook.dispose();
}
}
与逐个写入单元格的方式相比,数组遍历的写法更适合批量数据导出,且可无缝适配 List<List<String>>、实体类集合等动态数据源,仅需调整数据遍历逻辑即可。
生成效果:

当已有 Excel 格式的数据,需要导出为 CSV 格式用于数据迁移或系统导入时,仅需三行核心代码即可完成转换。
import com.spire.xls.*;
import java.nio.charset.Charset;
public class ExcelToCSV {
public static void main(String[] args) {
// 加载 Excel 文件
Workbook workbook = new Workbook();
workbook.loadFromFile("示例.xlsx");
// 获取第一个工作表
Worksheet sheet = workbook.getWorksheets().get(0);
// 将工作表另存为 CSV
sheet.saveToFile("转换结果.csv", ",", Charset.forName("UTF-8"));
workbook.dispose();
}
}
核心逻辑十分简单:加载 Excel 文件 → 定位到指定工作表 → 调用转换 API 另存为 CSV。
Excel 转 CSV 效果:
反向操作:你也可以实现 CSV 文件转 Excel 工作簿,为纯文本 CSV 数据添加样式、公式、多工作表等高级能力。
CSV 格式中,逗号、双引号属于特殊字符,手动处理不当会导致列错位、数据错乱。Spire.XLS for Java 严格遵循 RFC 4180 规范,自动为包含特殊字符的字段添加双引号包裹,无需手动编写转义逻辑,完美兼容 Excel、WPS、各类数据导入工具。
代码示例:
// 内容包含逗号、双引号
sheet.getCellRange("A1").setValue("人体工学办公椅,静音滚轮 | \"2026新款·加大加宽款\"");

除标准逗号分隔符外,Spire.XLS for Java 支持自定义分隔符与编码格式,适配不同地区、不同系统的格式要求:
代码示例:
// 使用分号作为分隔符,UTF-16 作为编码
sheet.saveToFile("european_data.csv", ";", Charset.forName("UTF-16"));
// 使用制表符作为分隔符生成 TSV 文件
sheet.saveToFile("tab_separated.tsv", "\t", Charset.forName("UTF-8"));
不需要。该库可完全独立运行,无任何 Office 依赖。
可以。实现逻辑为:加载现有 CSV 文件到工作表中,定位到最后一行已使用数据的行号,从下一行开始写入新数据,最后重新保存文件即可。
不能。CSV 本质上是纯文本的单工作表格式。每次调用 saveToFile() 方法只会将一个工作表导出为一个 CSV 文件。若要导出多个工作表,需为每个工作表分别调用保存方法,生成独立的 CSV 文件。
Spire.XLS for Java 提供商业版和免费版。免费版虽有一定的使用限制,但完全支持基础的 CSV 操作和轻量级电子表格处理任务。
生成 CSV 文件是 Java 开发中一项常规但关键的任务。CSV 输出的质量和可靠性直接影响报表、系统迁移、数据分析等下游流程。要实现无错误的 CSV 生成,需要一个能自动处理格式、编码和特殊字符的库。
Spire.XLS for Java 恰好满足这一需求。通过本文提供的分步代码示例,开发者可以快速将可靠的 CSV 生成功能集成到 Java 项目中,提升开发效率,同时消除常见的格式缺陷和编码错误。 如需了解更多高级功能(如将 CSV 转换为 PDF),可查阅 Spire.XLS for Java 在线教程。

PDF 因其跨平台格式一致性,广泛用于报告、发票和电子表单等场景。然而,由于其固定布局的特性,如果没有专业工具,编辑 PDF 往往并不容易。对于希望使用 Python 编辑 PDF 的开发者来说,Spire.PDF for Python 提供了一套功能全面且易于使用的解决方案。借助这个 Python PDF 编辑库,您可以通过编程方式修改 PDF 文件,包括编辑文本、替换图片、添加批注、处理表单以及设置文档安全权限,而无需依赖 Adobe Acrobat 或其他第三方软件。
在本文中,我们将探讨如何使用 Spire.PDF for Python 在 Python 应用程序中实现 PDF 编辑功能。
Python 是一种功能强大且用途广泛的编程语言,非常适合实现 PDF 文档的自动化处理与管理。在执行 PDF 编辑任务时,Spire.PDF for Python 是一个全面且易于上手的解决方案。
Spire.PDF for Python 是一个独立的 PDF 处理库,无需安装任何外部软件即可创建、读取、编辑、转换和保存 PDF 文件。
主要功能包括:
如需了解更多功能,请参考 Spire.PDF for Python 官方教程。
凭借直观易用的 API 设计,Spire.PDF 能帮助开发者快速、高效地完成 PDF 编辑任务。
要安装 Spire.PDF for Python,只需运行以下 pip 命令:
pip install spire.pdf
如果需要免费版本,可以安装:
pip install spire.pdf.free
您也可以从官方网站手动下载该库。
下面的示例演示如何创建一个简单的 PDF 文档:
from spire.pdf import PdfDocument, PdfFont, PdfBrushes, PdfFontFamily, PdfFontStyle
# 创建 PDF 文档
pdf = PdfDocument()
# 添加页面
page = pdf.Pages.Add()
# 创建字体
font = PdfFont(PdfFontFamily.TimesRoman, 28.0, PdfFontStyle.Bold)
# 创建画刷
brush = PdfBrushes.get_Black()
# 绘制文本
page.Canvas.DrawString("Hello, World", font, brush, 100.0, 100.0)
# 保存文档
pdf.SaveToFile("output/NewPDF.pdf")
pdf.Close()
运行结果:
生成的 PDF 使用 Times Roman 粗体显示文本 "Hello, World"。

安装完成后,您就具备使用 Python 编辑 PDF 的基础环境。接下来将介绍如何编辑 PDF 的结构、内容、安全设置以及元数据。
Spire.PDF for Python 提供了简单而强大的 API,可帮助开发者以编程方式编辑 PDF。凭借其直观的 API,开发者可以自动化各种 PDF 编辑任务,包括修改文档结构、页面内容、安全设置和属性。下面将分别介绍这些常见场景。
结构编辑允许您调整页面顺序、合并文件或插入/删除页面,非常适用于文档整理和批量生成场景。
通过 PdfDocument 类中的 Pages.Insert() 和 Pages.RemoveAt() 方法,可以在指定位置插入或删除页面。
代码示例
from spire.pdf import PdfDocument, PdfPageSize, PdfMargins, PdfPageRotateAngle
# 加载 PDF
pdf = PdfDocument()
pdf.LoadFromFile("Sample.pdf")
# 插入页面
pdf.Pages.Insert(0, PdfPageSize.A4(), PdfMargins(50.0, 60.0), PdfPageRotateAngle.RotateAngle90)
# 删除第二页
pdf.Pages.RemoveAt(1)
# 保存
pdf.SaveToFile("output/InsertDeletePage.pdf")
pdf.Close()
输出结果:

使用 AppendPage() 方法可将多个 PDF 合并为一个文件。
代码示例
import os
from spire.pdf import PdfDocument
# 指定 PDF 文件路径
pdfPath = "PDFs/"
# 从路径读取 PDF 文件名并添加到列表
files = [pdfPath + file for file in os.listdir(pdfPath) if file.endswith(".pdf")]
# 加载第一个 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile(files[0])
# 遍历其他 PDF 文件
for i in range(1, len(files)):
# 加载当前 PDF 文件
pdf2 = PdfDocument()
pdf2.LoadFromFile(files[i])
# 将当前 PDF 文件的页面追加到第一个 PDF 文件
pdf.AppendPage(pdf2)
# 保存合并后的 PDF 文件
pdf.SaveToFile("output/MergePDFs.pdf")
pdf.Close()
运行结果:

您可能也感兴趣:使用 Python 代码拆分 PDF 文件
作为一个功能完善的 Python PDF 编辑器,Spire.PDF 支持多种内容级操作,包括修改文本、图像、注释和交互式表单。
通过 PdfTextReplacer 类,可以查找并替换页面中的指定文本。请注意,精确替换可能需要考虑大小写和布局。
示例代码
from spire.pdf import PdfDocument, PdfTextReplacer, ReplaceActionType, Color
# 加载 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile("Sample.pdf")
# 遍历所有页面
for i in range(pdf.Pages.Count):
page = pdf.Pages.get_Item(i)
# 创建 PdfTextReplacer 对象
replacer = PdfTextReplacer(page)
# 设置替换选项
replacer.Options.ReplaceType = ReplaceActionType.IgnoreCase
# 替换文本
replacer.ReplaceAllText("机器人", "无人机")
# 保存 PDF 文件
pdf.SaveToFile("output/ReplaceText.pdf")
pdf.Close()
运行结果:

使用 PdfImageHelper 类可以快速替换 PDF 中的图片。通过检索特定页面的图像信息,您可以使用 ReplaceImage() 方法直接用新图像替换原图像。
示例代码
from spire.pdf import PdfDocument, PdfImageHelper, PdfImage
# 加载 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile("Sample.pdf")
# 获取一个页面
page = pdf.Pages.get_Item(0)
# 创建 PdfImageHelper 实例
imageHelper = PdfImageHelper()
# 获取页面上第一张图像的信息
imageInfo = imageHelper.GetImagesInfo(page)[0]
# 加载新图像
newImage = PdfImage.FromFile("Image.png")
# 替换图像
imageHelper.ReplaceImage(imageInfo, newImage)
# 保存 PDF 文件
pdf.SaveToFile("output/ReplaceImage.pdf")
pdf.Close()
运行结果:

要使用 Python 添加注释或笔记,请使用 PdfTextMarkupAnnotation 类并将其添加到页面的 AnnotationsWidget 集合中。
from spire.pdf import PdfDocument, PdfTextFinder, PdfTextMarkupAnnotation, PdfRGBColor, Color
# 加载 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile("Sample.pdf")
# 获取一个页面
page = pdf.Pages.get_Item(0)
# 创建 PdfTextFinder 实例并设置选项
finder = PdfTextFinder(page)
finder.Options.Parameter.IgnoreCase = False
finder.Options.Parameter.WholeWord = True
# 查找要添加注释的文本
text = finder.Find("充当思维的“外挂硬盘”")[0]
# 获取文本边界
bound = text.Bounds[0]
# 添加注释
commentText = ("此处指生成式AI的\"发散性\"能力。与人类线性思维不同,"
"AI能够在高维空间中通过概率采样生成大量看似不相关但逻辑自洽的创意组合,"
"为创作者提供超出个人经验范围的参考素材,从而降低\"创意枯竭\"的发生概率。")
comment = PdfTextMarkupAnnotation("Commenter", commentText, bound)
comment.TextMarkupColor = PdfRGBColor(Color.get_Yellow())
page.AnnotationsWidget.Add(comment)
# 保存 PDF 文件
pdf.SaveToFile("output/CommentNote.pdf")
pdf.Close()
结果:

Spire.PDF 支持读取和填写 PDF 表单字段。通过访问 PdfFormWidget 对象的 FieldsWidget 属性,您可以遍历所有交互式表单元素(如文本框、组合框和复选框),并更新或提取其值。
代码示例
from spire.pdf import PdfDocument, PdfFormWidget, PdfComboBoxWidgetFieldWidget, PdfCheckBoxWidgetFieldWidget, PdfTextBoxFieldWidget
# 加载 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile("员工信息登记表.pdf")
forms = pdf.Form
formWidgets = PdfFormWidget(forms).FieldsWidget
# 填写表单
for i in range(formWidgets.Count):
formField = formWidgets.get_Item(i)
if formField.Name == "姓名":
textBox = PdfTextBoxFieldWidget(formField)
textBox.Text = "张三"
elif formField.Name == "出生日期":
textBox = PdfTextBoxFieldWidget(formField)
textBox.Text = "1985年3月15日"
elif formField.Name == "性别":
comboBox = PdfComboBoxWidgetFieldWidget(formField)
comboBox.SelectedIndex = [ 0 ]
elif formField.Name == "同意条款":
checkBox = PdfCheckBoxWidgetFieldWidget(formField)
checkBox.Checked = True
# 读取表单值
formValues = []
for i in range(formWidgets.Count):
formField = formWidgets.get_Item(i)
if isinstance(formField, PdfTextBoxFieldWidget):
formValues.append(formField.Name + ": " + formField.Text)
elif isinstance(formField, PdfComboBoxWidgetFieldWidget):
formValues.append(formField.Name + ": " + formField.SelectedValue)
elif isinstance(formField, PdfCheckBoxWidgetFieldWidget):
formValues.append(formField.Name + ": " + str(formField.Checked))
# 将表单值写入文件
with open("output/FormValues.txt", "w") as file:
file.write("\n".join(formValues))
# 保存 PDF 文件
pdf.SaveToFile("output/FilledForm.pdf")
pdf.Close()
运行结果:

对于包含敏感信息的文档,PDF 安全控制至关重要。Spire.PDF 支持加密、密码保护、数字签名处理和权限设置。
使用 Encrypt() 方法可以为 PDF 添加密码,并限制打印、复制等操作权限。
代码示例
from spire.pdf import PdfDocument, PdfEncryptionAlgorithm, PdfDocumentPrivilege, PdfPasswordSecurityPolicy
# 加载 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile("员工信息登记表.pdf")
# 创建 PdfSecurityPolicy 对象并设置密码和加密算法
securityPolicy = PdfPasswordSecurityPolicy("userPSD", "ownerPSD")
securityPolicy.EncryptionAlgorithm = PdfEncryptionAlgorithm.AES_128
# 设置文档权限
pdfPrivileges = PdfDocumentPrivilege.ForbidAll()
pdfPrivileges.AllowPrint = True
pdfPrivileges.AllowFillFormFields = True
# 应用文档权限
securityPolicy.DocumentPrivilege = pdfPrivileges
# 使用安全策略加密 PDF
pdf.Encrypt(securityPolicy)
# 保存 PDF 文件
pdf.SaveToFile("output/EncryptedForm.pdf")
pdf.Close()
运行结果:

要打开受保护的文件,请在调用 LoadFromFile() 时提供用户密码,使用 Decrypt() 解密文档,然后重新保存为无保护状态。
代码示例
from spire.pdf import PdfDocument
# 使用所有者密码加载加密的 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile("output/EncryptedForm.pdf", "ownerPSD")
# 解密 PDF 文件
pdf.Decrypt()
# 保存 PDF 文件
pdf.SaveToFile("output/DecryptedForm.pdf")
pdf.Close()
为您推荐:使用 Python 在 PDF 中添加和移除数字签名
Spire.PDF 还支持读取和修改 PDF 元数据以及查看器设置。
通过 PDF 文档的 DocumentInformation 属性更新元数据,如标题、作者或主题。
代码示例
from spire.pdf import PdfDocument
# 加载 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile("员工信息登记表.pdf")
# 设置文档元数据
pdf.DocumentInformation.Author = "张三"
pdf.DocumentInformation.Title = "员工信息登记表"
pdf.DocumentInformation.Producer = "Spire.PDF"
# 保存 PDF 文件
pdf.SaveToFile("output/EditProperties.pdf")
pdf.Close()
输出结果:

通过 ViewerPreferences 属性,可以自定义 PDF 的显示方式。
代码示例
from spire.pdf import PdfDocument, PdfPageLayout, PrintScalingMode
# 加载 PDF 文件
pdf = PdfDocument()
pdf.LoadFromFile("员工信息登记表.pdf")
# 设置查看器首选项
pdf.ViewerPreferences.DisplayTitle = True
pdf.ViewerPreferences.HideToolbar = True
pdf.ViewerPreferences.HideWindowUI = True
pdf.ViewerPreferences.FitWindow = False
pdf.ViewerPreferences.HideMenubar = True
pdf.ViewerPreferences.PrintScaling = PrintScalingMode.AppDefault
pdf.ViewerPreferences.PageLayout = PdfPageLayout.OneColumn
# 保存 PDF 文件
pdf.SaveToFile("output/EditViewerPreference.pdf")
pdf.Close()
输出结果:

借助 Spire.PDF for Python,使用 Python 编辑 PDF 既实用又高效。无论您是在构建自动化工具、处理电子表单,还是为敏感文档设置安全保护,Spire.PDF 都能提供完整的 PDF 编辑能力,并通过简洁易用的 API 快速实现各种功能。
从内容编辑、表单交互,到文档结构管理和安全控制,Spire.PDF for Python 为开发者和企业提供了一套完善的 PDF 处理解决方案,可帮助您显著提升 PDF 工作流的自动化水平。
答:可以。Python 拥有多种 PDF 处理库,例如 Spire.PDF for Python,支持编辑文本、图片、表单、批注以及安全设置等功能。
答:使用 Spire.PDF for Python 等库,可以加载现有 PDF,以编程方式修改其内容或结构,并仅用几行代码保存更改。
答:Spire.PDF for Python 是一个热门选择。它提供了全面的功能,用于创建、读取、编辑、转换和保护 PDF 文档,无需额外软件。
答:可以。您可以使用 Spire.PDF for Python 免费版编辑 PDF 文件。但免费版存在一定限制,例如每个文档最多处理 10 页。此外,您可以申请 30 天临时授权许可证,在测试期间解除所有功能限制和水印。
我们很高兴地宣布 Spire.Doc for Python 14.6.2 正式发布。本次更新新增了通过环境变量配置许可证的支持。详细更新内容如下。
新功能:
SPIRE_OFFICE_LICENSE_KEY
SPIRE_DOC_LICENSE_KEY
SPIRE_LICENSE_KEY
Spire.XLS for .NET 16.6.3 现已发布。该版本对 Worksheet.SaveToPdf() 方法进行了调整,移除了传递的 FileFormat 参数。详情如下。
调整: