Spire.PDF 12.7.8 现已正式发布。该版本修复了转换 OFD 到 PDF 时出现 “OFD 解析失败”异常的问题,同时提升了 PDF 文档处理的稳定性。详情如下。
问题修复:
Spire.Presentation for Java 11.7.2 已发布。该版本支持转换 PPT 到纯 HTML 格式,并修复了两个在转换 PPT 到 PDF 时出现的问题。详情请查看以下内容。
新功能:
Presentation ppt = new Presentation();
ppt.loadFromFile(inputFile);
ppt.getSaveToHtmlOption().setPureHtml(true);
ppt.saveToFile(outputFile, FileFormat.HTML);
ppt.dispose();
问题修复:
https://www.e-iceblue.cn/Downloads/Spire-Presentation-JAVA.html
Spire.XLS 16.7.0 现已正式发布。该版本成功修复了复制工作表时宏丢失、枚举类型单词拼写错误以及公式计算结果不正确的问题。更多详情如下。
问题修复:
Spire.XLS for Python 16.7.1 现已发布。该版本新增支持数据模拟分析(方案管理器)、Slicer 切片器以及 Excel 转 JSON 数据功能,进一步扩展了数据分析与处理能力。详情如下。
新功能:
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
scenarios = worksheet.Scenarios
currentChangePercentage_Values = [0.23, 0.8, 1.1, 0.5, 0.35, 0.2]
increasedChangePercentage_Values = [0.45, 0.56, 0.9, 0.5, 0.58, 0.43]
decreasedChangePercentage_Values = [0.3, 0.2, 0.5, 0.3, 0.5, 0.23]
currentQuantity_Values = [1500, 3000, 5000, 4000, 500, 4000]
increasedQuantity_Values = [1000, 5000, 4500, 3900, 10000, 8900]
decreasedQuantity_Values = [1000, 2000, 3000, 3000, 300, 4000]
scenarios.Add("Current % of Change", worksheet.Range["F5:F10"], currentChangePercentage_Values)
scenarios.Add("Increased % of Change", worksheet.Range["F5:F10"], increasedChangePercentage_Values)
scenarios.Add("Decreased % of Change", worksheet.Range["F5:F10"], decreasedChangePercentage_Values)
scenarios.Add("Current Quantity", worksheet.Range["D5:D10"], currentQuantity_Values)
scenarios.Add("Increased Quantity", worksheet.Range["D5:D10"], increasedQuantity_Values)
scenarios.Add("Decreased Quantity", worksheet.Range["D5:D10"], decreasedQuantity_Values)
wb.SaveToFile(outputFile, ExcelVersion.Version2013)
wb.Dispose()
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
scenarios = worksheet.Scenarios
currentChangePercentage_Values = [0.23, 0.8, 1.1, 0.5, 0.35, 0.2]
increasedChangePercentage_Values = [0.45, 0.56, 0.9, 0.5, 0.58, 0.43]
decreasedChangePercentage_Values = [0.3, 0.2, 0.5, 0.3, 0.5, 0.23]
currentQuantity_Values = [1500, 3000, 5000, 4000, 500, 4000]
increasedQuantity_Values = [1000, 5000, 4500, 3900, 10000, 8900]
decreasedQuantity_Values = [1000, 2000, 3000, 3000, 300, 4000]
scenarios.Add("Current % of Change", worksheet.Range["F5:F10"], currentChangePercentage_Values)
scenarios.Add("Increased % of Change", worksheet.Range["F5:F10"], increasedChangePercentage_Values)
scenarios.Add("Decreased % of Change", worksheet.Range["F5:F10"], decreasedChangePercentage_Values)
scenarios.Add("Current Quantity", worksheet.Range["D5:D10"], currentQuantity_Values)
scenarios.Add("Increased Quantity", worksheet.Range["D5:D10"], increasedQuantity_Values)
scenarios.Add("Decreased Quantity", worksheet.Range["D5:D10"], decreasedQuantity_Values)
worksheet.Scenarios.Summary(worksheet.Range["L7"])
wb.SaveToFile(outputFile, ExcelVersion.Version2013)
wb.Dispose()
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
scenarios = worksheet.Scenarios
scenario1 = scenarios[0]
scenario2 = scenarios[1]
scenario1.SetVariableCells(worksheet.Range["A1:A5"], scenario2.Values)
sourceCell = worksheet.Range["B1:B5"]
scenario2.SetVariableCells(sourceCell, scenario2.Values)
scenario1.Show()
scenario2.Show()
self.get_content(scenario1, outputFile_TXT)
wb.SaveToFile(outputFile, ExcelVersion.Version2013)
wb.Dispose()
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
scenarios = worksheet.Scenarios
scenarios.RemoveScenarioAt(0)
scenarios.RemoveScenarioByName("two")
content = ""
content += "Count:" + str(scenarios.Count) + "\n"
content += "ContainsScenario:" + str(scenarios.ContainsScenario("two")) + "\n"
content += "ContainsScenario:" + str(scenarios.ContainsScenario("one")) + "\n"
with open(outputFile, 'w') as f:
f.write(content)
wb.Dispose()
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
scenarios = worksheet.Scenarios
scenarios.RemoveScenarioAt(0)
scenarios.RemoveScenarioByName("two")
content = ""
content += "Count:" + str(scenarios.Count) + "\n"
content += "ContainsScenario:" + str(scenarios.ContainsScenario("two")) + "\n"
content += "ContainsScenario:" + str(scenarios.ContainsScenario("one")) + "\n"
with open(outputFile, 'w') as f:
f.write(content)
wb.Dispose()
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
slicers = worksheet.Slicers
table = worksheet.ListObjects.Create("Super Table", worksheet.Range["A1:C9"])
count = 3
for type in SlicerStyleType.__dict__.values():
if isinstance(type, SlicerStyleType):
count += 5
rangeStr = "E" + str(count)
index = slicers.Add(table, rangeStr, 0)
xlsSlicer = slicers[index]
xlsSlicer.Name = "slicers_" + str(count)
xlsSlicer.StyleType = type
wb.SaveToFile(outputFile_xlsx, ExcelVersion.Version2013)
wb.Dispose()
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
pt = sheet.PivotTables[0]
slicers = worksheet.Slicers
index = slicers.Add(pt, "E12", 0)
xlsSlicer = slicers[index]
xlsSlicer.Name = "test_xlsSlicer"
xlsSlicer.Width = 100
xlsSlicer.Height = 120
xlsSlicer.StyleType = SlicerStyleType.SlicerStyleLight2
xlsSlicer.PositionLocked = True
slicerCache = xlsSlicer.SlicerCache
slicerCache.CrossFilterType = SlicerCacheCrossFilterType.ShowItemsWithNoData
slicerCacheItems = xlsSlicer.SlicerCache.SlicerCacheItems
xlsSlicerCacheItem = slicerCacheItems[0]
xlsSlicerCacheItem.Selected = False
slicers_2 = worksheet.Slicers
r1 = pt.PivotFields["year"]
index_2 = slicers_2.Add(pt, "I12", r1)
xlsSlicer_2 = slicers[index_2]
xlsSlicer_2.RowHeight = 40
xlsSlicer_2.StyleType = SlicerStyleType.SlicerStyleLight3
xlsSlicer_2.PositionLocked = False
slicerCache_2 = xlsSlicer_2.SlicerCache
slicerCache_2.CrossFilterType = SlicerCacheCrossFilterType.ShowItemsWithDataAtTop
slicerCacheItems_2 = xlsSlicer_2.SlicerCache.SlicerCacheItems
xlsSlicerCacheItem_2 = slicerCacheItems_2[1]
xlsSlicerCacheItem_2.Selected = False
pt.CalculateData()
wb.SaveToFile(outputFile_xlsx, ExcelVersion.Version2013)
wb.Dispose()
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet_1 = wb.Worksheets[0]
slicers = worksheet_1.Slicers
slicers.RemoveAt(0)
slicer = worksheet_1.Slicers[1]
worksheet_1.Slicers.Remove(slicer)
worksheet_2 = wb.Worksheets[2]
worksheet_2.Slicers.Clear()
wb.SaveToFile(outputFile_xlsx, ExcelVersion.Version2013)
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
slicers = worksheet.Slicers
xlsSlicer = slicers[0]
xlsSlicer.StyleType = SlicerStyleType.SlicerStyleDark4
xlsSlicer.Caption = "Slicer"
xlsSlicer.PositionLocked = True
slicerCacheItems = xlsSlicer.SlicerCache.SlicerCacheItems
xlsSlicerCacheItem = slicerCacheItems[0]
xlsSlicerCacheItem.Selected = False
displayValue = xlsSlicerCacheItem.DisplayValue
slicerCache = xlsSlicer.SlicerCache
slicerCache.CrossFilterType = SlicerCacheCrossFilterType.ShowItemsWithNoData
wb.SaveToFile(outputFile_xlsx, ExcelVersion.Version2013)
wb = Workbook()
wb.LoadFromFile(inputFile)
worksheet = wb.Worksheets[0]
slicers = worksheet.Slicers
content = ""
content += "slicers.Count:" + str(slicers.Count) + "\n"
xlsSlicer = slicers[1]
content += "xlsSlicer.Name:" + xlsSlicer.Name + "\n"
content += "xlsSlicer.Caption:" + xlsSlicer.Caption + "\n"
content += "xlsSlicer.NumberOfColumns:" + str(xlsSlicer.NumberOfColumns) + "\n"
content += "xlsSlicer.ColumnWidth:" + str(xlsSlicer.ColumnWidth) + "\n"
content += "xlsSlicer.RowHeight:" + str(xlsSlicer.RowHeight) + "\n"
content += "xlsSlicer.ShowCaption:" + str(xlsSlicer.ShowCaption) + "\n"
content += "xlsSlicer.PositionLocked:" + str(xlsSlicer.PositionLocked) + "\n"
content += "xlsSlicer.Width:" + str(xlsSlicer.Width) + "\n"
content += "xlsSlicer.Height:" + str(xlsSlicer.Height) + "\n"
slicerCache = xlsSlicer.SlicerCache
content += "slicerCache.SourceName:" + slicerCache.SourceName + "\n"
content += "slicerCache.IsTabular:" + str(slicerCache.IsTabular) + "\n"
content += "slicerCache.Name:" + slicerCache.Name + "\n"
slicerCacheItems = slicerCache.SlicerCacheItems
xlsSlicerCacheItem = slicerCacheItems[1]
content += "xlsSlicerCacheItem.Selected:" + str(xlsSlicerCacheItem.Selected) + "\n"
with open(outputFile_T, 'w') as f:
f.write(content)
wb.Dispose()
workbook = Workbook()
workbook.LoadFromFile(inputFile)
workbook.SaveToFile(outputFile, FileFormat.Json)
workbook.Dispose()
PDF/A 是 ISO 标准化的长期归档格式,它将文档中的字体、色彩和元数据固化到统一的合规层级,确保文档在未来数十年内无论使用何种 PDF 阅读器都能完整重现。与之相对,普通 PDF 在日常编辑和内容提取上更加灵活。实际业务中通常需要在两种格式间灵活切换:合同归档时转为 PDF/A 以满足法规要求,审计复核时又需还原为普通 PDF 以提取文本内容。
Spire.PDF for JavaScript 基于 WebAssembly 在浏览器端直接完成 PDF 与 PDF/A 之间的双向转换,通过虚拟文件系统(VFS)管理输入输出文件,无需后端服务支持。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.PDF for JavaScript。以下示例默认已安装 Spire.PDF 并完成 WebAssembly 模块初始化。
PDF/A 归档转换的核心在于将普通 PDF 中的字体、色彩和元数据固化到符合 ISO 标准的层级。Spire.PDF 通过 PdfStandardsConverter 组件一键完成此操作,产品支持 PDF/A-1a、PDF/A-1b、PDF/A-2a、PDF/A-2b、PDF/A-3a、PDF/A-3b 等多个合规级别。
PdfStandardsConverter 支持的转换标准及其适用场景如下:
| 方法 | 标准 | 说明 |
|---|---|---|
ToPdfA1B |
PDF/A-1b | 基于 PDF 1.4,仅保证视觉外观的可再现性,是最常用的归档级别 |
ToPdfA1A |
PDF/A-1a | 在 1b 基础上要求文档标签与结构信息,支持无障碍阅读 |
ToPdfA2A |
PDF/A-2a | 基于 PDF 1.7,要求标签与结构信息,支持图层和透明度 |
ToPdfA2B |
PDF/A-2b | PDF/A-2 基本合规级别,允许透明度、图层和嵌入 OLE 对象 |
ToPdfA3A |
PDF/A-3a | 在 2a 基础上允许嵌入 XML、Excel 等任意格式文件作为附件 |
ToPdfA3B |
PDF/A-3b | PDF/A-3 基本合规级别,支持嵌入任意格式附件 |
ToPdfX1A2001 |
PDF/X-1a:2001 | 印刷交换标准,适合出版和打印工作流 |
以下以 PDF/A-2b 为例演示转换过程:
function App() {
const convertToPDFA = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 将字体和 PDF 文件载入 VFS
await window.spire.FetchFileToVFS('arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = '电影介绍.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfStandardsConverter
let converter = new pdfModule.PdfStandardsConverter({ filePath: inputFileName });
// 转换为 PDF/A-1b 格式
const outputFileName = 'ToPDFA_result.pdf';
converter.ToPdfA2B({ filePath: outputFileName });
// // 转换为 PDF/A-1a
// converter.ToPdfA1A({ filePath: outputFileName });
// // 转换为 PDF/A-2a
// converter.ToPdfA2A({ filePath: outputFileName });
// // 转换为 PDF/A-2b
// converter.ToPdfA2B({ filePath: outputFileName });
// // 转换为 PDF/A-3a
// converter.ToPdfA3A({ filePath: outputFileName });
// // 转换为 PDF/A-3b
// converter.ToPdfA3B({ filePath: outputFileName });
// // 转换为 PDF/X-1a:2001
// converter.ToPdfX1A2001({ filePath: outputFileName });
// 从 VFS 读取转换后的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
converter.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Convert PDF To PDF/A</h1>
<button onClick={convertToPDFA}>
Generate
</button>
</div>
);
}
export default App;
通过 PdfStandardsConverter 转换后生成的 PDF/A 输出

