PDFBOX替换文本(pdfbox版本2.0.24)

之前替换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;
	}

上一篇:Spring事务——TransacationInterceptor源码学习


下一篇:Python 的AES加密与解密