一、poi word關鍵字替換踩過的巨坑
案例:@加盟商} 把關鍵字替換成爲南京海瀚軟體科技有限公司
通過XWPFRun獲取文字時,可能會被word分成了三段 @ 加盟商 },這時無法識別一個完整的變數去做替換。
二、解決方式
1、不要在word中直接輸入參數,通過在文字中寫好複製貼上過來。
經過實踐證明某些欄位確實可以,但是還是會出現被分段。(wps去做變數時不分段率高一些)
2、將word另存爲xml檔案,在編輯器中將變數在一個<w:t></w:t>節點裏完整的放在裏面
這種方式最終結果是可以實現的。但是在xml中找非常費神且容易改錯,且文件量過大的時候,每一個都去改不太實際
3、使用程式碼的方式去做替換
// 遍歷獲取段落中所有的runs
List<XWPFRun> runs = paragraph.getRuns();
// 合併邏輯
for (int i = 0; i < runs.size(); i++) {
String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
if (text0 != null&&text0.contains("@")) {
int startIndex = text0.lastIndexOf("@");
int endIndex = 1;
if (startIndex != -1) {
endIndex = text0.substring(startIndex).indexOf("}");
}
if (endIndex < 0) {
// 記錄分隔符中間跨越的runs數量,用於字串拼接和替換
int num = 0;
int j = i+1 ;
for (; j < runs.size(); j++) {
String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
if (text1 != null && text1.contains("}")) {
num = j - i;
break;
}
}
if (num != 0) {
// num!=0說明找到了@@配對,需要替換
StringBuilder newText = new StringBuilder();
for (int s = i; s <= i + num; s++) {
String text2 = runs.get(s).getText(runs.get(s).getTextPosition());
String replaceText = text2;
if(s==i&&text2.contains("@")&&text2.contains("}")) {
newText.append(text2);
}
else if(s==i&&text2.contains("@")) {
replaceText = text2.substring(0, text2.indexOf("@"));
newText.append(text2.substring(text2.indexOf("@")));
}
else if (text2.contains("}")) {
replaceText = text2.substring(replaceText.indexOf("}") + 1);
newText.append(text2.substring(0, text2.indexOf("}") + 1));
} else {
replaceText = "";
newText.append(text2);
}
runs.get(s).setText(replaceText, 0);
}
runs.get(i).setText(newText.toString(), 0);
i = i - 1;
}
}
}
}
最後附上poi 處理 word,關鍵字替換文字、圖片、插入excel表、轉pdf的完整案例程式碼
package com.agilecontrol.fair.ifc.gold.poi.utils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.Cell;
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;
import org.apache.xmlbeans.XmlCursor;
import org.hsqldb.lib.HashMap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblBorders;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.support.DaoSupport;
import com.agilecontrol.fair.FairConfig;
import com.agilecontrol.fair.ifc.gold.FtpUtils;
import com.agilecontrol.fair.ifc.gold.poi.model.CustomXWPFDocument;
import com.agilecontrol.fair.ifc.gold.sap.SAPWebServiceClient_FreezeCustomer;
import com.agilecontrol.fair.ifc.gold.utils.ConnUtils;
import com.agilecontrol.nea.core.query.QueryEngine;
import com.agilecontrol.nea.core.query.QueryException;
import com.agilecontrol.nea.util.NDSException;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import com.lowagie.text.Row;
/**
* @Title: ConnUtils.java
* @Package com.agilecontrol.fair.ifc.gold.utils
* @Description: TODO(MD三期附件WORD生成PDF)
* @author gaigai
* @date 2020年8月4日
* @version V1.0
*/
public class GeneratePdfUntils {
protected static Logger logger = LoggerFactory.getLogger(GeneratePdfUntils.class);
private static final int wdFormatPDF = 17; // PDF 格式
QueryEngine engine;
public boolean generatePdf(JSONArray param, JSONObject data, String template, String descpath, String shamName,
String sfileName, String toFileName, String readname, int ht_attach_id) throws Exception {
boolean flag = true;
try {
engine = QueryEngine.getInstance();
generateWord(param, data, template, descpath);
wordToPDF(shamName, sfileName, toFileName, readname, ht_attach_id);
} catch (Exception e) {
// TODO: handle exception
flag = false;
logger.debug("【MD三期附件WORD生成PDF錯誤資訊:{}】", e.getMessage());
} finally {
// TODO: handle finally clause
}
return flag;
}
/**
* word 先替換變數再生成pdf
*
* @param shamName 假名字 @param sfileName 原始檔地址 @param toFileName 複製到哪裏去的地址
* @param readname 真名字 @param ht_attach_id 附件id
*/
public void wordToPDF(String shamName, String sfileName, String toFileName, String readname, int ht_attach_id)
throws Exception {
readname = readname.split("//.")[0];
logger.debug("啓動 Word...");
ActiveXComponent app = null;
String readurl = sfileName + ht_attach_id + '.' + shamName.split("\\.")[1];
logger.debug("readurl:" + readurl);
try {
ComThread.InitSTA(true);
app = new ActiveXComponent("Word.Application");
app.setProperty("Visible", false);
Dispatch docs = app.getProperty("Documents").toDispatch();
Dispatch doc = Dispatch.call(docs, //
"Open", //
readurl, // FileName
false, // ConfirmConversions
true // ReadOnly
).toDispatch();
if (!FtpUtils.isPortalPathExist(toFileName)) {
FtpUtils.isChartPathExist(toFileName);
}
SimpleDateFormat df = new SimpleDateFormat("yyMMddHHmmss");// 設定日期格式
String time = df.format(new Date());
String filename = toFileName + "\\" + shamName.split("\\.")[0] + ".pdf";
File tofile = new File(filename);
if (tofile.exists()) {
tofile.delete();
}
Dispatch selection = Dispatch.get(app, "Selection").toDispatch();// 選定範圍或者插入點
long start = System.currentTimeMillis();
Dispatch.call(doc, "SaveAs", filename, // FileName
wdFormatPDF);
String targetPath = toFileName + "\\" + "attachment.properties";
Properties prop = new Properties();
String propertiesTemplatePath = FairConfig.PROJECT_ADDRESS + "\\" + "attachment.properties";
// 複製檔案到建立地址然後修改內容
FtpUtils.copyFile(propertiesTemplatePath, targetPath);
FileInputStream fis = new FileInputStream(targetPath);
prop.load(fis);
fis.close();
prop.setProperty(shamName.split("\\.")[0] + ".filename", readname.split("\\.")[0] + ".pdf");
prop.setProperty("ext", "pdf");
FileOutputStream foss = new FileOutputStream(targetPath);
prop.store(foss, "Copyright (c) Boxcode Studio");
String[] splitFileId = toFileName.split("\\\\");
String updateFileName = "UPDATE m_htattach SET file_fill ='" + readname.split("\\.")[0] + time + ".pdf"
+ "' WHERE id = " + ht_attach_id;
int sus = engine.executeUpdate(updateFileName);
long end = System.currentTimeMillis();
Dispatch.call(doc, "Close", false);
} catch (Exception e) {
logger.debug("========Error:文件轉換失敗:" + e.getMessage());
throw new Exception(e);
} finally {
File tofile = new File(readurl);
if (tofile.exists()) {
tofile.delete();
}
if (app != null)
app.invoke("Quit", new Variant[] {});
// 如果沒有這句話,winword.exe進程將不會關閉
ComThread.Release();
}
}
public static boolean generateWord(JSONArray param, JSONObject data, String template, String descpath) {
CustomXWPFDocument doc = null;
boolean flag = false;
try {
OPCPackage pack = POIXMLDocument.openPackage(template);
doc = new CustomXWPFDocument(pack);
if (param != null && param.length() > 0) {
// 處理段落
List<XWPFParagraph> paragraphList = doc.getParagraphs();
processParagraphs(paragraphList, param, data, doc);
// 處理表格
Iterator<XWPFTable> it = doc.getTablesIterator();
while (it.hasNext()) {
XWPFTable table = it.next();
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
processParagraphs(paragraphListTable, param, data, doc);
}
}
}
}
FileOutputStream fopts = new FileOutputStream(descpath);
doc.write(fopts);
fopts.close();
flag = true;
} catch (Exception e) {
flag = false;
e.printStackTrace();
}
return flag;
}
public static void processParagraphs(List<XWPFParagraph> paragraphList, JSONArray map, JSONObject data,
CustomXWPFDocument doc) throws JSONException, Exception {
if (paragraphList != null && paragraphList.size() > 0) {
for (XWPFParagraph paragraph : paragraphList) {
// 遍歷獲取段落中所有的runs
List<XWPFRun> runs = paragraph.getRuns();
// 合併邏輯
for (int i = 0; i < runs.size(); i++) {
String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
if (text0 != null&&text0.contains("@")) {
int startIndex = text0.lastIndexOf("@");
int endIndex = 1;
if (startIndex != -1) {
endIndex = text0.substring(startIndex).indexOf("}");
}
if (endIndex < 0) {
// 記錄分隔符中間跨越的runs數量,用於字串拼接和替換
int num = 0;
int j = i+1 ;
for (; j < runs.size(); j++) {
String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
if (text1 != null && text1.contains("}")) {
num = j - i;
break;
}
}
if (num != 0) {
// num!=0說明找到了@@配對,需要替換
StringBuilder newText = new StringBuilder();
for (int s = i; s <= i + num; s++) {
String text2 = runs.get(s).getText(runs.get(s).getTextPosition());
String replaceText = text2;
if(s==i&&text2.contains("@")&&text2.contains("}")) {
newText.append(text2);
}
else if(s==i&&text2.contains("@")) {
replaceText = text2.substring(0, text2.indexOf("@"));
newText.append(text2.substring(text2.indexOf("@")));
}
else if (text2.contains("}")) {
replaceText = text2.substring(replaceText.indexOf("}") + 1);
newText.append(text2.substring(0, text2.indexOf("}") + 1));
} else {
replaceText = "";
newText.append(text2);
}
runs.get(s).setText(replaceText, 0);
}
runs.get(i).setText(newText.toString(), 0);
i = i - 1;
}
}
}
}
String content = paragraph.getParagraphText();
// 從第一個@符號到第二個@做拼接
for (int j = 0; j < runs.size(); j++) {
String text = runs.get(j).getText(runs.get(j).getTextPosition());
// logger.debug(text);
boolean isSetText = false;
if (text != null) {
for (int i = 0; i < map.length(); i++) {
String key = map.getJSONObject(i).optString("paraname");
String colcode = map.getJSONObject(i).optString("colcode").toLowerCase();
// 防止@格式不對
if (text.indexOf(key) >= 0) {
isSetText = true;
String value = data.optString(colcode);
if (key.contains("URL")) {// 圖片替換
text = text.replace(key, "");
if (value == null || value.length() == 0) {
continue;
}
int width = 220;
int height = 150;
int picType = getPictureType("PNG");
String getTrueFileName = FtpUtils
.getTrueFileName(FairConfig.PROJECT_ADDRESS + value);
if (getTrueFileName.length() == 0 || getTrueFileName == null) {
continue;
}
String url = FairConfig.PROJECT_ADDRESS + value + File.separatorChar
+ FtpUtils.getShamName(getTrueFileName, FairConfig.PROJECT_ADDRESS + value);
byte[] byteArray = (byte[]) WordReplaceUtil.inputStream2ByteArray(new FileInputStream(url),
true);
ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
try {
doc.addPicture(byteArray, picType);
doc.createPicture(doc.getAllPictures().size() - 1, width, height, paragraph);
} catch (Exception e) {
e.printStackTrace();
}
} else if (key.contains("XLS")) {
isSetText = true;
text = text.replace(key, "");
XmlCursor cursor = paragraph.getCTP().newCursor();
// 在指定遊標位置插入表格
XWPFTable table = doc.insertNewTbl(cursor);
if (table == null) {
continue;
}
JSONArray dataArray = data.optJSONArray(key);
if (dataArray != null && dataArray.length() > 0) {
inserInfo(table, dataArray);
}
} else {// 文字替換
text = text.replace(key, value);
isSetText = true;
}
}
}
if (isSetText) {
runs.get(j).setText(text, 0);
}
}
}
}
}
// if (paragraphList != null && paragraphList.size() > 0) {
// for (XWPFParagraph paragraph1 : paragraphList) {
// // 遍歷獲取段落中所有的runs
// List<XWPFRun> runs1 = paragraph1.getRuns();
// // 合併邏輯
// for (int i = 0; i < runs1.size(); i++) {
// String text = runs1.get(i).getText(runs1.get(i).getTextPosition());
// logger.debug("替換後的文字:"+text);
//
// }
// }
// }
}
private static int getPictureType(String picType) {
int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
if (picType != null) {
if (picType.equalsIgnoreCase("png")) {
res = CustomXWPFDocument.PICTURE_TYPE_PNG;
} else if (picType.equalsIgnoreCase("dib")) {
res = CustomXWPFDocument.PICTURE_TYPE_DIB;
} else if (picType.equalsIgnoreCase("emf")) {
res = CustomXWPFDocument.PICTURE_TYPE_EMF;
} else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
} else if (picType.equalsIgnoreCase("wmf")) {
res = CustomXWPFDocument.PICTURE_TYPE_WMF;
}
}
return res;
}
public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {
byte[] byteArray = null;
try {
int total = in.available();
byteArray = new byte[total];
in.read(byteArray);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (isClose) {
try {
in.close();
} catch (Exception e2) {
System.out.println("關閉流失敗");
}
}
}
return byteArray;
}
/**
* 把資訊插入表格
* @param table
* @param data
*/
public static void inserInfo(XWPFTable table, JSONArray dataArray) {
System.out.println("【生成表格開始】");
// 表格屬性
CTTblPr tablePr = table.getCTTbl().addNewTblPr();
CTTblBorders borders = tablePr.addNewTblBorders();
CTBorder hBorder = borders.addNewInsideH();
hBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
hBorder.setSz(new BigInteger("1")); // 線條大小
hBorder.setColor("000000"); // 設定顏色
CTBorder wBorder = borders.addNewInsideV();
wBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
wBorder.setSz(new BigInteger("1")); // 線條大小
wBorder.setColor("000000"); // 設定顏色
CTBorder bBorder = borders.addNewBottom();
bBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
bBorder.setSz(new BigInteger("1")); // 線條大小
bBorder.setColor("000000"); // 設定顏色
CTBorder tBorder = borders.addNewTop();
tBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
tBorder.setSz(new BigInteger("1")); // 線條大小
tBorder.setColor("000000"); // 設定顏色
CTBorder lBorder = borders.addNewLeft();
lBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
lBorder.setSz(new BigInteger("1")); // 線條大小
lBorder.setColor("000000"); // 設定顏色
CTBorder rBorder = borders.addNewRight();
rBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
rBorder.setSz(new BigInteger("1")); // 線條大小
rBorder.setColor("000000"); // 設定顏色
JSONObject obj = dataArray.optJSONObject(0);
XWPFTableRow firstrow = table.createRow();
XWPFTableCell firstcell = null;
String cols[] = JSONObject.getNames(obj);
for (int j = 0; j < cols.length; j++) {
// 第一行建立了多少列,後續增加的行自動增加列
String key = cols[j];
firstcell = firstrow.addNewTableCell();
firstcell.setText(key);
CTTc cttc = firstcell.getCTTc();
CTTcPr ctPr = cttc.addNewTcPr();
ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
ctPr.addNewTcW().setW(BigInteger.valueOf(3000));
cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
}
XWPFTableRow row = null;
XWPFTableCell cell = null;
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.optJSONObject(i);
row = table.createRow();
for (int j = 0; j < cols.length; j++) {
cell = row.getCell(j);
cell.setText(data.optString(cols[j]));
// 設定水平居中,需要ooxml-schemas包支援
CTTc cttc = cell.getCTTc();
CTTcPr ctPr = cttc.addNewTcPr();
ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
ctPr.addNewTcW().setW(BigInteger.valueOf(3000));
cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
}
}
}
}