package com.mis.webdemo.word3;
import lombok.SneakyThrows;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* https://blog.csdn.net/weixin_44076772/article/details/97797654
* 测评文件导出工具类
* https://blog.csdn.net/qq_53739052/article/details/130348820
*/
public class ExportUtils3 {
//占位符数组
private static String[] PLACEHOLDER = {"${company}", "${address}", "${secondParty}", "${quickMark}", "${a}", "${b}","${c}"};
//图片占位符单独处理
private static String[] PICTURE_PLACEHOLDER = {"${quickMark}"};
private static int WIDTH = 100; //100%
private static int HEIGHT = 100; //100%
public static final String XPATH_TXBX_TXBXCONTENT = "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' \n"
+ " declare namespace mc='http://schemas.openxmlformats.org/markup-compatibility/2006' .//mc:Choice/*/w:txbxContent";
public static final String XPATH_TEXTBOX_TXBXCONTENT = "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' \n"
+ " declare namespace mc='http://schemas.openxmlformats.org/markup-compatibility/2006' .//mc:Fallback/*/w:txbxContent";
public static final String XPATH_PICT_TEXTBOX_TXBXCONTENT = "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' \n"
+ " declare namespace v='urn:schemas-microsoft-com:vml' ./v:shape/v:textbox/w:txbxContent";
public static final String XPATH_TEXTBOX_TXBXCONTENT2 = "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:txbxContent/w:p/w:r";
/**
* 一页一码word导出
* @param dtos 二维码数据列表
* @param response
*/
@SneakyThrows
public static void UniqueCodeWordExport(List<TicketProject3> dtos, HttpServletResponse response) {
//生成文档的名称列表
//List<String> fileNameList = new ArrayList<>();
for (TicketProject3 dto : dtos) {
String filePath = "C:\\PLM系统-ESS产品开发过程-文档自动生成功能 SOW_V5.0.docx"; //模板地址
InputStream fis = new FileInputStream(new File(filePath));
XWPFDocument doc = new XWPFDocument(fis);
// List<XWPFParagraph> paragraphList = doc.getParagraphs();
// for (XWPFParagraph paragraph : paragraphList){
// List<XWPFRun> runList = paragraph.getRuns();
// for (XWPFRun run : runList){
// //System.out.println(run.getCTR());
// XmlObject xmlObject = (XmlObject) run.getCTR();
// XmlObject[] xmlObjects = xmlObject.selectPath(XPATH_TEXTBOX_TXBXCONTENT2);
// if (xmlObjects !=null && xmlObjects.length>=1){
// CTR ctr = CTR.Factory.parse(xmlObjects[0].xmlText());
// XWPFRun bufferrun = new XWPFRun(ctr, (IRunBody)paragraph);
// //System.out.println(bufferrun);
// String text = bufferrun.text();
// if (text != null && text.contains("${a}")) {
// text = text.replace("${a}","replaced");
// bufferrun.setText(text, 0);
// }
// xmlObjects[0].set(bufferrun.getCTR());
// }
// }
// }
Iterator<XWPFParagraph> itPara = doc.getParagraphsIterator();
while (itPara.hasNext()) {
XWPFParagraph paragraph = (XWPFParagraph) itPara.next();
List<String> placeholderList = Arrays.asList(PLACEHOLDER);
placeholderList.forEach(s->{
List<XWPFRun> runs=paragraph.getRuns();
//文字替换
for (int i = 0; i < runs.size(); i++) {
//获取字符
String text = runs.get(i).getText(runs.get(i).getTextPosition());
if (text == null){
XmlObject xmlObject = (XmlObject) runs.get(i).getCTR();
XmlObject[] xmlObjects = xmlObject.selectPath(XPATH_TEXTBOX_TXBXCONTENT2);
if (xmlObjects !=null && xmlObjects.length>=1){
CTR ctr = null;
try {
ctr = CTR.Factory.parse(xmlObjects[0].xmlText());
} catch (XmlException e) {
e.printStackTrace();
}
XWPFRun bufferrun = new XWPFRun(ctr, (IRunBody)paragraph);
String txtBoxVal = bufferrun.text();
System.out.println("txtBoxVal:"+txtBoxVal+",s:"+s);
if (txtBoxVal != null && txtBoxVal.contains(s)) {
txtBoxVal = txtBoxVal.replace(s ,getValue(dto, s));
bufferrun.setText(txtBoxVal, 0);
}
xmlObjects[0].set(bufferrun.getCTR());
}
}else if (text != null && text.contains("$")) {
//System.out.println("text:"+text);
//包含占位符的字符缓存
StringBuilder cache = new StringBuilder(text);
//记录run结束的角标,开始的角标为i
int endIndex = 0;
boolean contains = text.contains("}");
//同一个run中是否包含完成占位符
if (!contains) {
int j = i + 1 ;
for (; j < runs.size(); j++) {
String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
if (text1 == null) {
continue;
}
cache.append(text1);
if (text1.contains("}")) {
endIndex = j;
break;
}
}
}
if (contains || endIndex != 0) {
//处理替换
String key = cache.toString(); //这里是完整的占位符
System.out.println("key:"+key+",s:"+s);
if (key.contains(s)) {
if (key.contains(PICTURE_PLACEHOLDER[0])) { //替换图片
InputStream in = null;
String evaluationQrcode = dto.getImg();
System.out.println("=======:"+evaluationQrcode);
String type = evaluationQrcode.substring(evaluationQrcode.lastIndexOf(".") + 1); //二维码图片后缀
//创建Random类对象
try {
if (endIndex == 0) {
paragraph.removeRun(endIndex); //endIndex为0时,直接删掉run
}else {
for (int j = endIndex; j > i; j--) {
//角标移除后,runs会同步变动,直接继续处理i就可以
paragraph.removeRun(j);
}
}
//重新创建一个run,用来放二维码图片
XWPFRun run = paragraph.createRun();
in = new FileInputStream("C:\\pic.png");//设置图片路径
run.addPicture(in, Document.PICTURE_TYPE_PNG, "picTest",
Units.toEMU(WIDTH), Units.toEMU(HEIGHT)); //图片写入
break;
} catch (InvalidFormatException | IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}else {
System.out.println("执行文字替换");
//文字替换
/**
* 参数0表示生成的文字是要从哪一个地方开始放置,设置文字从位置0开始
* 就可以把原来的文字全部替换掉了
*/
runs.get(i).setColor("FF0000");
runs.get(i).setText(getValue(dto, s), 0);
for (int j = endIndex; j > i; j--) {
//角标移除后,runs会同步变动,直接继续处理i就可以
paragraph.removeRun(j);
}
break;
}
}
}
}
}
});
}
//fileNameList.add(fileName);
FileOutputStream outStream = new FileOutputStream("C:\\wqxiea.docx"); //单个文件保存路径
doc.write(outStream);
outStream.close();
}
// //合并word
// if (fileNameList.size() > 0) {
// String mainFileName = fileNameList.get(0);
// FileInputStream fis = new FileInputStream("D:/file/evaluation/word/"+mainFileName);
// XWPFDocument doc = new XWPFDocument(fis);
// for (int i = 1; i < fileNameList.size(); i++) { //循环合并
// FileInputStream fisTail = new FileInputStream("D:/file/evaluation/word/"+fileNameList.get(i));
// XWPFDocument docTail = new XWPFDocument(fisTail);
// doc = mergeWord(doc, docTail);
// }
// //TODO 以下写文件改为使用response下载文件
// FileOutputStream outStream = null;
// outStream = new FileOutputStream("D:/file/evaluation/word/合并文件.docx");
// doc.write(outStream);
// outStream.close();
// //TODO 删除fileNameList列表的文件
// }
}
private static String getValue(TicketProject3 dto, String s) {
if (s.equals("${company}")){
return dto.getCompany();
}else if (s.equals("${address}")){
return dto.getAddress();
}else if (s.equals("${secondParty}")){
return dto.getSecondParty();
}else if (s.equals("${a}")){
return dto.getA();
}else if (s.equals("${b}")){
return dto.getB();
}else if (s.equals("${c}")){
return dto.getC();
}
return "";
// if (s.equals("${evaluationProjectName}")) {
// return dto.getEvaluationProjectName();
// }else if (s.equals("${ticketName}")) {
// return dto.getTicketName();
// }else if (s.equals("${guide}")) {
// return dto.getGuide();
// }else if (s.equals("${attention}")) { //注意事项
// return dto.getAttention();
// }else if (s.equals("${evaluationCode}")) {
// return dto.getEvaluationCode();
// }else if (s.equals("${createdDate}")) {
// return dto.getCreatedDate();
// }else return null;
}
/**
* 根据图片类型,取得对应的图片类型代码
* @param picType
* @return int
*/
private static int getPictureType(String picType){
int res = Document.PICTURE_TYPE_PICT;
if(picType != null){
if(picType.equalsIgnoreCase("png")){
res = Document.PICTURE_TYPE_PNG;
}else if(picType.equalsIgnoreCase("dib")){
res = Document.PICTURE_TYPE_DIB;
}else if(picType.equalsIgnoreCase("emf")){
res = Document.PICTURE_TYPE_EMF;
}else if(picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")){
res = Document.PICTURE_TYPE_JPEG;
}else if(picType.equalsIgnoreCase("wmf")){
res = Document.PICTURE_TYPE_WMF;
}
}
return res;
}
/**
* 将两个word文档合并
* @param document
* @param doucDocument2
* @return 合并后的文档
*/
public static XWPFDocument mergeWord(XWPFDocument document,XWPFDocument doucDocument2) {
XWPFParagraph p = document.createParagraph();
//设置分页符
p.setPageBreak(true);
CTBody src1Body = document.getDocument().getBody();
CTBody src2Body = doucDocument2.getDocument().getBody();
// XWPFParagraph p2 = src2Document.createParagraph();
XmlOptions optionsOuter = new XmlOptions();
optionsOuter.setSaveOuter();
String appendString = src2Body.xmlText(optionsOuter);
String srcString = src1Body.xmlText();
String prefix = srcString.substring(0,srcString.indexOf(">")+1);
String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
String sufix = srcString.substring( srcString.lastIndexOf("<") );
String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
CTBody makeBody = null;
try {
makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
} catch (XmlException e) {
e.printStackTrace();
}
src1Body.set(makeBody);
return document;
}
}
package com.mis.webdemo.word3;
import lombok.Builder;
import lombok.Data;
@Builder
@Data
public class TicketProject3 {
private static final long serialVersionUID = 1L;
/**
*
*/
private String company;
private String address;
private String secondParty;
private String img;
private String a;
private String b;
private String c;
// /**
// * 序列码 VvjiN63o9m
// */
// private String evaluationCode;
// /**
// * 二维码文件地址 https://www.hjhrcloud.com/e/
// */
// private String evaluationQrcode;
// /**
// * 项目名称
// */
// private String evaluationProjectName;
// /**
// * 票种角色(领导班子、外部董事、中层)
// */
// private String ticketName;
// /**
// * 描述 请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分
// */
// private String describe;
//
// /**
// * 简介(一页一码使用:欢迎参加2023年×××民主测评。。。)
// */
// private String information;
// /**
// * 指导语
// */
// private String guide;
// /**
// * 注意事项
// */
// private String attention;
//
// /**
// * 日期
// */
// private String createdDate;
}
package com.mis.webdemo.word3;
import java.util.ArrayList;
import java.util.List;
public class Word3 {
public static void main(String[] args) {
try{
String line = System.getProperty("line.separator");
List<TicketProject3> entitys = new ArrayList<>();
entitys.add(TicketProject3
.builder()
.company("宁德时代")
.address("东湖片区")
.secondParty("2303")
.img("C:\\pic.png")
.a("a1")
.b("b1")
.c("c1")
.build());
// entitys.add(TicketProject
// .builder()
// .evaluationCode("VvjiN6678o")
// .evaluationProjectName("2023年×××民主测评")
// .ticketName("领导班子")
// .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
// .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
// .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2023年3月01日关闭;" +line+
// " 2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
// " 3.评价一经提交无法进行修改;" +line+
// " 4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
// .evaluationQrcode("D:/file/evaluation/1682057427848.jpg")
// .createdDate("2023年03月11日")
// .build());
//合并导出word文档
ExportUtils3.UniqueCodeWordExport(entitys, null);
}catch (Exception exception){
exception.printStackTrace();
}
}
}