Java 给 PDF 添加页码:实现“第 X 页,共 Y 页”

页码看起来只是 PDF 中一个很小的元素,但对于报告、合同、说明书等多页文档来说非常实用。尤其是在打印、审阅或引用文档内容时,清晰的页码可以帮助读者快速定位页面。

如果 PDF 是由 Java 程序批量生成或处理的,逐个手动添加页码显然并不现实。这种情况下,可以直接在程序中遍历 PDF 页面,并将当前页码和总页数自动添加到每一页。

本文将介绍如何使用 Spire.PDF for Java 为已有 PDF 添加类似 "第 1 页,共 10 页" 的动态页码。

安装 Spire.PDF for Java

如果使用 Maven,可以在项目的 pom.xml 文件中添加以下仓库和依赖:

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>

Java 给 PDF 添加"第 X 页,共 Y 页"页码

Spire.PDF for Java 提供了几个用于处理动态页码的字段:

  • PdfPageNumberField:获取当前页的页码
  • PdfPageCountField:获取 PDF 的总页数
  • PdfCompositeField:将多个动态字段组合成一段文本

因此,我们不需要自己为每一页生成不同的页码文本,而是可以将当前页码和总页数组合成 "第 {0} 页,共 {1} 页" 的格式。

下面是完整代码:

java 复制代码
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.automaticfields.PdfCompositeField;
import com.spire.pdf.automaticfields.PdfPageCountField;
import com.spire.pdf.automaticfields.PdfPageNumberField;
import com.spire.pdf.graphics.PdfBrush;
import com.spire.pdf.graphics.PdfBrushes;
import com.spire.pdf.graphics.PdfTrueTypeFont;

import java.awt.Font;
import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;

public class AddPageNumbers {

    public static void main(String[] args) {

        // 加载 PDF 文档
        PdfDocument document = new PdfDocument();
        document.loadFromFile("示例文档.pdf");

        // 设置页码字体和颜色
        PdfTrueTypeFont font = new PdfTrueTypeFont(
                new Font("Microsoft YaHei", Font.PLAIN, 10), true);
        PdfBrush brush = PdfBrushes.getBlack();

        // 创建当前页码字段和总页数字段
        PdfPageNumberField pageNumberField = new PdfPageNumberField();
        PdfPageCountField pageCountField = new PdfPageCountField();

        // 将两个字段组合成"第 X 页,共 Y 页"
        PdfCompositeField pageNumber = new PdfCompositeField(
                font,
                brush,
                "第 {0} 页,共 {1} 页",
                pageNumberField,
                pageCountField
        );

        // 遍历 PDF 中的所有页面
        for (int i = 0; i < document.getPages().getCount(); i++) {

            PdfPageBase page = document.getPages().get(i);
            Dimension2D pageSize = page.getSize();

            // 测量页码文本宽度,用于计算居中位置
            String text = "第 " + (i + 1) + " 页,共 "
                    + document.getPages().getCount() + " 页";
            Dimension2D textSize = font.measureString(text);

            // 计算页码位置
            double x = (pageSize.getWidth() - textSize.getWidth()) / 2;
            double y = pageSize.getHeight() - 30;

            // 在当前页面底部绘制页码
            pageNumber.setLocation(new Point2D.Double(x, y));
            pageNumber.draw(page.getCanvas(), 0, 0);
        }

        // 保存结果文档
        document.saveToFile("添加页码后的PDF.pdf");
        document.dispose();
    }
}

下面分步骤看一下这段代码的实现过程。

第一步:加载 PDF 文档

首先创建 PdfDocument 对象,并加载需要添加页码的 PDF:

java 复制代码
PdfDocument document = new PdfDocument();
document.loadFromFile("示例文档.pdf");

这里处理的是已有 PDF。如果 PDF 本身也是由程序生成的,同样可以在文档最终保存之前执行后面的页码添加操作。

第二步:设置页码的字体和颜色

接下来设置页码使用的字体和颜色:

java 复制代码
PdfTrueTypeFont font = new PdfTrueTypeFont(
        new Font("Microsoft YaHei", Font.PLAIN, 10), true);

PdfBrush brush = PdfBrushes.getBlack();

这里使用 10 磅微软雅黑和黑色文字。

实际使用时,可以根据 PDF 的整体排版调整字体、字号和颜色。例如正式报告中的页码通常不需要太大,9~11 磅一般就足够了。

如果程序部署在 Linux 或服务器环境中,还需要注意系统是否安装了代码中指定的字体。

第三步:创建动态页码

页码的核心部分是下面三个字段:

java 复制代码
PdfPageNumberField pageNumberField = new PdfPageNumberField();
PdfPageCountField pageCountField = new PdfPageCountField();

其中:

PdfPageNumberField 表示当前页面的页码,而 PdfPageCountField 表示整个 PDF 的总页数。