PDF/A 是长期归档的标准格式,但在日常编辑和内容提取场景中,可能需要将 PDF/A 还原为标准 PDF。Spire.PDF for JavaScript 通过加载 PDF/A 文档后逐页复制内容到新文档的方式实现这一反向转换,确保输出的标准 PDF 不保留 PDF/A 合规约束。
function App() {
const convertToNormalPDF = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 将字体和 PDF 文件载入 VFS
await window.spire.FetchFileToVFS('arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'PDFA_JP.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfDocument 对象
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 创建新的 PDF 文档用于在新文件上绘制内容
let newDoc = new pdfModule.PdfNewDocument();
newDoc.CompressionLevel = pdfModule.PdfCompressionLevel.None;
// 遍历原始文档中的每一页
for (let i = 0; i < doc.Pages.Count; i++) {
let page = doc.Pages.get_Item(i);
// 获取当前页面的大小
let size = page.Size;
// 向新文档添加相同大小且无边距的页面
let newPage = newDoc.Pages.Add({ size: size, margins: new pdfModule.PdfMargins() });
// 将原始页面的内容绘制到新页面上
let template = page.CreateTemplate();
let layoutWidget = new pdfModule.PdfLayoutWidget(template.H);
layoutWidget.Draw({ page: newPage, x: 0, y: 0 });
// page.CreateTemplate().Draw({page: newPage, x: 0, y: 0});
}
// 定义输出文件名
const outputFileName = "PDFAToPdf_result.pdf";
// 保存文档到指定路径
newDoc.Save(outputFileName);
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
newDoc.Dispose();
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Convert PDF/A To Normal PDF</h1>
<button onClick={convertToNormalPDF}>
Generate
</button>
</div>
);
}
export default App;
通过创建新文档逐页复制内容生成的普通 PDF 输出

