转载:
模板替换内容key是: ${enforcername1}
package com.jsy.test.pdf;
import java.io.FileOutputStream;
import java.io.IOException;import java.util.HashMap;import java.util.List;import java.util.Map;import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;import org.apache.poi.xwpf.usermodel.XWPFDocument;import org.apache.poi.xwpf.usermodel.XWPFParagraph;import org.apache.poi.xwpf.usermodel.XWPFRun;import org.apache.poi.xwpf.usermodel.XWPFTable;import org.apache.poi.xwpf.usermodel.XWPFTableCell;import org.apache.poi.xwpf.usermodel.XWPFTableRow;public class WordReplace {
/*
* 在Word文档中段落的最小的操作单位是XWPFRun,正常的一个段落,会被分割成多个小的XWPFRun,这些XWPFRun组合在一起就是一个完整的段落。 * * * 通常我们在Word文档中做的标记${mark_1},在文档中会被分割成多个XWPFRun,所以我们没法使用一个XWPFRun来进行标记文本替换。在这里, * 我们想到一个方法,就是使用类似于找到字符串中子串下标的方法,找到段落XWPFRun中子Run下标,记录起始和终止下标,在终止下标后insertNewRun * (int pos),然后再从终止下标往前xwpfParagraph.removeRun(i);到起始下标。 * * * 这个方法可以以整个段落位单位进行标记文本替换。然后遍历文档中所有的段落进行替换。 全部代码如下: *//**
* 替换所有段落中的标记 * * @param xwpfParagraphList * @param params */ public static void replaceInAllParagraphs(List<XWPFParagraph> xwpfParagraphList, Map<String, String> params) { for (XWPFParagraph paragraph : xwpfParagraphList) { if (paragraph.getText() == null || paragraph.getText().equals("")) continue; for (String key : params.keySet()) { if (paragraph.getText().contains(key)) { System.err.println("旧值: "+key); replaceInParagraph(paragraph, key, params.get(key)); } } } }/**
* 替换段落中的字符串 * * @param xwpfParagraph * @param oldString * @param newString */ public static void replaceInParagraph(XWPFParagraph xwpfParagraph, String oldString, String newString) { Map<String, Integer> pos_map = findSubRunPosInParagraph(xwpfParagraph, oldString); System.err.println(pos_map.toString()); if (pos_map != null) { System.out.println("start_pos:" + pos_map.get("start_pos")); System.out.println("end_pos:" + pos_map.get("end_pos")); List<XWPFRun> runs = xwpfParagraph.getRuns(); XWPFRun modelRun = runs.get(pos_map.get("end_pos")); XWPFRun xwpfRun = xwpfParagraph.insertNewRun(pos_map.get("end_pos") + 1); System.err.println(newString); xwpfRun.setText(newString); System.out.println("字体大小:" + modelRun.getFontSize()); if (modelRun.getFontSize() != -1) xwpfRun.setFontSize(modelRun.getFontSize());// 默认值是五号字体,但五号字体getFontSize()时,返回-1 xwpfRun.setFontFamily(modelRun.getFontFamily()); for (int i = pos_map.get("end_pos"); i >= pos_map.get("start_pos"); i--) { System.out.println("remove run pos in :" + i); xwpfParagraph.removeRun(i); } } }/**
* 找到段落中子串的起始XWPFRun下标和终止XWPFRun的下标 * * @param xwpfParagraph * @param substring * @return */ public static Map<String, Integer> findSubRunPosInParagraph(XWPFParagraph xwpfParagraph, String substring) { List<XWPFRun> runs = xwpfParagraph.getRuns(); int start_pos = 0; int end_pos = 0; String subtemp = ""; for (int i = 0; i < runs.size(); i++) { subtemp = ""; start_pos = i; for (int j = i; j < runs.size(); j++) { if (runs.get(j).getText(runs.get(j).getTextPosition()) == null) continue; subtemp += runs.get(j).getText(runs.get(j).getTextPosition()); if (subtemp.equals(substring)) { end_pos = j; Map<String, Integer> map = new HashMap<>(); map.put("start_pos", start_pos); map.put("end_pos", end_pos); return map; } } } return null; }// 对表格中标记文本的替换
// 有些标记做在表格单元格中,每个单元格中的内容都是一个普通的段落,所以,我们只需遍历出所有的单元格,然后遍历出每个单元格中的所有段落,再调用以上方法进行标记文本替换即可。代码如下/**
* 替换所有的表格 * * @param xwpfTableList * @param params */ public static void replaceInTables(List<XWPFTable> xwpfTableList, Map<String, String> params) { for (XWPFTable table : xwpfTableList) { replaceInTable(table, params); } }/**
* 替换一个表格中的所有行 * * @param xwpfTable * @param params */ public static void replaceInTable(XWPFTable xwpfTable, Map<String, String> params) { List<XWPFTableRow> rows = xwpfTable.getRows(); replaceInRows(rows, params); }/**
* 替换表格中的一行 * * @param rows * @param params */ public static void replaceInRows(List<XWPFTableRow> rows, Map<String, String> params) { for (int i = 0; i < rows.size(); i++) { XWPFTableRow row = rows.get(i); replaceInCells(row.getTableCells(), params); } }/**
* 替换一行中所有的单元格 * * @param xwpfTableCellList * @param params */ public static void replaceInCells(List<XWPFTableCell> xwpfTableCellList, Map<String, String> params) { for (XWPFTableCell cell : xwpfTableCellList) { replaceInCell(cell, params); } }/**
* 替换表格中每一行中的每一个单元格中的所有段落 * * @param cell * @param params */ public static void replaceInCell(XWPFTableCell cell, Map<String, String> params) { List<XWPFParagraph> cellParagraphs = cell.getParagraphs(); replaceInAllParagraphs(cellParagraphs, params); }// 调用方法测试
public static void main(String[] args) throws IOException, Exception { // TODO Auto-generated method stub String filepathString = "C:\\Users\\Administrator\\Desktop\\现场笔录.docx"; String destpathString = "C:\\Users\\Administrator\\Desktop\\现场笔录_new.docx"; Map<String, String> map = new HashMap<String, String>(); map.put("${enforcername1}", "小白鼠"); map.put("${enforcername2}", "喵喵喵"); map.put("${drivername}", "卡特琳娜"); map.put("${driverphone}", "15112345678");OPCPackage pack = POIXMLDocument.openPackage(filepathString);
XWPFDocument document = new XWPFDocument(pack); /** * 对段落中的标记进行替换 */ List<XWPFParagraph> parasList = document.getParagraphs(); replaceInAllParagraphs(parasList, map); /** * 对表格中的标记进行替换 */ List<XWPFTable> tables = document.getTables(); replaceInTables(tables, map); FileOutputStream outStream = null; try { outStream = new FileOutputStream(destpathString); document.write(outStream); outStream.flush(); outStream.close(); } catch (IOException e) { e.printStackTrace(); } }}
个人感觉有点慢,15KB的文件需要750ms,希望如果有大佬有好的方案可以评论区告诉我!因为我这个还要转PDF然后立即预览的,所以尽量缩减时间.
我现在的方法网页预览需要5164毫秒.