Java 如何为 PDF 添加、修改和删除书签

在项目报告、产品手册、技术规范或合同汇编这类页数较多的 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 保持更清晰的导航结构,也方便后续维护。

相关推荐
铃木之影1 小时前
Java 版本 RAG 示例(Spring AI + Milvus)
java·人工智能·spring
C++、Java和Python的菜鸟2 小时前
第14章 项目部署(Linux)
java
智海深蓝2 小时前
智慧渔业海上养殖数字孪生实践方向与难点拆解分析
java·前端·网络
2601_955760072 小时前
Claude API 多人协作中的版本管理方法
java·ai编程
梦想的旅途22 小时前
企业微信API二次开发:外部群模块功能清单与全场景对接
java·python·企业微信
校招VIP3 小时前
[校大]27届东华理工大学JAVA简历:中厂简历通过率30%
java·秋招·校招·实习·产品岗·27届
老白干3 小时前
基于 Spring AOP 的操作日志记录:以 DeptController 增删接口为例
java·python·spring
晚安code4 小时前
Java编程规范避坑指南:阿里开发手册15条强制规约实战解析
java·后端
小蒜学长4 小时前
“守望自然”招募志愿者环保行动网站的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端