原因:需要输入密码才能打开的加密 PDF 无法直接被 PdfStandardsConverter 处理,需要在加载时提供密码。
解决:PdfStandardsConverter 方法支持传入 password 参数,转换带密码的 PDF 到 PDF/A:
// 创建 PdfStandardsConverter 时指定密码
let converter = new pdfModule.PdfStandardsConverter({ filePath: inputFileName, password: "password" });
converter.ToPdfA2A({ filePath: outputFileName });
converter.Dispose();
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。
书签是 Word 文档中一种不可见的定位标记,它充当文档中的"坐标",能够精确标记某个位置或一段文本范围。但书签的真正价值不仅在于定位——通过编程手段获取书签范围内的内容、替换其中的占位文本、删除不再需要的内容,或在书签位置插入文本、段落、表格和图片,开发者可以实现合同模板的自动填充、报告数据的动态注入、表单内容的批量清理等高级文档处理流程。可以说,围绕着书签内容"读、写、删、插"的操作组合,构成了 Word 自动化的核心工作流。
Spire.Doc for JavaScript 基于 WebAssembly 在浏览器端直接完成书签内容的读取、替换、删除以及元素插入操作,通过虚拟文件系统(VFS)管理文件,无需后端服务支持。
本文介绍四个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。以下示例默认已安装 Spire.Doc 并完成 WebAssembly 模块初始化。
获取书签内容是对书签进行任何操作的前提。通过 BookmarksNavigator 定位到指定书签后,GetBookmarkContent 方法将书签范围内的内容作为 TextBodyPart 对象返回,开发者可以遍历其 BodyItems 集合下的元素进行获取。
function App() {
const bookmarkContent = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查 WASM 模块是否已加载完成
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将 Word 文档载入虚拟文件系统(VFS)
const inputFileName = '合同模板.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载 Word 文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 创建 BookmarksNavigator 并定位到书签
let navigator = new docModule.BookmarksNavigator(doc);
navigator.MoveToBookmark("合同书签");
let textBodyPart = navigator.GetBookmarkContent();
// 迭代书签内容中的元素,提取文本
let text = "";
for (let i = 0; i < textBodyPart.BodyItems.Count; i++) {
let item = textBodyPart.BodyItems.get_Item(i);
if (item instanceof docModule.Paragraph) {
for (let j = 0; j < item.ChildObjects.Count; j++) {
let childObject = item.ChildObjects.get_Item(j);
if (childObject instanceof docModule.TextRange) {
text += childObject.Text;
}
}
}
}
// 保存为 .txt 文件
const outputFileName = "获取的内容.txt";
// TXT 文件保存到 VFS 并触发下载
window.dotnetRuntime.Module.FS.writeFile(outputFileName, text);
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Get Bookmark Content from Word Document</h1>
<button onClick={bookmarkContent}>
Generate
</button>
</div>
);
}
export default App;
执行上述代码,将把书签"合同书签"范围内的文本内容提取并保存为独立的 .txt 文件:

