之前替换PDFBOX文本一直替换不了,查看到网上很多案例全都是直接使用cosstring.setValue。并没有效果。直到研究了PDFTextStripper的showText方法(解析)以及PDPageContentStream的showText方法(写入)才得到最终的解决方案。
首先在官方的exmaple文档中有个RemoveAll的案例这个案例当中是去除所有pdf的文字(github搜索pdfbox找到exmaple),得到图片。可以看到操作符Tj,TJ是写入文字,前一个token就是COSString。Tf是设置文字前两个token是字体。我们对这个方法体改造就可以做到替换文本了。
关键点:PDFont的encode方法,PDFont 的readCode方法,PDFont的toUnicode方法,替换当前PDFont为自己加载的pdfont。
public static List<Object> replaceText(PDContentStream contentStream, PDDocument document,String witch,String replace) throws Exception {
//得到当前页面的所有的资源
PDResources resources = contentStream.getResources();
//解析当前页面,并得到token
PDFStreamParser parser = new PDFStreamParser(contentStream);
Object token = parser.parseNextToken();
List<Object> newTokens = new ArrayList<Object>();
Map<COSName,PDFont> fontMap = new HashMap<COSName, PDFont>();
//遇到字体的时候保留起来,写入文本之前一定会设置字体,因此解析文本的时候上一个设置的字体就是当前文本使用的字体
List<PDFont> souce_fonts = new ArrayList<PDFont>();
/*
* 加载字体(虽然我们得到了当前流的字体但是使用encode的时候无法进行编码,
* 猜测pdfbox并没有加载到相应的字体库绘图文件,所以我们要替换掉对应的字体,
* 为了方便目前中文全用simsun字体替代),最好相应的字体加载相应的文件
* */
PDFont simSunFont = getSimSunFont(document);
while (token != null) {
if (token instanceof Operator) {
Operator op = (Operator) token;
// 得到对应的字体
if ("Tf".equals(op.getName())) {
COSName cosName = (COSName) newTokens.get(newTokens.size() - 2);
PDFont sourceFont = resources.getFont(cosName);
System.out.println(sourceFont.getName());//如果encode异常的时候加入对应的字体
souce_fonts.add(resources.getFont(cosName));
if((sourceFont.getName().toLowerCase().indexOf("simsun")>-1)||(sourceFont.getName().toLowerCase().indexOf("pingfang")>-1) && fontMap.get(cosName) == null) {
fontMap.put(cosName, simSunFont);
}
}
if ("Tj".equals(op.getName()) || "TJ".equals(op.getName()) || "".equals(op.getName())) {
COSString previous = (COSString) newTokens.get(newTokens.size() - 1);
PDType0Font pdFont = (PDType0Font) souce_fonts.get(souce_fonts.size() - 1);
//得到COSString的字节数组,并用原始字体进行读取转换操作拼接成字符串
byte[] bytes = previous.getBytes();
InputStream in = new ByteArrayInputStream(bytes);
StringBuffer sb = new StringBuffer();
while (in.available() > 0) {
int code = pdFont.readCode(in);
String value = pdFont.toUnicode(code);
sb.append(value);
}
System.out.println("原有字符串:" + sb.toString());
//替换文本
previous.setValue(simSunFont.encode(sb.toString().replace(witch, replace)));
} else if ("\"".equals(op.getName())) {
// remove the 3 arguments to this operator
newTokens.remove(newTokens.size() - 1);
newTokens.remove(newTokens.size() - 1);
newTokens.remove(newTokens.size() - 1);
token = parser.parseNextToken();
}
}
newTokens.add(token);
token = parser.parseNextToken();
}
//统一替换字体资源
Set<Entry<COSName, PDFont>> entrySet = fontMap.entrySet();
for (Entry<COSName, PDFont> entry : entrySet) {
resources.put(entry.getKey(), entry.getValue());
}
return newTokens;
}
public static PDFont getSimSunFont(PDDocument document) throws IOException {
TrueTypeCollection ttc3 = new TrueTypeCollection(
new File("G:\\projects\\pdfdetail\\src\\main\\resources\\simsun.ttc"));
TrueTypeFont trueTypeFont = ttc3.getFontByName("SimSun");
PDType0Font sourceFont = PDType0Font.load(document, trueTypeFont, true);
return sourceFont;
}