如果您想更改页面的内容,仅更改页面的内容流是不够的。页面可能包含对包含您要删除的内容的表单 XObject 的引用。
次要问题是图像。例如:假设您的文档包含经过 OCR 处理的扫描文档。在这种情况下,仅删除(矢量)文本是不够的,您还需要处理图像中的(像素)文本。
假设你的次要问题不存在,你需要一个双重方法:
- 以文本形式从页面中获取内容,以检测哪些页面中有您要删除的名称或字词。
- 递归循环遍历所有内容流以查找该文本并在没有该文本的情况下重写这些内容流。
根据您的问题,我假设您已经解决了问题 1。解决问题 2 并不是那么简单。在我的书的第 15 章中,我有一个示例,其中提取文本返回“Hello World”,但是当您查看内容流内部时,您会看到:
BT
/F1 12 Tf
88.66 367 Td
(ld) Tj
-22 0 Td
(Wor) Tj
-15.33 0 Td
(llo) Tj
-15.33 0 Td
(He) Tj
ET
在您从这个流 sn-p 中删除“Hello World”之前,您需要一些启发式方法,以便您的程序能够识别这种语法中的文本。
找到文本后,您需要重写流。如需灵感,您可以查看 itext-xtra 包中的 OCG remover functionality。
长话短说:如果您的 PDF 相对简单,即:可以在不同的内容流(页面内容和表单 XObject 内容)中轻松检测到文本,那么只需在一些字符串操作后重写这些流即可.
我为您制作了一个名为 ReplaceStream 的简单示例,它将 PDF 中的 "Hello World" 替换为 "HELLO WORLD"。
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfDictionary dict = reader.getPageN(1);
PdfObject object = dict.getDirectObject(PdfName.CONTENTS);
if (object instanceof PRStream) {
PRStream stream = (PRStream)object;
byte[] data = PdfReader.getStreamBytes(stream);
stream.setData(new String(data).replace("Hello World", "HELLO WORLD").getBytes());
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
一些注意事项:
- 我检查
object 是否是一个流。它也可以是一个 array 流。在这种情况下,您需要遍历该数组。
- 我不检查是否有为页面定义的表单 XObjects。
- 我认为
Hello World 可以在 PDF 语法中轻松检测到。
- ...
在现实生活中,PDF 从未如此简单,您的项目的复杂性会随着文档中使用的每个特殊功能而急剧增加。