替换书签内容是在文档模板填充中最常用的操作。通过 BookmarksNavigator 定位到书签后,调用ReplaceBookmarkContent 方法不仅支持替换为纯文本,还支持替换为表格等复杂元素,适合在合同生成、报表填充等场景中替换占位符。
function App() {
const replaceBookmarkContent = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查 WASM 模块是否已加载完成
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将字体和 Word 文件载入 VFS
await window.spire.FetchFileToVFS('SIMSUN.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = '书签文档.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载 Word 文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 创建 BookmarksNavigator 并定位到书签
let navigator = new docModule.BookmarksNavigator(doc);
navigator.MoveToBookmark("书签1");
// 替换“书签1”的内容——用文本替换
navigator.ReplaceBookmarkContent({ text: "这是替换到书签的文本", saveFormatting: true });
// 继续替换"书签2"的内容——用表格替换
navigator.MoveToBookmark("书签2");
// 创建表格
let table = new docModule.Table(doc, true);
table.ResetCells(4, 5);
// 创建数据表并将数据填充到表格中
let dt = [
["城市", "省份", "人口", "面积(km²)", "简称"],
["北京", "北京", "2189万", "16410", "京"],
["上海", "上海", "2475万", "6340", "沪"],
["广州", "广东", "1867万", "7434", "穗"]];
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 5; j++) {
table.Rows.get_Item(i).Cells.get_Item(j).AddParagraph().AppendText(dt[i][j]);
}
}
// 创建 TextBodyPart 实例并将表格添加到其中
let part = new docModule.TextBodyPart({ doc: doc });
part.BodyItems.Add(table);
// 用 TextBodyPart 对象替换当前书签内容
navigator.ReplaceBookmarkContent({ bodyPart: part });
// 保存为新的 .docx 文件
const outputFileName = "替换书签.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Replace Bookmark Content in Word</h1>
<button onClick={replaceBookmarkContent}>
Generate
</button>
</div>
);
}
export default App;
此方式可以替换书签内容为纯文本,也支持替换为表格等复杂元素。书签标记本身在替换后仍然保留,便于后续再次定位。执行效果如下图所示:

删除书签内容与删除书签标记是两个不同的操作。通过 BookmarksNavigator 定位到书签后,调用 DeleteBookmarkContent 方法可以移除书签范围内的文本内容,但保留书签标记本身,以便后续重新填入内容。如果只需清空内容而保留定位标记,此方法是首选。
function App() {
const deleteBookmarkContent = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查 WASM 模块是否已加载完成
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将 Word 文件载入 VFS
const inputFileName = '合同模板.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载 Word 文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 创建 BookmarksNavigator 并定位到书签
let navigator = new docModule.BookmarksNavigator(doc);
navigator.MoveToBookmark("合同书签");
// 删除书签内容,保留书签标记
navigator.DeleteBookmarkContent(true);
// 保存为新的 .docx 文件
const outputFileName = "删除书签内容.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Delete Bookmark Content in Word Document</h1>
<button onClick={deleteBookmarkContent}>
Generate
</button>
</div>
);
}
export default App;
使用
DeleteBookmarkContent删除的是书签范围内的文本内容,书签标记本身仍然保留;而Bookmarks.Remove移除的是书签标记,范围内的文本不受影响。
执行后文档中书签"合同书签"范围内的文本被移除,但书签标记仍保留在文档中:

Spire.Doc 支持在书签位置灵活插入多种类型的文档元素。提供 InsertText、InsertParagraph、InsertTable 等方法插入文本、段落和表格。同时也可以根据书签起始节点在段落 ChildObjects 中的索引来插入多种元素。
function App() {
const insertElementsAtBookmark = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查 WASM 模块是否已加载完成
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 加载字体和 Word 文档到 VFS
await window.spire.FetchFileToVFS('MSYH.TTC', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = '书签文档.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 创建 Document 对象并加载文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 定位到书签位置
let navigator = new docModule.BookmarksNavigator(doc);
navigator.MoveToBookmark("书签1");
// 1. 插入文本
navigator.InsertText("这是插入的文本内容。", true);
// 2. 插入段落
let newParagraph = new docModule.Paragraph(doc);
newParagraph.AppendText("这是插入的段落内容。")
navigator.MoveToBookmark("书签2");
navigator.InsertParagraph(newParagraph);
// 3. 插入表格 — 2 行 3 列
let table = new docModule.Table(doc, true);
table.ResetCells(2, 3);
table.Rows.get_Item(0).Cells.get_Item(0).AddParagraph().AppendText("名称");
table.Rows.get_Item(0).Cells.get_Item(1).AddParagraph().AppendText("数量");
table.Rows.get_Item(0).Cells.get_Item(2).AddParagraph().AppendText("备注");
table.Rows.get_Item(1).Cells.get_Item(0).AddParagraph().AppendText("产品 A");
table.Rows.get_Item(1).Cells.get_Item(1).AddParagraph().AppendText("100");
table.Rows.get_Item(1).Cells.get_Item(2).AddParagraph().AppendText("已入库");
navigator.MoveToBookmark("书签3");
navigator.InsertTable(table);
// 4. 插入图片
const imageFileName = '图片.png';
await window.spire.FetchFileToVFS(imageFileName, '', `${process.env.PUBLIC_URL}/data/`);
let picture = new docModule.DocPicture(doc);
picture.LoadImage(imageFileName);
picture.Width = 100;
picture.Height = 200;
navigator.MoveToBookmark("书签4");
// 获取书签起始节点
let start = navigator.CurrentBookmark.BookmarkStart;
// 获取书签所在的段落
let bookmarkPara = start.OwnerParagraph;
// 获取书签起始节点在段落中的索引位置
let startIndex = bookmarkPara.ChildObjects.IndexOf(start);
// 在书签起始节点后插入图片
bookmarkPara.ChildObjects.Insert(startIndex + 1, picture);
// 保存文档为 .docx 格式
const outputFileName = "插入元素到书签.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放 Document 资源
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Insert Elements at Bookmark Position</h1>
<button onClick={insertElementsAtBookmark}>
Generate
</button>
</div>
);
}
export default App;
执行上述代码,将在书签处插入文本、新段落、表格和图片,原有内容不会被覆盖:

