听说你想用Java去掉PDF里全部文本?看这
程序员文章站
2022-03-18 20:33:40
听说你想用Java去掉文章里全部文本?看这,PDFBox去文本...
1、网上查Java处理PDF文档的框架常见就两个iText和PDFBox,其中iText的口碑比较好,网上说功能比PDFBox强大效果更好,但是自己用可以商用需要授权的,就是收钱的,而PDFBox是apache的开源项目,应该不收钱八。
2、根据查阅资料可知,文本信息存PDF结构里面的stream里面,文本工具打开如下图
stream内送是经过压缩处理过的所以显示乱码,压缩算法记在/Filter里面。不过这些都不用考虑,PDFBox加载PDF文档时已经帮忙解压好啦,并将流保存在一个对象里面,取出来用即可。
3、流里面有文本、图像等信息,时通过流里面的操作符区分的,所有的操作符PDFBox已经枚举好了在对象OperatorName里面,含有Tj和DJ字样标识前面是需要展示的文字。要去掉所有文本只需要把这两个操作符前面的内容直接置空即可
4、其中Resources和Contents里面都含有文字的流,直接遍历字典下所有流判断即可
使用的pdfbox版本
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.22</version>
</dependency>
public class PDFUtil{
/**
* 去掉文档里的全部文本信息
*/
public static void removeAllText(PDDocument pdDocument) {
try {
for (PDPage pdPage : pdDocument.getPages()) {
//遍历Resources
PDResources pdResources = pdPage.getResources();
COSDictionary resourceDictionary = pdResources.getCOSObject();
findStreamToRemoveText(resourceDictionary);
//遍历Contents
pdPage.getContentStreams().forEachRemaining(pdStream -> {
removeText(pdStream.getCOSObject());
});
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
/**
* 遍历字典寻找流类型的对象进行去文字
* 注意的是流对象也同时是字典对象
* @param cosDictionary 字典类型
*/
public static void findStreamToRemoveText(COSDictionary cosDictionary) {
for (COSBase resourceBase : cosDictionary.getValues()) {
//流
if (resourceBase instanceof COSStream) {
removeText((COSStream) resourceBase);
}
//字典
if (resourceBase instanceof COSDictionary) {
findStreamToRemoveText((COSDictionary) resourceBase);
}
//对象
if (resourceBase instanceof COSObject) {
if (((COSObject) resourceBase).getObject() instanceof COSStream) {
removeText((COSStream) ((COSObject) resourceBase).getObject());
}
if (((COSObject) resourceBase).getObject() instanceof COSDictionary) {
findStreamToRemoveText((COSDictionary) ((COSObject) resourceBase).getObject());
}
}
}
}
/**
* 去除流里面的文字
*
* @param pdStream 待修改流
*/
public static void removeText(COSStream pdStream) {
try {
PDFStreamParser parser = new PDFStreamParser(pdStream);
try {
parser.parse();
} catch (Exception e) {
//如果有异常说明无法处理流,可能由于流的格式不符合要求,没有文本
return;
}
//获取流解码后的内容
List<Object> tokens = parser.getTokens();
boolean isRemove = false;
for (int i = 0; i < tokens.size(); i++) {
Object token = tokens.get(i);
if (token instanceof Operator) {
Operator operator = (Operator) token;
//流中含有Tj和DJ字样标识前面是需要展示的文字,直接置空去掉
if (operator.getName().equals(OperatorName.SHOW_TEXT)) {
COSString previous = (COSString) tokens.get(i - 1);
//注意指定该编码确保把流写回去,不然会出现乱码导致文档全空(由于PDF流中使用压缩格式)
previous.setValue("".getBytes(StandardCharsets.ISO_8859_1));
isRemove = true;
} else if (operator.getName().equals(OperatorName.SHOW_TEXT_ADJUSTED)) {
COSArray previous = (COSArray) tokens.get(i - 1);
for (int k = 0; k < previous.size(); k++) {
Object arrElement = previous.getObject(k);
if (arrElement instanceof COSString) {
COSString cosString = (COSString) arrElement;
cosString.setValue("".getBytes(StandardCharsets.ISO_8859_1));
isRemove = true;
}
}
}
}
}
//删除了文字才进行流的更新
if (isRemove) {
OutputStream out = pdStream.createOutputStream();
ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
tokenWriter.writeTokens(tokens);
out.close();
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
}
本文地址:https://blog.csdn.net/I_am_hardy/article/details/111991101