在项目报告、产品手册、技术规范或合同汇编这类页数较多的 PDF 中,如果没有书签,读者通常只能通过滚动页面或输入页码查找内容。
给 PDF 添加书签后,可以在阅读器侧边栏直接看到文档结构,并快速跳转到指定章节。对于已经存在书签的 PDF,还可能需要修改过时的章节名称,或者删除失效、重复的书签。
本文介绍如何使用 Java 操作 PDF 书签,主要包括:
- 为 PDF 添加书签
- 创建多级书签
- 修改已有书签
- 删除指定或全部书签
安装所需的 PDF 库
本文使用 Spire.PDF for Java 来读取和修改 PDF 文件。它提供了 PDF 书签相关 API,可以添加一级和多级书签,也可以修改或删除现有书签。
如果使用 Maven,可以在 pom.xml 中添加官方仓库和依赖。以下以当前官方提供的 12.8.1 版本为例,实际项目可以根据使用时的最新版本进行调整。
xml
<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.8.1</version>
</dependency>
</dependencies>
安装完成后,就可以通过 PdfDocument 加载现有 PDF,并使用书签集合进行后续操作。
为 PDF 添加书签
假设有一份已经生成好的 项目报告.pdf,内容包含:
- 项目概述
- 实施方案
- 数据分析
- 总结建议
可以根据这些章节所在的页面创建书签。
下面的示例分别为前四页添加对应的一级书签:
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.actions.PdfGoToAction;
import com.spire.pdf.bookmarks.PdfBookmark;
import com.spire.pdf.bookmarks.PdfTextStyle;
import com.spire.pdf.general.PdfDestination;
import com.spire.pdf.graphics.PdfRGBColor;
import java.awt.Color;
import java.awt.geom.Point2D;
public class AddPdfBookmarks {
public static void main(String[] args) {
// 创建 PdfDocument 对象
PdfDocument pdf = new PdfDocument();
// 加载 PDF 文件
pdf.loadFromFile("项目报告.pdf");
// 定义书签标题
String[] bookmarkTitles = {
"项目概述",
"实施方案",
"数据分析",
"总结建议"
};
// 为前四页添加书签
for (int i = 0; i < bookmarkTitles.length; i++) {
PdfPageBase page = pdf.getPages().get(i);
// 添加书签
PdfBookmark bookmark =
pdf.getBookmarks().add(bookmarkTitles[i]);
// 设置书签跳转目标
PdfDestination destination =
new PdfDestination(
page,
new Point2D.Float(0, 0)
);
bookmark.setAction(
new PdfGoToAction(destination)
);
// 设置书签颜色
bookmark.setColor(
new PdfRGBColor(
new Color(47, 84, 150)
)
);
// 设置为粗体
bookmark.setDisplayStyle(
PdfTextStyle.Bold
);
}
// 保存结果
pdf.saveToFile("添加书签后的项目报告.pdf");
pdf.close();
}
}
这里主要涉及三个步骤。
首先通过:
java
pdf.getBookmarks().add("项目概述");
在文档的书签集合中创建一个书签。
然后创建 PdfDestination:
java
PdfDestination destination =
new PdfDestination(
page,
new Point2D.Float(0, 0)
);
它决定点击书签后跳转到哪一页以及页面中的哪个位置。
最后通过:
java
bookmark.setAction(
new PdfGoToAction(destination)
);
把跳转动作绑定到书签。
需要注意,pdf.getPages().get(i) 中的页面索引从 0 开始,因此:
text
get(0) → 第 1 页
get(1) → 第 2 页
get(2) → 第 3 页
如果实际章节不是从第一页开始,只需要把对应的页面索引调整为真实位置即可。
为 PDF 创建多级书签
对于结构较复杂的报告,只使用一级书签可能还不够。
例如"实施方案"下面还包括:
text
实施方案
├── 实施计划
├── 人员安排
└── 风险控制
这种情况下可以在一级书签下面继续添加子书签。
Spire.PDF for Java 可以通过 PdfBookmark.add() 在父书签下面创建子书签。
例如:
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.actions.PdfGoToAction;
import com.spire.pdf.bookmarks.PdfBookmark;
import com.spire.pdf.general.PdfDestination;
import java.awt.geom.Point2D;
public class AddChildBookmarks {
public static void main(String[] args) {
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile("项目报告.pdf");
// 创建一级书签
PdfBookmark parentBookmark =
pdf.getBookmarks().add("实施方案");
// 一级书签跳转到第 2 页
PdfPageBase parentPage =
pdf.getPages().get(1);
PdfDestination parentDestination =
new PdfDestination(
parentPage,
new Point2D.Float(0, 0)
);
parentBookmark.setAction(
new PdfGoToAction(parentDestination)
);
// 创建子书签:实施计划
PdfBookmark planBookmark =
parentBookmark.add("实施计划");
PdfDestination planDestination =
new PdfDestination(
pdf.getPages().get(1),
new Point2D.Float(0, 120)
);
planBookmark.setAction(
new PdfGoToAction(planDestination)
);
// 创建子书签:人员安排
PdfBookmark staffBookmark =
parentBookmark.add("人员安排");
PdfDestination staffDestination =
new PdfDestination(
pdf.getPages().get(2),
new Point2D.Float(0, 0)
);
staffBookmark.setAction(
new PdfGoToAction(staffDestination)
);
// 创建子书签:风险控制
PdfBookmark riskBookmark =
parentBookmark.add("风险控制");
PdfDestination riskDestination =
new PdfDestination(
pdf.getPages().get(3),
new Point2D.Float(0, 0)
);
riskBookmark.setAction(
new PdfGoToAction(riskDestination)
);
pdf.saveToFile("多级书签项目报告.pdf");
pdf.close();
}
}
创建多级书签时,子书签并不是添加到:
java
pdf.getBookmarks()
而是添加到对应的父书签:
java
parentBookmark.add("实施计划");
这样才能形成真正的层级结构。
如果 PDF 本身已经按照"章节 → 小节"的方式组织,使用多级书签通常比创建大量平级书签更容易浏览。
修改 PDF 中已有的书签
PDF 使用一段时间后,章节名称可能发生变化。
例如原来的:
text
项目方案
后来改成:
text
项目实施方案
如果 PDF 页面内容已经更新,但书签仍然是旧名称,就可以直接修改已有书签,而不需要重新创建整个 PDF。
通过 getBookmarks().get() 可以取得指定书签,然后使用 setTitle()、setColor() 和 setDisplayStyle() 修改其属性。
示例:
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.bookmarks.PdfBookmark;
import com.spire.pdf.bookmarks.PdfTextStyle;
import com.spire.pdf.graphics.PdfRGBColor;
import java.awt.Color;
public class EditPdfBookmark {
public static void main(String[] args) {
PdfDocument pdf = new PdfDocument();
// 加载包含书签的 PDF
pdf.loadFromFile("项目报告.pdf");
// 获取第一个书签
PdfBookmark bookmark =
pdf.getBookmarks().get(0);
// 修改书签标题
bookmark.setTitle("项目实施方案");
// 修改书签颜色
bookmark.setColor(
new PdfRGBColor(
new Color(31, 78, 121)
)
);
// 设置为粗体
bookmark.setDisplayStyle(
PdfTextStyle.Bold
);
// 保存结果
pdf.saveToFile("修改书签后的项目报告.pdf");
pdf.close();
}
}
如果只是调整章节名称,一般只需要:
java
bookmark.setTitle("新的章节名称");
颜色和字体样式属于可选设置,可以根据文档的实际需求决定是否修改。
删除 PDF 中指定的书签
如果某个章节已经从 PDF 中删除,对应书签也应该同步清理,否则用户点击后仍可能跳转到已经没有实际意义的位置。
删除一级书签可以使用:
java
pdf.getBookmarks().removeAt(0);
例如删除第一个书签:
java
import com.spire.pdf.PdfDocument;
public class DeletePdfBookmark {
public static void main(String[] args) {
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile("项目报告.pdf");
// 删除第一个一级书签
pdf.getBookmarks().removeAt(0);
pdf.saveToFile("删除书签后的项目报告.pdf");
pdf.close();
}
}
如果一级书签包含子书签,删除这个一级书签时,其子书签也会一起删除。
删除指定的子书签
如果只需要删除某个章节下的小节,而希望保留父书签,可以先取得父书签,再调用它的 removeAt():
java
PdfBookmark parentBookmark =
pdf.getBookmarks().get(0);
// 删除父书签下的第一个子书签
parentBookmark.removeAt(0);
这里删除的是:
text
一级书签
└── 第一个子书签 ← 删除
而不是整个一级书签。
这种方式适合文档结构发生局部调整的情况。
删除 PDF 中的全部书签
如果需要彻底重建 PDF 的导航结构,可以先删除全部旧书签:
java
pdf.getBookmarks().clear();
完整示例:
java
import com.spire.pdf.PdfDocument;
public class DeleteAllPdfBookmarks {
public static void main(String[] args) {
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile("项目报告.pdf");
// 删除全部书签
pdf.getBookmarks().clear();
pdf.saveToFile("无书签项目报告.pdf");
pdf.close();
}
}
clear() 会清空文档的整个书签集合。
这种方式比较适合:
- 原有书签结构已经完全失效
- 需要根据新的目录重新生成书签
- 批量处理来源不同、书签质量不一致的 PDF
实际开发中的几个注意点
1. PDF 页面索引从 0 开始
给书签设置目标页面时,很容易出现页码偏移。
例如业务上说"跳转到第 5 页",代码实际应该使用:
java
pdf.getPages().get(4);
如果书签来自数据库或配置文件,而其中存储的是正常的 1-based 页码,可以在代码中统一减 1:
java
int pageIndex = pageNumber - 1;
在批量生成书签时尤其要注意这一点。
2. 添加书签前检查目标页面是否存在
如果书签配置来自外部数据,不要直接假设所有页码都有效。
例如:
java
int pageIndex = 10;
if (pageIndex >= 0 &&
pageIndex < pdf.getPages().getCount()) {
PdfPageBase page =
pdf.getPages().get(pageIndex);
// 创建书签
}
这样可以避免配置中的错误页码导致程序处理失败。
3. 书签标题最好与正文结构保持一致
如果正文标题是:
text
3. 项目实施方案
书签却写成:
text
方案
虽然技术上没有问题,但长文档中会增加理解成本。
对于自动生成的报告,可以直接复用系统中的章节名称来生成书签,而不是另外维护一套名称。
例如:
java
String[] chapterNames = {
"1. 项目概述",
"2. 实施方案",
"3. 数据分析",
"4. 总结建议"
};
这样 PDF 正文、目录和书签结构更容易保持一致。
4. 多级书签不要设置得过深
PDF 书签支持层级结构,但实际使用中层级过多反而不方便浏览。
一般的报告或说明书使用:
text
章节
└── 小节
或者:
text
章节
└── 小节
└── 子项
通常已经足够。
如果业务数据本身存在六七层结构,可以考虑只把主要层级放入 PDF 书签,而不是完整复制所有数据层级。
总结
对于页数较多的 PDF,书签能够提供比单纯输入页码更直观的导航方式。
本文介绍了如何使用 Java:
- 为 PDF 添加一级书签
- 创建父子结构的多级书签
- 修改已有书签的标题和样式
- 删除指定一级书签或子书签
- 清空 PDF 中的全部书签
在实际项目中,如果 PDF 由系统自动生成,可以在生成文档后根据章节名称和页码同步建立书签;如果是处理已有 PDF,则可以根据新的文档结构修改或重建书签。
这样可以让项目报告、技术文档、说明书等长 PDF 保持更清晰的导航结构,也方便后续维护。