ReplaceBookmarkContent 默认会用纯文本替换,原格式会丢失。如果需要保留书签原有的格式,传入 saveFormatting: true:
navigator.ReplaceBookmarkContent({ text: "新内容", saveFormatting: true });
这样替换后的文本会沿用书签原来的字体、字号、颜色等格式。
遍历 doc.Bookmarks 集合,逐个定位并操作:
for (let i = 0; i < doc.Bookmarks.Count; i++) {
let bookmark = doc.Bookmarks.get_Item(i);
navigator.MoveToBookmark(bookmark.Name);
// 执行替换、删除或插入操作
}
DeleteBookmarkContent 和直接删除书签标记有什么区别?DeleteBookmarkContent:只清空书签范围内的内容,书签标记保留在文档中,后续仍可通过书签名称定位并填入新内容。Bookmarks.Remove:移除书签标记本身,书签范围内的内容不受影响,但书签名称会消失,无法再定位。两者互补,根据需求选择:需要保留"占位"能力就用 DeleteBookmarkContent,不需要书签了就移除标记。
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。
书签相当于 Word 文档中看不见的"锚点",能准确定位到指定位置或选中文本。无论是合同模板中的待填写区域、长文档中需要跳转的关键章节,还是批量生成报表时用于定位数据插入点,书签都是背后关键的锚点。开发者可以基于书签实现文档内容的动态填充、定位导航、内容提取等高级功能,因此书签操作是 Word 自动化中最常用的基础能力之一。
Spire.Doc for JavaScript 基于 WebAssembly 在浏览器端直接完成书签的创建、定位和删除操作,通过虚拟文件系统(VFS)管理文件,无需后端服务支持。
本文介绍三个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。以下示例默认已安装 Spire.Doc 并完成 WebAssembly 模块初始化。
在已有文档中追加书签,可以通过 AppendBookmarkStart 和 AppendBookmarkEnd 在指定段落上标记书签区域。不仅支持为文档中已有的段落添加书签标记,也支持在新段落中追加书签。Spire.Doc 同时支持嵌套书签,方便构建层级结构。
function App() {
const createBookmarkInWord = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查模块是否就绪
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将 Word 文件载入 VFS
const inputFileName = '中国旅行手册.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 获取第一节,添加书签
let section = doc.Sections.get_Item(0);
AddBookmark(section);
// 保存为 .docx 文件
const outputFileName = "添加书签.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从 VFS 读取文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
doc.Dispose();
};
function AddBookmark(section) {
// 书签 1:给文档已有的段落添加书签
let paraStart = section.Paragraphs.get_Item(1);
let paraEnd = section.Paragraphs.get_Item(3);
paraStart.AppendBookmarkStart("书签1");
paraEnd.AppendBookmarkEnd("书签1");
// 书签 2:新增段落添加书签
let paragraph = section.AddParagraph();
paragraph.AppendBookmarkStart("书签2");
paragraph.AppendText("这是新增段落。");
paragraph.AppendBookmarkEnd("书签2");
}
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Bookmark in Word</h1>
<button onClick={createBookmarkInWord}>
Generate
</button>
</div>
);
}
export default App;
执行上述代码,添加的书签如下图所示:

为已有段落中的特定文本添加书签,需要先通过 FindAllString 查找文本位置,使用 BookmarkStart 和 BookmarkEnd 构造函数创建书签对象,再通过 ChildObjects.Insert 将起始标记插入到匹配文本的 TextRange 之前,结束标记插入到其后。
function App() {
const addBookmarkForMatchedText = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查 WASM 模块是否已加载完成
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将Word 文档载入虚拟文件系统(VFS)
const inputFileName = '中国旅行手册.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载 Word 文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 查找文档中所有匹配"美食"的文本
let textSelections = doc.FindAllString('美食', false, true);
// 遍历每一处匹配结果,在其前后插入书签起始和结束标记
for (let i = 0; i < textSelections.length; i++) {
// 创建书签起始和结束对象(书签名称为 "书签_0"、"书签_1"...)
let start = new docModule.BookmarkStart(doc, "书签_" + i);
let end = new docModule.BookmarkEnd(doc, "书签_" + i);
let selection = textSelections[i];
// 获取匹配文本的 TextRange
let textRange = selection.GetAsOneRange();
// 获取匹配文本所在的段落
let para = textRange.OwnerParagraph;
// 获取该 TextRange 在段落子对象中的索引位置
let index = para.ChildObjects.IndexOf(textRange);
// 在 TextRange 之前插入书签起始,之后插入书签结束
para.ChildObjects.Insert(index, start);
para.ChildObjects.Insert(index + 2, end);
}
// 保存为新的 .docx 文件
const outputFileName = "添加书签.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从VFS中读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放资源
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Bookmarks for Specific Text in Word Documents</h1>
<button onClick={addBookmarkForMatchedText}>
Generate
</button>
</div>
);
}
export default App;
此方式适用于需要在现有文档基础上添加定位标记的场景,例如在已写好的合同文档中标明待填充区域。执行上述代码,添加的书签如下图所示:

删除书签仅移除书签标记本身,书签范围内的文本内容会被保留。通过 document.Bookmarks 集合获取书签对象后,调用 Remove 方法即可完成删除。
function App() {
const deleteBookmark = async () => {
// 获取 Spire.Doc WASM 模块
const docModule = window.wasmModule?.spiredoc;
// 检查 WASM 模块是否已加载完成
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将Word 文档载入虚拟文件系统(VFS)
const inputFileName = '添加书签.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 加载 Word 文档
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 按名称获取书签
let bookmark = doc.Bookmarks.get_Item("书签_1");
// // 按索引获取书签
// let bookmark = doc.Bookmarks.get_Item(0);
// 删除书签(不删除其内容)
doc.Bookmarks.Remove(bookmark);
// 保存为新的 .docx 文件
const outputFileName = "删除书签.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// 从 VFS 读取生成的文件并触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
// 释放文档资源
doc.Dispose();
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Delete Bookmark by Name in Word Document</h1>
<button onClick={deleteBookmark}>
Generate
</button>
</div>
);
}
export default App;
删除书签后,文档中原书签标记消失,但标记范围内的文本仍保留在文档中。

