最近项目中有个需求,那就是对pdf文档中的关键字内容进行随意替换,百度跟谷歌上面查了一些资料,已经有大佬给出了该需求的解决方案,目前市面上操作pdf文件使用的比较多的就是itext
和pdfbox
,而且解决的方案都是一样的。
说什么itext本身不提供内容替换功能,其实itext7是提供了内容替换功能的,只是我在做的时候遇到了一些问题,就是文档上面没有的字符替换后会以空格的方式展现出来,有兴趣的同学可以去看看。下面这个就是itext7内容替换的例子。
https://itextpdf.com/en/resources/examples/itext-7/replacing-pdf-objects
需要用到背景遮盖,然后再背景上面继续填写要替换的内容,具体解决方案分为下面这几步
- 找到关键字的坐标位置
- 将关键字坐标设置白色的背景,因为pdf的背景都是白色的
- 将需要替换的关键字填充到白色背景上面
通过上面这几步我们就达到了所谓的替换功能。下面给个效果图
源文件
替换后的文件
核心代码
public class ReplaceUtils {
public static void doOverText(OverTextDTO overTextDTO) throws IOException {
Map<String,String> keyMap = new HashMap<String, String>();
//为key自动加上占位符并转义
for(String key : overTextDTO.getReplaceMap().keySet()) {
keyMap.put(key,"\\$\\{" + key + "\\}");
}
List<List<OverAreaDTO>> lists =
getTextPosition(overTextDTO.getSourceFilePath(),overTextDTO.getFinishFilePath(),keyMap,overTextDTO.getReplaceMap());
overText(overTextDTO.getSourceFilePath(),overTextDTO.getFinishFilePath(),lists,overTextDTO.getFont());
}
/**
* 获取关键字的坐标
* @param keyMap
* @return
*/
public static List<List<OverAreaDTO>> getTextPosition(String sourcePath,String finishPath, Map<String,String> keyMap,Map<String,String> replaceMap) throws IOException {
PdfReader reader = new PdfReader(sourcePath);
PdfDocument pdfDocument = new PdfDocument(reader, new PdfWriter(finishPath));
List<List<OverAreaDTO>> list = new ArrayList<List<OverAreaDTO>>();
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
List<OverAreaDTO> overAreaDTOS = new ArrayList<OverAreaDTO>();
for (String key : keyMap.keySet()) {
PdfPage page = pdfDocument.getPage(i);
RegexBasedLocationExtractionStrategy strategy = new RegexBasedLocationExtractionStrategy(keyMap.get(key));
PdfCanvasProcessor canvasProcessor = new PdfCanvasProcessor(strategy);
canvasProcessor.processPageContent(page);
Collection<IPdfTextLocation> resultantLocations = strategy.getResultantLocations();
PdfCanvas pdfCanvas = new PdfCanvas(page);
pdfCanvas.setLineWidth(0.5f);
for (IPdfTextLocation location : resultantLocations) {
Rectangle rectangle = location.getRectangle();
pdfCanvas.rectangle(rectangle);
pdfCanvas.setStrokeColor(ColorConstants.RED);
pdfCanvas.stroke();
OverAreaDTO overAreaDTO = new OverAreaDTO();
overAreaDTO.setPageNum(location.getPageNumber());
overAreaDTO.setX(rectangle.getX());
overAreaDTO.setY(rectangle.getY());
overAreaDTO.setWidth(rectangle.getWidth());
overAreaDTO.setHeight(rectangle.getHeight());
overAreaDTO.setKey(key);
overAreaDTO.setValue(replaceMap.get(key));
overAreaDTOS.add(overAreaDTO);
}
}
list.add(overAreaDTOS);
}
pdfDocument.close();
return list;
}
/**
* 覆盖原有的内容 并填充新内容
*
* @param sourcePath 源文件
* @param finishPath 替换后的文件
* @param list
*/
public static void overText(String sourcePath,String finishPath,List<List<OverAreaDTO>> list,PdfFont font) throws IOException {
PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourcePath), new PdfWriter(finishPath));
//pdfDoc.getFirstPage().newContentStreamAfter() 会覆盖掉字体
//pdfDoc.getFirstPage().newContentStreamBefore() 只会在字体的下层添加一个背景色
for (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {
PdfCanvas canvas = new PdfCanvas(pdfDoc.getPage(i).newContentStreamAfter(),
pdfDoc.getPage(i).getResources(), pdfDoc);
canvas.saveState();
List<OverAreaDTO> overAreaDTOS = list.get(i-1);
//用白色背景覆盖原本的字体
for (OverAreaDTO overArea :overAreaDTOS) {
canvas.setFillColor(ColorConstants.WHITE);
//覆盖的时候y + 0.35 填充字体的时候 + 1.5 主要就是避免覆盖占位符下面的线
canvas.rectangle(overArea.getX(), overArea.getY() + 0.35, overArea.getWidth(), overArea.getHeight());
}
canvas.fill();
canvas.restoreState();
//填充新内容
canvas.beginText();
for (OverAreaDTO overArea :overAreaDTOS) {
canvas.setFontAndSize(font,overArea.getHeight());
canvas.setTextMatrix(overArea.getX(),overArea.getY() + 1.5f);
canvas.newlineShowText(overArea.getValue());
}
canvas.endText();
}
pdfDoc.close();
}
}