然后通过 PdfCompositeField 将两个字段组合起来:

java 复制代码
PdfCompositeField pageNumber = new PdfCompositeField(
        font,
        brush,
        "第 {0} 页,共 {1} 页",
        pageNumberField,
        pageCountField
);

其中 {0} 对应当前页码,{1} 对应总页数。

假设 PDF 一共有 8 页,最终得到的页码就是:

text 复制代码
第 1 页,共 8 页
第 2 页,共 8 页
第 3 页,共 8 页
...
第 8 页,共 8 页

这种方式的好处是无需提前知道 PDF 到底有多少页,页码和总页数会根据实际文档自动生成。S

第四步:将页码添加到每一页

接下来遍历 PDF 中的所有页面:

java 复制代码
for (int i = 0; i < document.getPages().getCount(); i++) {

    PdfPageBase page = document.getPages().get(i);
    Dimension2D pageSize = page.getSize();

    // ...
}

这里没有直接假设所有页面尺寸完全相同,而是分别读取每一页的大小。

这样对于同时包含 A4 页面、横向页面或其他尺寸页面的 PDF,也可以根据当前页面重新计算页码位置。

第五步:让页码在页面底部居中

如果直接写死 X 坐标,页码可能无法真正居中。因此可以先计算实际显示文本的宽度:

java 复制代码
String text = "第 " + (i + 1) + " 页,共 "
        + document.getPages().getCount() + " 页";

Dimension2D textSize = font.measureString(text);

然后根据页面宽度计算 X 坐标:

java 复制代码
double x = (pageSize.getWidth() - textSize.getWidth()) / 2;
double y = pageSize.getHeight() - 30;

其中:

  • x 用于让页码水平居中
  • y 用于控制页码距离页面底部的位置

最后将组合字段绘制到页面:

java 复制代码
pageNumber.setLocation(new Point2D.Double(x, y));
pageNumber.draw(page.getCanvas(), 0, 0);

Spire.PDF 支持通过页面 Canvas 在指定坐标绘制内容,因此除了居中页码,也可以通过调整 X、Y 坐标将页码放到左下角或右下角。

修改页码格式

如果不需要显示总页数,可以将页码格式改成:

java 复制代码
"第 {0} 页"

得到:

text 复制代码
第 1 页
第 2 页
第 3 页

也可以使用更加简洁的格式:

java 复制代码
"{0} / {1}"

结果类似:

text 复制代码
1 / 10

如果文档页脚中还需要显示报告名称,也可以直接加入固定文本:

java 复制代码
"项目报告 | 第 {0} 页,共 {1} 页"

这样无需改变其他页码处理逻辑。

注意 PDF 底部是否有足够空间

给已有 PDF 添加页码时,有一点需要特别注意:添加页码并不会自动为原有内容腾出页脚空间。

例如,原 PDF 中的表格、正文或签名区域已经延伸到页面底部,此时直接添加页码可能与原有内容重叠。

遇到这种情况,可以适当调整:

java 复制代码
double y = pageSize.getHeight() - 30;

中的 Y 坐标,将页码移动到合适的位置。

如果 PDF 本身也是由程序生成的,更合适的做法是在生成页面内容时提前预留页脚区域。

总结

在批量生成报告、合同或其他多页 PDF 时,通过 Java 自动添加页码可以省去大量重复操作。

Spire.PDF for Java 提供的 PdfPageNumberFieldPdfPageCountField 可以分别获取当前页码和总页数,再通过 PdfCompositeField 将它们组合成 "第 X 页,共 Y 页" 等格式。

在此基础上,只需遍历 PDF 页面并计算合适的坐标,就可以将页码统一添加到每一页的底部,也可以根据实际需求进一步调整页码格式、字体和位置。

相关推荐
风流 少年1 小时前
Spring AI 2.0:MCP
java·后端·spring
天疆说1 小时前
01 硬件选型与 llama.cpp 部署:4× RTX 5880 Ada 跑 DeepSeek-V4-Flash
java·redis·llama
水无痕simon2 小时前
3 短信应用场景以及平台架构
java·服务器
mister_guo2 小时前
JVM 内存管理原理及生产配置实战:从“参数能启动”到“内存可控”
java·jvm
风流 少年2 小时前
Spring AI 2.0:Tool
java·python·spring
爱敲键盘的猴子3 小时前
Java Spring -- 声明式事务控制
java·spring·事务控制
桐桐桐3 小时前
PDF 翻译工具横评与自建批量方案:从在线工具到 Python 自动化
python·pdf·自动化
爱编程的小新☆3 小时前
【LeetCode】从递归到 Flood Fill:5 道题吃透 DFS 的选择、回溯与标记
java·算法·leetcode·深度优先·回溯·flood fill
weixin_383196473 小时前
java基础面试题@Autowired和@Resource区别
java·开发语言