在日常开发中,经常需要对 PDF 文档做页面级别的编辑------删除多余页面、调整页面顺序、旋转方向错误的页面,或者裁剪掉不需要的边距。这类需求看起来简单,真动手写的时候,索引变化、坐标系方向这些细节很容易踩坑。本文通过 Java 代码示例,逐一介绍这几种页面操作的具体实现。
环境准备
这里用到的是 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.7.0</version>
</dependency>
</dependencies>
不用 Maven 的话,官网也能直接下 jar 包手动导入项目。添加依赖后,所有页面相关的操作基本都是围绕 PdfDocument 这个类和它的 getPages() 页面集合展开的,下面分别看看。
一、删除页面
删除页面是最常见的操作。用 PdfDocument.getPages().removeAt(int index) 方法,通过指定索引(从 0 开始)删除特定页面。
java
import com.spire.pdf.PdfDocument;
public class DeletePage {
public static void main(String[] args) {
// 创建 PdfDocument 实例
PdfDocument pdf = new PdfDocument();
// 加载 PDF 文档
pdf.loadFromFile("sample.pdf");
// 删除第二页(索引从 0 开始,1 表示第二页)
pdf.getPages().removeAt(1);
// 保存文档
pdf.saveToFile("output/deletePage.pdf");
pdf.close();
}
}
页码从 0 开始计数,容易跟常见的"第一页=1"搞混,操作前最好再确认一遍索引。删除操作不可逆,建议处理前备份原始文件。
如果要一次删多个页面,要注意从后往前删。比如要删第 2、4、6 页,如果正序遍历删除,删完第 2 页后,原来的第 4 页就顶到了第 3 页的位置,接着还按原始索引删就全乱了:
java
int[] pagesToDelete = {1, 3, 5}; // 对应第2、4、6页
Arrays.sort(pagesToDelete);
for (int i = pagesToDelete.length - 1; i >= 0; i--) {
pdf.getPages().removeAt(pagesToDelete[i]);
}
二、调整页面顺序
页面顺序混乱时,用 PdfDocument.getPages().reArrange() 方法重排。该方法接收一个 int[] 数组,数组中每个元素代表原文档的页面索引,按数组顺序组成新的页面排列。
java
import com.spire.pdf.PdfDocument;
public class RearrangePages {
public static void main(String[] args) {
// 创建 PdfDocument 实例
PdfDocument doc = new PdfDocument();
// 加载 PDF 文档
doc.loadFromFile("input.pdf");
// 重排页面顺序:原第1页、第3页、第2页、第4页
doc.getPages().reArrange(new int[]{0, 2, 1, 3});
// 保存文档
doc.saveToFile("output/rearranged.pdf");
doc.close();
}
}
三、旋转页面
扫描版 PDF 经常会出现方向错误的问题。旋转基于 90 度增量,支持 0°/90°/180°/270° 四个方向,通过 PdfPageBase.setRotation() 方法设置。
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.PdfPageRotateAngle;
public class RotatePdfPage {
public static void main(String[] args) {
// 创建 PdfDocument 实例
PdfDocument pdf = new PdfDocument();
// 加载 PDF 文档
pdf.loadFromFile("sample.pdf");
// 获取第一页
PdfPageBase page = pdf.getPages().get(0);
// 获取当前旋转角度
int rotation = page.getRotation().getValue();
// 在原有角度基础上旋转 180 度
rotation += PdfPageRotateAngle.Rotate_Angle_180.getValue();
page.setRotation(PdfPageRotateAngle.fromValue(rotation));
// 保存文档
pdf.saveToFile("output/rotated.pdf");
pdf.close();
}
}
要注意这里是在原有角度基础上做加法,不是直接赋值。扫描件本身可能已经带了旋转属性(手机拍照生成的 PDF 很多默认就是 270 度),如果不先读一下原始角度就直接赋新值,最终显示效果跟预期会完全不一样。
批量旋转所有页面,遍历一下 pdf.getPages() 集合逐个处理即可:
java
for (int i = 0; i < pdf.getPages().getCount(); i++) {
PdfPageBase page = pdf.getPages().get(i);
int rotation = page.getRotation().getValue();
rotation += PdfPageRotateAngle.Rotate_Angle_90.getValue();
page.setRotation(PdfPageRotateAngle.fromValue(rotation));
}
四、裁剪页面
裁剪的本质是修改页面的 CropBox(裁剪框),定义页面内容的可见区域,超出该区域的内容不会显示。用 PdfPageBase.setCropBox(Rectangle2D rect) 方法实现。
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import java.awt.geom.Rectangle2D;
public class CropPdfPage {
public static void main(String[] args) {
// 创建 PdfDocument 实例
PdfDocument pdf = new PdfDocument();
// 加载 PDF 文档
pdf.loadFromFile("example.pdf");
// 获取第一页
PdfPageBase page = pdf.getPages().get(0);
// 定义裁剪区域:参数依次为 x、y、宽度、高度
Rectangle2D rect = new Rectangle2D.Float(0, 0, 550, 822);
// 设置裁剪框
page.setCropBox(rect);
// 保存裁剪后的文档
pdf.saveToFile("output/cropped.pdf");
pdf.close();
}
}
Rectangle2D.Float 的四个参数单位都是 pt。这里有个注意事项得单独说一下:Spire.PDF 的坐标系统跟原生 PDF 规范是不一样的。PDF 规范里坐标原点定义在页面左下角,y 轴向上;但 Spire.PDF 为了方便程序员"自上而下"地在页面上定位内容,把坐标系统统一成了原点在左上角、y 轴向下。setCropBox 传入的 Rectangle2D 也遵循这套坐标系:x、y 表示裁剪区域左上角相对页面左上角的偏移,width、height 从这个点开始向右、向下延伸。
所以想裁掉页面顶部的一条黑边,直接把 y 设成要裁掉的高度就行,不需要用页面总高度反推。比如页面顶部有 20pt 黑边要裁掉,就写 new Rectangle2D.Float(0, 20, width, height - 20)------从距离顶部 20pt 的地方开始往下延伸到页面底部,正好跳过顶部黑边。之前下意识按"PDF 坐标原点在左下角"这个印象去算,反而是把方向搞反了,裁出来的效果要么黑边还在,要么内容被切掉一大截,后来才意识到 Spire.PDF 这一层是把坐标系统重新定义过的,不能直接套用 PDF 规范里的原始定义去想当然。
另外,setCropBox 改的只是可视区域,原始内容依然完整保留在文件里,只是渲染时超出裁剪框的部分不显示,不算真正意义上的内容删除。涉及敏感信息脱敏的场景,不能靠裁剪来实现。
综合应用示例
实际项目里这几个操作往往是组合使用的。以下示例演示"删除页面 → 重排顺序 → 旋转页面 → 裁剪页面"整合到一起的流程:
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.PdfPageBase;
import com.spire.pdf.PdfPageRotateAngle;
import java.awt.geom.Rectangle2D;
public class PdfPageOperations {
public static void main(String[] args) {
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile("original.pdf");
// 1. 删除第 3 页(索引为 2)
pdf.getPages().removeAt(2);
// 2. 重排剩余页面顺序
pdf.getPages().reArrange(new int[]{0, 1, 3, 4});
// 3. 旋转第一页 90 度
PdfPageBase page = pdf.getPages().get(0);
int rotation = page.getRotation().getValue();
rotation += PdfPageRotateAngle.Rotate_Angle_90.getValue();
page.setRotation(PdfPageRotateAngle.fromValue(rotation));
// 4. 裁剪第二页
PdfPageBase page2 = pdf.getPages().get(1);
Rectangle2D rect = new Rectangle2D.Float(50, 50, 500, 700);
page2.setCropBox(rect);
// 保存最终结果
pdf.saveToFile("output/final.pdf");
pdf.close();
}
}
顺序上建议先做删除、排序这类会改变页面索引的操作,再做旋转、裁剪这类针对具体页面对象的操作,这样代码逻辑更清晰,也不容易因为索引变化而误操作到错误的页面。
小结
这篇文章介绍了如何在 Java 中实现 PDF 页面删除、排序、旋转和裁剪。删除页面用 removeAt(),重排页面用 reArrange(),旋转页面用 setRotation(),裁剪页面用 setCropBox(),这几个方法基本覆盖了日常开发中常见的页面编辑需求。有几点需要注意:批量删除要倒序操作;旋转角度是累加而不是覆盖;裁剪的坐标系原点在左下角,跟常见的图形坐标习惯相反。这几个细节提前留意,能省不少调试时间。