原因:Word 文档中书签名称必须唯一,重复添加同名书签会导致错误。
解决:在添加书签前检查名称是否已存在:
if (document.Bookmarks.FindByName("MyBookmark") === null) {
paragraph.AppendBookmarkStart("MyBookmark");
paragraph.AppendText("内容");
paragraph.AppendBookmarkEnd("MyBookmark");
}
原因:Spire.Doc 的 Bookmarks.Remove 仅移除书签标记(开始和结束标记),中间的文本内容不受影响。
解决:根据实际需求选择操作:
// 仅删除书签标记,保留文本
document.Bookmarks.Remove(bookmark);
// 删除书签及其内容(通过 BookmarksNavigator 定位后删除)
let navigator = new docModule.BookmarksNavigator(doc);
navigator.MoveToBookmark("MyBookmark");
navigator.navigator.DeleteBookmarkContent(true); // 删除书签范围内的内容
AppendBookmarkStart 和 AppendBookmarkEnd 必须在同一段落吗原因:起始标记和结束标记可以在不同段落,代码中"书签1"的示例展示了跨段落的用法。关键限制在于书签范围内的文档对象结构必须完整,不能跨表格单元格标记,因为单元格是独立容器,跨单元格可能导致书签识别异常。
解决:如果书签范围跨越了表格单元格,应调整书签的起始或结束位置,确保在同一单元格内闭合。
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。

在许多 Java 应用程序中,处理 PDF 文件是一项常见需求——无论是生成发票、修改合同,还是为报告添加批注,都经常需要对 PDF 文档进行操作。虽然 PDF 格式因其稳定性和跨平台特性,成为文档共享的常用格式,但如果没有合适的开发库,直接通过代码编辑 PDF 文件可能会比较复杂。
本教程将介绍如何使用 Spire.PDF for Java(一个功能全面且易于使用的 PDF 开发 API)对 PDF 文件中的内容进行添加、替换、删除和安全保护操作。我们将通过示例详细讲解如何添加页面、文本、图像、表格、注释,如何替换内容、删除元素,以及如何使用水印和密码保护文件。
Spire.PDF 提供了一套完整的 PDF 处理功能,非常适合需要在 Java 应用程序中操作 PDF 文件的开发人员。以下是选择 Spire.PDF 的几个主要原因:
借助 Spire.PDF,开发人员可以轻松处理 PDF 文件,而无需深入了解 PDF 格式本身的复杂结构。
安装 Spire.PDF for Java
开始使用 Spire.PDF 前,首先需要将其添加到 Java 项目中。您可以从其官方网站下载该库,或通过 Maven 引入:
Maven 用户配置方式:
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.pdf</artifactId>
<version>12.6.4</version>
</dependency>
</dependencies>
手动配置方式:
从官方网站下载 Spire.PDF for Java,并将 JAR 文件添加到项目的 classpath 中。
初始化并加载 PDF 文档
完成类库配置后,就可以开始加载 PDF 文件。操作如下:
PdfDocument doc = new PdfDocument();
doc.loadFromFile("sample.pdf");
此代码创建了一个新的 PdfDocument 对象,并通过指定路径加载 PDF 文件。通过调用 loadFromFile,PDF 文档将被加载到内存中,之后即可进行进一步编辑操作。
使用 Spire.PDF for Java,可以轻松向现有 PDF 文档中添加各种内容,例如新页面、文本、图片、表格以及批注等。
添加新页面
向现有 PDF 文档添加新页面非常简单。示例代码如下:
// 添加新页面
PdfPageBase new_page = doc.getPages().add(PdfPageSize.A4, new PdfMargins(54));
// 在页面上绘制文本或进行其他操作
new_page.getCanvas().drawString("这是一个新添加的页面。", new PdfTrueTypeFont(new Font("Times New Roman",Font.PLAIN,18)), PdfBrushes.getBlue(), 0, 0);
在上述代码中,我们使用 add 方法创建了一个 A4 大小并带有指定边距的新页面。然后,我们使用指定的字体和颜色在新页面上绘制字符串。drawString 方法将文本放置在页面的左上角,让您能够快速添加内容。
向 PDF 文件添加文本
要将文本插入到现有页面的特定区域,请使用以下代码:
// 获取特定页面
PdfPageBase page = doc.getPages().get(0);
// 定义一个用于放置文本的矩形区域
Rectangle2D.Float rect = new Rectangle2D.Float(54, 300, (float) page.getActualSize().getWidth() - 108, 100);
// 创建画刷和字体
PdfSolidBrush brush = new PdfSolidB
rush(new PdfRGBColor(Color.BLUE));
PdfTrueTypeFont font = new PdfTrueTypeFont(new Font("Microsoft YaHei", Font.PLAIN,18));
// 在页面的指定区域绘制文本
page.getCanvas().drawString("此行由 Spire.PDF for Java 创建。",font, brush, rect);
该代码获取文档的第一页,并定义了一个用于放置文本的矩形。Rectangle2D.Float 类允许您指定文本定位的精确尺寸。然后,我们使用 drawString 方法,用蓝色画刷和自定义字体绘制指定文本,确保文本在定义区域内渲染。

向 PDF 文件添加图像
插入图像到 PDF 也同样简单:
// 获取特定页面
PdfPageBase page = doc.getPages().get(0);
// 加载图像
PdfImage image = PdfImage.fromFile("logo.png");
// 指定添加图像的坐标
float x = 54;
float y = 300;
// 在页面的指定坐标处绘制图像
page.getCanvas().drawImage(image, x, y);
这里,我们从指定文件路径加载图像,并在第一页的定义坐标 (x, y) 处绘制它。drawImage 方法允许您精确地定位图像,方便将视觉元素融入文档中。

