1.导入jar
<dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.4.3</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency>
2.导出
//pdf template path private static String TEMPLATEPATH = ""; //new pdf path private static String NEWPATH = ""; /** * @description: pdf export */ public static String exportPdfFile(String numName,Map<String,Object> objectMap,String templateName,String newName){ // 参数转换 Map<String,String> map = checkPdfTemplate(numName,objectMap); // 生成pdf文件 String newFilePath = pdfOut(map,"",""); return newFilePath; } /** * @description: check pdf Template */ public static Map<String,String> checkPdfTemplate(String numName,Map<String,Object> objectMap){ Map<String,Object> map = new HashMap<>(); if (numName.equals("xx")){ map = pdfParams(objectMap); } Map<String,String> maps = map.entrySet().stream() .filter(e -> e.getValue() != null) .collect(Collectors.toMap(Map.Entry::getKey,e-> (String)e.getValue())); return maps; } /** * @description: pdf params package */ public static Map<String,Object> pdfParams(Map<String,Object> objectMap){ Map<String,Object> map = new HashMap<>(); map.put("xxx",objectMap.get("")); return map; } /** * @description: template create pdf */ public static String pdfOut(Map<String,String> params, String templateName, String newName ){ PdfReader reader; FileOutputStream out; ByteArrayOutputStream bos; PdfStamper stamper; Document doc = null; try { BaseFont bf = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED); // 生成文件流 out = new FileOutputStream(NEWPATH+newName); // 读取pdf模板 reader = new PdfReader(TEMPLATEPATH+templateName); bos = new ByteArrayOutputStream(); stamper = new PdfStamper(reader, bos); AcroFields form = stamper.getAcroFields(); // 参数值转换为formMap form.addSubstitutionFont(bf); for(String key : params.keySet()){ String value = params.get(key); form.setField(key,value); } // 如果为false,生成的PDF文件可以编辑,如果为true,生成的PDF文件不可以编辑 stamper.setFormFlattening(false); stamper.close(); doc = new Document(); PdfCopy copy = new PdfCopy(doc, out); doc.open(); PdfImportedPage importPage = null; // 循环复制页面 for (int i=1;i<=reader.getNumberOfPages();i++){ importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), i); copy.addPage(importPage); } doc.close(); }catch (Exception e){ throw new PdfException(e); }finally { doc.close(); return NEWPATH+newName; } }
2.1模板制作
模板使用Adobe Acrobat X Pro进行制作(下载正版工具,试用期30天,可以下载注册机激活)
第一步,打开模板文件,没有模板文件就创建空白文件
第二步,在Tools里面找到Forms,点击Edit(编辑)进入的下面这个页面
第三步,点击Add New Field,选择合适的工具框,文本类的一般选择Text Field,拖拽文本框到合适的位置,双击编辑文本名称(最好自己定义名称,不要使用默认的)
第四步,选择File->Save as->PDF(另保存),保存完模板就可以直接使用了
map.put("xxx",objectMap.get("")); //xxx对应Text Field的名称,进行遍历赋值操作
3.导入
/** * @description: pdf import */ public static Map<String,Object> importPdfFile(MultipartFile file) throws PdfException { PdfReader reader = null; ByteArrayOutputStream bos; PdfStamper stamper; Map<String,Object> map1 = new HashMap<>(); String con = ""; try { BaseFont bf = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED); reader = new PdfReader(file.getInputStream()); bos = new ByteArrayOutputStream(); stamper = new PdfStamper(reader, bos); AcroFields form = stamper.getAcroFields(); form.addSubstitutionFont(bf); PdfReaderContentParser parser = new PdfReaderContentParser(reader); int pageNum = reader.getNumberOfPages(); String pageContent = ""; // 只能从第1页开始读 for (int i = 1; i <= pageNum; i++) { PdfRenderListener listener = new PdfRenderListener(); // 解析PDF,并处理里面的文字 parser.processContent(i, listener); List<Map<String, Rectangle2D.Float>> list_text = listener.rows_text_rect; for (int k = 0; k < list_text.size(); k++) { Map<String, Rectangle2D.Float> map = list_text.get(k); for (Map.Entry<String, Rectangle2D.Float> entry : map.entrySet()) { //每个内容和对应的坐标 //System.out.println(entry.getKey() + "---" + entry.getValue()); con += entry.getKey(); } } } }catch (Exception e){ throw new PdfException(e); }finally { reader.close(); } System.out.println("con = " + con); return map1; }
3.1PdfRenderListener解析规则,解析规则需要实现RenderListener接口自定义规则
public class PdfRenderListener implements RenderListener { /*----------------*/ //存放文字的矩形 public List<Rectangle2D.Float> rectText = new ArrayList<>(); //存放文字 public List<String> textList = new ArrayList<>(); //存放文字的Y坐标 public List<Float> listY = new ArrayList<>(); //存放每一行文字的坐标位置 public List<Map<String,Rectangle2D.Float>> rows_text_rect = new ArrayList<>(); @Override public void beginTextBlock() { //解析之前做处理 } //文字处理 @Override public void renderText(TextRenderInfo textRenderInfo) { String text = textRenderInfo.getText(); if (text.length() > 0){ RectangularShape rectBase = textRenderInfo.getBaseline().getBoundingRectange(); //rectAsce测试和rectBase结果一样,未发现明显区别 Rectangle2D.Float rectAsce = textRenderInfo.getAscentLine().getBoundingRectange(); float leftX = (float) rectBase.getMinX(); float leftY = (float) rectBase.getMinY(); float rightX = (float) rectBase.getMaxX(); float rightY = (float) rectBase.getMaxY(); Rectangle2D.Float rect = new Rectangle2D.Float(leftX, leftY, rightX - leftX, rightY - leftY); if(listY.contains(rect.y)){ int index = listY.indexOf(rect.y); float tempx = rect.x > rectText.get(index).x ? rectText.get(index).x : rect.x; rectText.set(index,new Rectangle2D.Float(tempx,rect.y,rect.width + rectText.get(index).width,rect.height)); textList.set(index,textList.get(index) + text); }else{ rectText.add(rect); textList.add(text); listY.add(rect.y); } Map<String,Rectangle2D.Float> map = new HashMap<>(); map.put(text,rect); rows_text_rect.add(map); } } @Override public void endTextBlock() { //解析之后操作 } @Override public void renderImage(ImageRenderInfo imageRenderInfo) { //解析图片操作 } }
ps:目前获取到的PDF文字,使用模板再次导入的情况下只能获取到固定的文字,Field的获取不到,Field获取思路:1.通过坐标轴获取,文档不复杂还好,不然工作量很大 2.jar中工具类获取(目前还没找到),这块后面有时间再研究下,后补。