向 PDF 文件添加表格
Spire.PDF 也支持向 PDF 文档中插入表格:
// 获取特定页面
PdfPageBase page = doc.getPages().get(0);
// 创建表格
PdfTable table = new PdfTable();
// 定义表格数据
String[][] data = {
new String[]{"姓名", "年龄", "国家"},
new String[]{"Alice", "25", "美国"},
new String[]{"Bob", "30", "英国"},
new String[]{"Charlie", "28", "加拿大"}
};
// 将数据分配给表格
table.setDataSource(data);
// 设置表格样式
PdfTableStyle style = new PdfTableStyle();
style.getDefaultStyle().setFont(new PdfTrueTypeFont(new Font("Arial", Font.PLAIN, 12)));
table.setStyle(style);
// 在页面上绘制表格
table.draw(page, new Point2D.Float(50, 80));
在此示例中,我们创建了一个表格,并使用二维数组定义其数据源。分配数据后,我们使用 PdfTableStyle 为表格设置样式,这允许您自定义字体和表格外观。最后,我们使用 draw 方法在指定坐标处渲染第一页的表格。
添加注释或批注
PDF 批注可以增强文档的交互性,例如添加备注、评论或提示信息。实例代码如下:
// 获取特定页面
PdfPageBase page = doc.getPages().get(0);
// 创建自由文本注释
PdfPopupAnnotation popupAnnotation = new PdfPopupAnnotation();
popupAnnotation.setLocation(new Point2D.Double(90, 260));
// 设置注释的内容
popupAnnotation.setText("这是一个由 Spire.PDF for Java 添加的弹出式注释。");
// 设置注释的图标和颜色
popupAnnotation.setIcon(PdfPopupIcon.Comment);
popupAnnotation.setColor(new PdfRGBColor(Color.RED));
// 将注释添加到注释集合中
page.getAnnotations().add(popupAnnotation);
此代码段在页面上的指定位置创建一个弹出式注释。通过调用 setLocation,定义注释出现的位置。setText 方法允许您指定注释中显示的内容,同时您可以自定义批注的图标和颜色。最后,该注释被添加到页面的注释集合中。

您可能还喜欢:在 Java 中向 PDF 文档添加页码
除了向 PDF 文件添加内容之外,开发人员还经常需要修改已有内容,例如替换文本或更新图片。
使用 Spire.PDF for Java,可以方便地对 PDF 文档中的文本和图片进行替换操作。
替换 PDF 文件中的文本
要替换 PDF 中的现有文本,可以使用以下代码:
// 创建 PdfTextReplaceOptions 对象
PdfTextReplaceOptions textReplaceOptions = new PdfTextReplaceOptions();
// 指定文本替换选项
textReplaceOptions.setReplaceType(EnumSet.of(ReplaceActionType.IgnoreCase));
// 遍历页面
for (int i = 0; i < doc.getPages().getCount(); i++) {
// 获取特定页面
PdfPageBase page = doc.getPages().get(i);
// 基于页面创建 PdfTextReplacer 对象
PdfTextReplacer textReplacer = new PdfTextReplacer(page);
// 设置替换选项
textReplacer.setOptions(textReplaceOptions);
// 将所有匹配的目标文本替换为新文本
textReplacer.replaceAllText("PDF", "OFD");
}
在此示例中,我们创建了一个 PdfTextReplaceOptions 对象用于设置文本替换规则,例如忽略大小写。然后,通过循环遍历 PDF 文档中的所有页面,并为每一页创建一个 PdfTextReplacer 对象。最后,调用 replaceAllText 方法,以将所有出现的 "PDF" 替换为 "OFD"。

替换 PDF 文件中的图像
替换图像遵循类似的模式:
// 获取特定页面
PdfPageBase page = doc.getPages().get(0);
// 加载图像
PdfImage image = PdfImage.fromFile("logo.png");
// 从页面获取图像信息
PdfImageHelper imageHelper = new PdfImageHelper();
PdfImageInfo[] imageInfo = imageHelper.getImagesInfo(page);
// 替换图像
imageHelper.replaceImage(imageInfo[0], image);
此代码使用 PdfImageHelper 类从指定页面检索图像信息。从文件加载新图像后,调用 replaceImage 将页面上第一个图像替换为新图像。

您可能还喜欢:Java 在 PDF 中应用不同的字体
除了添加和替换内容之外,删除 PDF 中不需要的元素也是常见需求。
从 PDF 文件中移除页面
要从 PDF 中移除整个页面,请使用以下代码:
// 移除特定页面
doc.getPages().removeAt(0);
这个简单的命令移除了文档的第一页。通过调用 removeAt,可以指定要移除页面的索引,从而简化 PDF 中的页面管理。
从 PDF 文件中删除图像
要从页面中移除图像:
// 获取特定页面
PdfPageBase page = pdf.getPages().get(0);
// 从页面获取图像信息
PdfImageHelper imageHelper = new PdfImageHelper();
PdfImageInfo[] imageInfos = imageHelper.getImagesInfo(page);
// 删除页面上指定的图像
imageHelper.deleteImage(imageInfos[0]);
此代码检索首页的所有图像,并使用 PdfImageHelper 的 deleteImage 方法删除第一个图像。
删除注释
移除注释也同样简单:
// 获取特定页面
PdfPageBase page = pdf.getPages().get(0);
// 移除指定的注释
page.getAnnotationsWidget().removeAt(0);
此代码段会删除指定页面中的第一个注释。removeAt 方法用于指定需要删除的批注索引,可以帮助开发人员快速清理 PDF 中不需要的评论或标记。
删除附件
如果 PDF 文件中包含附件,也可以通过以下方式删除:
// 获取附件集合
PdfAttachmentCollection attachments = doc.getAttachments();
// 移除特定附件
attachments.removeAt(0);
此代码检索文档的附件集合并使用 removeAt 方法移除第一个附件。
Spire.PDF for Java 提供了完善的安全功能,可以帮助开发人员轻松实现 PDF 文件保护。
在 PDF 文件上应用水印
水印常用于品牌展示、版权保护或防止文档被未经授权复制。以下代码展示了如何为 PDF 文件添加文本水印:
// 创建字体和画刷
PdfTrueTypeFont font = new PdfTrueTypeFont(new Font("Arial Black", Font.PLAIN, 50), true);
PdfBrush brush = PdfBrushes.getBlue();
// 指定水印文本
String watermarkText = "严禁复制";
// 指定不透明度级别
float opacity = 0.6f;
// 遍历页面
for (int i = 0; i < doc.getPages().getCount(); i++) {
PdfPageBase page = doc.getPages().get(i);
// 设置水印的透明度级别
page.getCanvas().setTransparency(opacity);
// 测量水印文本的尺寸
Dimension2D textSize = font.measureString(watermarkText);
// 获取页面的宽度和高度
double pageWidth = page.getActualSize().getWidth();
double pageHeight = page.getActualSize().getHeight();
// 计算将水印置于页面中心的位置
double x = (pageWidth - textSize.getWidth()) / 2;
double y = (pageHeight - textSize.getHeight()) / 2;
// 在页面上计算出的位置绘制水印文本
page.getCanvas().drawString(watermarkText, font, brush, x, y);
}
此代码配置文本水印的外观,并使用 drawString 方法将其放置在 PDF 文件每一页的中心,有效地阻止未经授权的复制。
用密码保护 PDF 文件
如果需要限制用户访问 PDF 文件,可以为文档设置密码保护。
// 指定用户密码和所有者密码
String userPassword = "open_psd";
String ownerPassword = "permission_psd";
// 使用两个密码创建 PdfSecurityPolicy 对象
PdfSecurityPolicy securityPolicy = new PdfPasswordSecurityPolicy(userPassword, ownerPassword);
// 设置加密算法
securityPolicy.setEncryptionAlgorithm(PdfEncryptionAlgorithm.AES_256);
// 设置文档权限(如果不设置,则默认为全部禁止)
securityPolicy.setDocumentPrivilege(PdfDocumentPrivilege.getAllowAll());
// 限制编辑
securityPolicy.getDocumentPrivilege().setAllowModifyContents(false);
securityPolicy.getDocumentPrivilege().setAllowCopyContentAccessibility(false);
securityPolicy.getDocumentPrivilege().setAllowContentCopying(false);
// 加密 PDF 文件
doc.encrypt(securityPolicy);
该代码通过定义用户密码(用于打开)和所有者密码(用于编辑、打印等权限),为 PDF 文档添加密码保护和加密功能。PdfSecurityPolicy 对象管理安全设置,包括 AES-256 加密算法和权限级别。最后,doc.encrypt(securityPolicy) 加密文档,确保只有授权用户才能访问或修改它。

您可能还喜欢:如何在 Java 中为 PDF 添加数字签名
在 Java 中编辑 PDF 文件常被视为一项复杂的任务,但借助 Spire.PDF for Java,这一过程可以变得简单、高效。该库为开发者提供了灵活的 PDF 操作能力,可以通过清晰易懂的 API 来创建、修改、替换和保护 PDF 内容。从添加页面和图像到加密敏感文档,Spire.PDF 简化了工作流程中的每一步,同时保持专业的输出质量。
除了基础的 PDF 编辑功能之外,Spire.PDF 还支持自动化处理和企业级应用场景。无论您是将 PDF 处理集成到文档管理系统中,还是生成定制化报告,该库都能为长期项目提供稳定、可扩展的技术基础。凭借其全面的功能集和强大的性能,Spire.PDF for Java 是寻求对 PDF 文档进行精确、高效和可控操作的开发者的可靠选择。
答:Spire.PDF for Java 是全球开发者中的热门选择,它为有效的 PDF 处理提供了全面的功能。
答:可以。通过 Spire.PDF for Java,您可以使用 PdfTextReplacer 等类来替换或修改现有文本,并可自定义大小写敏感度和匹配行为选项。
答:在 Spire.PDF for Java 中,可以使用 drawImage() 插入图像,并使用 PdfImageHelper.replaceImage() 替换特定页面上的现有图像。
答:可以。使用 Spire.PDF for Java 提供的相应注释类,可以添加诸如高亮、评论和图章等注释。
答:可以。Spire.PDF for Java 提供了轻松提取 PDF 中文本、图像和其他元素的方法。有关详细说明和代码示例,请参阅:如何在 Java 中读取 PDF:提取文本、图像及更多内容
要充分体验 Spire.PDF for Java 的功能而不受任何评估限制,您可以申请 30 天免费试用许可证。
新功能:
# 初始化 PDF 文档对象
doc = PdfDocument()
# 加载需要签名的 PDF 文件
doc.LoadFromFile(inputFile)
# 指定 PFX 证书文件的密码
pfxPassword = "e-iceblue"
# 获取文档的第一页
page = doc.Pages.get_Item(0)
# 使用证书文件创建普通签名制作器
signatureMaker = PdfOrdinarySignatureMaker(doc, inputFile_pfx, pfxPassword)
# 设置签名验证层(False 表示显示有效性符号)
signatureMaker.SetAcro6Layers(False)
# 获取签名对象并配置签署人的基本信息
signature = signatureMaker.Signature
signature.Name = "Gary" # 签署人姓名
signature.ContactInfo = "028-81705109" # 联系方式
signature.Location = "Chengdu" # 签署地点
signature.Reason = "The certificate of this document" # 签署原因
# 创建自定义签名外观对象
appearance = PdfSignatureAppearance(signature)
appearance.NameLabel = "Signer: " # 姓名标签
appearance.ContactInfoLabel = "ContactInfo: " # 联系方式标签
appearance.LocationLabel = "Location: " # 地点标签
appearance.ReasonLabel = "Reaseon: " # 原因标签(保留原文拼写)
appearance.SignatureImage = PdfImage.FromFile(inputFile_image) # 加载签名图片
appearance.GraphicMode = GraphicMode.SignImageAndSignDetail # 同时显示签名图片和详细信息
signature.DocumentPermissions = PdfCertificationFlags.ForbidChanges # 禁止更改文档
appearance.SignImageLayout = SignImageLayout.Stretch # 签名图片拉伸布局
appearance.AutoFontSize = True # 启用自动调整字体大小
# 在页面指定位置生成并应用签名(参数顺序:签名字段名、页对象、X坐标、Y坐标、宽度、高度、外观对象)
signatureMaker.MakeSignature("signName", page, 54.0, page.Size.Height - 300.0, 40.0, 40.0, appearance)
# 保存已签名的 PDF 文档
doc.SaveToFile(outputFile)
# 关闭文档并释放资源
doc.Close()
问题修复:
Spire.Presentation for C++ 11.7.0 现已正式发布。该版本修复了拆分 PowerPoint 文档后,结果文档变大的问题、转换及加载过程中存在的问题,进一步提升了文档处理的稳定性。详情如下。
问题修复:
https://www.e-iceblue.cn/Downloads/Spire-Presentation-CPP.html