package com.gkhy.safePlatform.specialWork.util; import com.gkhy.safePlatform.commons.enums.E; import com.gkhy.safePlatform.commons.exception.AusinessException; import com.gkhy.safePlatform.commons.exception.BusinessException; import io.micrometer.core.instrument.util.StringUtils; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.Units; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; import org.apache.poi.xwpf.usermodel.*; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.impl.xb.xmlschema.SpaceAttribute; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor; import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr; import org.springframework.core.io.ClassPathResource; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.math.BigInteger; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.*; public class WorkExportUtil { private String tempLocalPath; private XWPFDocument xwpfDocument = null; private FileInputStream inputStream = null; private OutputStream outputStream = null; // 宽度 private static final int WIDTH = 70; // 高度 private static final int HEIGHT = 70; // 左偏移量 private static final int LEFTOFFSET = 400; //上偏移量 private static final int TOPOFFSET = -10; public WorkExportUtil(){ } public WorkExportUtil(String tempLocalPath){ this.tempLocalPath = tempLocalPath; } /** * 设置模板路径 * @param tempLocalPath */ public void setTempLocalPath(String tempLocalPath) { this.tempLocalPath = tempLocalPath; } /** * 初始化 * @throws IOException */ public void init() throws IOException{ //inputStream = new FileInputStream(new File(this.tempLocalPath)); ClassPathResource classPathResource = new ClassPathResource(this.tempLocalPath); InputStream input = classPathResource.getInputStream(); xwpfDocument = new XWPFDocument(input); } /** * 导出方法 * @param map 文档中所有数据 * @param map 需替换的文本的数据入参 * @return * @throws Exception */ public boolean export(Map map) throws Exception{ //生成二维码 generateQRCode((String) map.get("text")); //填充基础数据 Map baseDataMap = this.insertBaseDataToTable((Map) map.get("baseData")); if(baseDataMap.get("approval") != 0){//模板存在审批层 if(null != map.get("approval")){//审批数据不为空 insertValue(baseDataMap.get("approval"),(List>) map.get("approval")); } } if(0 != baseDataMap.get("measure")){//措施 if(null != map.get("measure")){//审批数据不为空 insertValue(baseDataMap.get("measure"),(List>) map.get("measure")); } } if(0 != baseDataMap.get("analysis")){//分析 if(null != map.get("analysis")){//审批数据不为空 insertValue(baseDataMap.get("analysis"),(List>) map.get("analysis")); } } createDefaultFooter1((String) map.get("workCode")); return true; } //添加水印 本示例没有用到水印 如果想添加水印 可以在导出方法里调用此方法 public void createWaterMark(XWPFDocument doc){ XWPFHeaderFooterPolicy policy=doc.getHeaderFooterPolicy(); policy.createWatermark("新疆国泰新华化工有限责任公司"); } //生成页码 /** * 创建默认的页脚(该页脚主要只居中显示页码) * @return 返回文档帮助类对象,可用于方法链调用 * @throws XmlException * XML异常 * @throws IOException * IO异常 */ public void createDefaultFooter1(String workCode) { // TODO 设置页码起始值 CTP pageNo = CTP.Factory.newInstance(); XWPFParagraph footer = new XWPFParagraph(pageNo, xwpfDocument); CTPPr begin = pageNo.addNewPPr(); begin.addNewPStyle().setVal("STYLE_FOOTER"); begin.addNewJc().setVal(STJc.CENTER); XWPFRun run = footer.createRun(); run.addTab(); run = footer.createRun(); run.setText(workCode+" "); setXWPFRunStyle(run, "宋体", 10); run = footer.createRun(); run.setText("第"); setXWPFRunStyle(run, "宋体", 10); CTFldChar fldChar = run.getCTR().addNewFldChar(); fldChar.setFldCharType(STFldCharType.Enum.forString("begin")); run = footer.createRun(); CTText ctText = run.getCTR().addNewInstrText(); ctText.setStringValue("PAGE \\* MERGEFORMAT"); ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve")); setXWPFRunStyle(run, "宋体", 10); fldChar = run.getCTR().addNewFldChar(); fldChar.setFldCharType(STFldCharType.Enum.forString("end")); run = footer.createRun(); run.setText("页/共"); setXWPFRunStyle(run, "宋体", 10); run = footer.createRun(); fldChar = run.getCTR().addNewFldChar(); fldChar.setFldCharType(STFldCharType.Enum.forString("begin")); run = footer.createRun(); ctText = run.getCTR().addNewInstrText(); ctText.setStringValue("NUMPAGES \\* MERGEFORMAT "); ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve")); setXWPFRunStyle(run, "宋体", 10); fldChar = run.getCTR().addNewFldChar(); fldChar.setFldCharType(STFldCharType.Enum.forString("end")); run = footer.createRun(); run.setText("页"); setXWPFRunStyle(run, "宋体", 10); CTSectPr sectPr = xwpfDocument.getDocument().getBody().isSetSectPr() ? xwpfDocument.getDocument().getBody().getSectPr() : xwpfDocument.getDocument().getBody().addNewSectPr(); XWPFHeaderFooterPolicy policy = new XWPFHeaderFooterPolicy(xwpfDocument, sectPr); policy.createFooter(STHdrFtr.DEFAULT, new XWPFParagraph[] { footer }); } //生成页脚 public void createFooter(){ /* * 生成页脚段落 * 给段落设置宽度为占满一行 * */ CTSectPr sectPr = xwpfDocument.getDocument().getBody().addNewSectPr(); XWPFHeaderFooterPolicy headerFooterPolicy = new XWPFHeaderFooterPolicy(xwpfDocument, sectPr); XWPFFooter footer = headerFooterPolicy.createFooter(STHdrFtr.DEFAULT); XWPFParagraph paragraph = footer.getParagraphArray(0); paragraph.setAlignment(ParagraphAlignment.LEFT); paragraph.setVerticalAlignment(TextAlignment.CENTER); paragraph.setBorderTop(Borders.THICK); CTTabStop tabStop = paragraph.getCTP().getPPr().addNewTabs().addNewTab(); tabStop.setVal(STTabJc.RIGHT); int twipsPerInch = 1440; tabStop.setPos(BigInteger.valueOf(6 * twipsPerInch)); /* * 给段落创建元素 * 设置元素字面为公司地址+公司电话 * */ XWPFRun run = paragraph.createRun(); run.addTab(); String s = run.getFontFamily(); /* * 生成页码 * 页码右对齐 * */ run = paragraph.createRun(); run.setText("第"); run = paragraph.createRun(); CTFldChar fldChar = run.getCTR().addNewFldChar(); fldChar.setFldCharType(STFldCharType.Enum.forString("begin")); run = paragraph.createRun(); CTText ctText = run.getCTR().addNewInstrText(); ctText.setStringValue("PAGE \\* MERGEFORMAT"); ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve")); setXWPFRunStyle(run, "宋体", 8); fldChar = run.getCTR().addNewFldChar(); fldChar.setFldCharType(STFldCharType.Enum.forString("end")); run = paragraph.createRun(); run.setText("页/共"); setXWPFRunStyle(run, "宋体", 8); run = paragraph.createRun(); fldChar = run.getCTR().addNewFldChar(); fldChar.setFldCharType(STFldCharType.Enum.forString("begin")); run = paragraph.createRun(); ctText = run.getCTR().addNewInstrText(); ctText.setStringValue("NUMPAGES \\* MERGEFORMAT "); ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve")); setXWPFRunStyle(run, "宋体", 8); fldChar = run.getCTR().addNewFldChar(); fldChar.setFldCharType(STFldCharType.Enum.forString("end")); run = paragraph.createRun(); run.setText("页"); setXWPFRunStyle(run, "宋体", 8); } /** * 设置页脚的字体样式 * * @param r1 段落元素 */ private void setXWPFRunStyle(XWPFRun r1,String font,int fontSize) { r1.setFontSize(fontSize); CTRPr rpr = r1.getCTR().isSetRPr() ? r1.getCTR().getRPr() : r1.getCTR().addNewRPr(); CTFonts fonts = rpr.addNewRFonts(); fonts.setAscii(font); fonts.setEastAsia(font); fonts.setHAnsi(font); } /** * 替换非表格埋点值 * @param xwpfDocument * @param textMap 需要替换的文本入参 */ public void replaceText(XWPFDocument xwpfDocument,Map textMap){ List paras=xwpfDocument.getParagraphs(); Set keySet=textMap.keySet(); for (XWPFParagraph para : paras) { //当前段落的属性 System.out.println("打印获取到的段落的每一行数据++++++++>>>>>>>"+para.getText()); System.out.println("========================>>>>>>"+para.getParagraphText()); List list=para.getRuns(); for(XWPFRun run:list){ for(String key:keySet){ if(key.equals(run.text())){ if(run.text().equals("name")){ run.setText(textMap.get(key)+"证",0); }else{ run.setText(textMap.get(key),0); run.setFontSize(12); } } } } } } /** * 填充表格基础数据 * @param baseDataMap * @return */ private Map insertBaseDataToTable( Map baseDataMap){ //获取行数 List tables = xwpfDocument.getTables(); List rows = tables.get(0).getRows(); int rowNumber = 0; int analysisMode = 0; int measureMode = 0; int approvalMode = 0; //循环行 for(XWPFTableRow row : rows){ List tableCells = row.getTableCells(); //循环列 for(XWPFTableCell cell:tableCells){ String cellText = cell.getText(); //如果列不为空 if(StringUtils.isNotBlank(cellText)){ //字体 String fontFamily = cell.getParagraphs().get(0).getRuns().get(0).getFontFamily(); Double fontSizeDouble = cell.getParagraphs().get(0).getRuns().get(0).getFontSizeAsDouble(); //转换为mapkey对应的字段 String cellTextKey = cellText.replace("$", "").replace("{", "").replace("}", ""); if(null != baseDataMap){ if (baseDataMap.containsKey(cellTextKey)) { //清除数据 clearCell(cell); // 填充内容 cell.setText(baseDataMap.get(cellTextKey)); cell.getParagraphs().get(0).getRuns().get(0).setFontFamily(fontFamily); cell.getParagraphs().get(0).getRuns().get(0).setFontSize(fontSizeDouble); } } if(cellText.contains("anaUname")){//包含分析人 analysisMode = rowNumber; continue; } if(cellText.contains("mUname")){//措施 measureMode = rowNumber; continue; } if(cellText.contains("apprUname")){//审批层 approvalMode = rowNumber; continue; } } } rowNumber+=1; } Map rowMap = new HashMap<>(); rowMap.put("analysis",analysisMode); rowMap.put("measure",measureMode); rowMap.put("approval",approvalMode); return rowMap; } /** * 插入审批层数据 */ public void insertValue(int rowNo,List> approvalList) throws Exception { XWPFTable xwpfTable = xwpfDocument.getTables().get(0); List rows = xwpfTable.getRows(); if (rows.size() < rowNo+1) { throw new Exception("模板错误!"); } // //模板的那一行 XWPFTableRow modeRow = rows.get(rowNo); int newRowNo = rowNo + 1; for (int i = 0, len = approvalList.size(); i < len; i++) { Map map = approvalList.get(i); if(null != map){ // 创建新的一行 XWPFTableRow newRow = xwpfTable.insertNewTableRow(newRowNo++); setNewRowStyle(modeRow, newRow,map); } } // 删除模版行 xwpfTable.removeRow(rowNo); } //填充值并调整样式 public void setNewRowStyle(XWPFTableRow modeRow, XWPFTableRow newRow,Map map){ try{ //复制行的样式给新行 newRow.getCtRow().setTrPr(modeRow.getCtRow().getTrPr()); //获取要复制样式的行的单元格 List modeCells = modeRow.getTableCells(); //循环复制单元格 for (XWPFTableCell modeCell : modeCells) { //添加新列 XWPFTableCell newCell = newRow.addNewTableCell(); //模板列 String modeCellText = modeCell.getText(); if (StringUtils.isNotBlank(modeCellText)) { //转换为mapkey对应的字段 String cellTextKey = modeCellText.replace("$", "").replace("{", "").replace("}", ""); if (map.containsKey(cellTextKey)) { //字体 String fontFamily = modeCell.getParagraphs().get(0).getRuns().get(0).getFontFamily(); Double fontSizeDouble = modeCell.getParagraphs().get(0).getRuns().get(0).getFontSizeAsDouble(); // 填充内容 newCell.setText(map.get(cellTextKey)); newCell.getParagraphs().get(0).getRuns().get(0).setFontFamily(fontFamily); newCell.getParagraphs().get(0).getRuns().get(0).setFontSize(fontSizeDouble); } } //复制单元格的样式给新单元格 newCell.getCTTc().setTcPr(modeCell.getCTTc().getTcPr()); //设置垂直居中 newCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);//垂直居中 //复制单元格的居中方式给新单元格 CTPPr pPr = modeCell.getCTTc().getPList().get(0).getPPr(); if(pPr!=null&&pPr.getJc()!=null&&pPr.getJc().getVal()!=null){ CTTc cttc = newCell.getCTTc(); CTP ctp = cttc.getPList().get(0); CTPPr ctppr = ctp.getPPr(); if (ctppr == null) { ctppr = ctp.addNewPPr(); } CTJc ctjc = ctppr.getJc(); if (ctjc == null) { ctjc = ctppr.addNewJc(); } ctjc.setVal(pPr.getJc().getVal()); //水平居中 } //得到复制单元格的段落 List sourceParagraphs = modeCell.getParagraphs(); if (StringUtils.isEmpty(modeCell.getText())) { continue; } //拿到第一段 XWPFParagraph modeParagraph = sourceParagraphs.get(0); //得到新单元格的段落 List targetParagraphs = newCell.getParagraphs(); //判断新单元格是否为空 if (StringUtils.isEmpty(newCell.getText())) { //添加新的段落 XWPFParagraph ph = newCell.addParagraph(); //复制段落样式给新段落 ph.getCTP().setPPr(modeParagraph.getCTP().getPPr()); //得到文本对象 XWPFRun run = ph.getRuns().isEmpty() ? ph.createRun() : ph.getRuns().get(0); //复制文本样式 run.setFontFamily(modeParagraph.getRuns().get(0).getFontFamily()); } else { XWPFParagraph ph = targetParagraphs.get(0); ph.getCTP().setPPr(modeParagraph.getCTP().getPPr()); XWPFRun run = ph.getRuns().isEmpty() ? ph.createRun() : ph.getRuns().get(0); run.setFontFamily(modeParagraph.getRuns().get(0).getFontFamily()); } } }catch (Exception e){ throw new AusinessException(E.EXPORT_FAIL,"单元格格式复制失败!"); } } /** * 清除单元格数据 * @param cell */ public void clearCell(XWPFTableCell cell){ List paragraphs = cell.getParagraphs(); for(int j = 0; j < paragraphs.size(); j++){ cell.removeParagraph(j); } } /** * 收尾方法 * @param outDocPath * @return * @throws IOException */ public boolean generate(String outDocPath) throws IOException{ outputStream = new FileOutputStream(outDocPath); xwpfDocument.write(outputStream); this.close(outputStream); this.close(inputStream); return true; } /** * 返回浏览器端 导出word * @param response * @return * @throws IOException */ public boolean responseWord(HttpServletResponse response,String fileName,String workCode) throws IOException{ if(StringUtils.isNotBlank(fileName) && StringUtils.isNotBlank(workCode) ){ fileName = fileName + "-"+ workCode; }else { fileName = "特殊作业证"; } fileName = URLEncoder.encode(fileName,"UTF-8"); response.setContentType("application/x-msdownload;charset=UTF-8"); response.addHeader("Content-Disposition", "attachment;filename=" + fileName + ".docx"); xwpfDocument.write(response.getOutputStream()); this.close(inputStream); return true; } /** * 关闭输入流 * @param is */ private void close(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 关闭输出流 * @param os */ private void close(OutputStream os) { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 获取二维码字节流 */ public void generateQRCode(String text) throws IOException, InvalidFormatException { //生成二维码 ByteArrayOutputStream outputStream = QrCodeUtil.generate(text); //插入图片 XWPFParagraph paragraph = xwpfDocument.getParagraphArray(0); List runs = paragraph.getRuns(); for (XWPFRun run : runs) { String runText = run.text().trim(); if(runText.equals("${qrcode}")){ run.setText("",0); ByteArrayInputStream is = new ByteArrayInputStream(outputStream.toByteArray()); run.addPicture(is, XWPFDocument.PICTURE_TYPE_JPEG, "qrcode", Units.toEMU(40), Units.toEMU(40)); //浮于文字上方 CTDrawing drawing= run.getCTR().getDrawingArray(0); CTGraphicalObject go= drawing.getInlineArray(0).getGraphic(); Random random= new Random(); int number = random.nextInt(999) + 1; CTAnchor anchor1 = getAnchorWithGraphic(go, "Seal" +number, Units.toEMU(WIDTH), Units.toEMU(HEIGHT),//图片大小 Units.toEMU(LEFTOFFSET), Units.toEMU(TOPOFFSET), false); drawing.setAnchorArray(new CTAnchor[]{anchor1});//添加浮动属性 drawing.removeInline(0);//删除行内属性//添加签名图片 //关闭输出流 outputStream.close(); //关闭输入流 is.close(); } } } /** * 设置二维码样式 * @param ctGraphicalObject * @param deskFileName * @param width * @param height * @param leftOffset * @param topOffset * @param behind * @return */ public static CTAnchor getAnchorWithGraphic(CTGraphicalObject ctGraphicalObject, String deskFileName,int width, int height,int leftOffset, int topOffset, boolean behind) { String anchorXML = "" + "" + "" + "" + leftOffset + "" + "" + "" + "" + topOffset + "" + "" + "" + "" + "" + "" + ""; CTDrawing drawing= null; try{ drawing= CTDrawing.Factory.parse(anchorXML); }catch(XmlException e) { e.printStackTrace(); } assert drawing != null; CTAnchor anchor= drawing.getAnchorArray(0); anchor.setGraphic(ctGraphicalObject); return anchor; } /** * 测试 * @param args * @throws Exception */ public static void main(String[] args) throws Exception { // 添加假数据 这里你也可以从数据库里获取数据 List> approvalList = new ArrayList<>(); for (int i =0;i < 5; i++){ Map map = new HashMap<>(); map.put("apprDepName", "2018"+i); map.put("apprOpinion", "我是第一列数据"+i); map.put("apprUname", new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date())); map.put("apResult", "监理意见"+i); map.put("apprSign", "我是备注"+i); map.put("apprTime","时间"); approvalList.add(map); } // 添加假数据 这里你也可以从数据库里获取数据 List> measureList = new ArrayList<>(); for (int i =0;i < 10; i++){ Map map = new HashMap<>(); map.put("index", "2018"+i); map.put("measure", "我是第一列数据"+i); map.put("mResult", new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date())); map.put("mUname", "监理意见"+i); measureList.add(map); } List> analysisList = new ArrayList<>(); for (int i =0;i < 10; i++){ Map map = new HashMap<>(); map.put("anaAddress", "2018"+i); map.put("anaData", "我是第一列数据"+i); map.put("anaResult", new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date())); map.put("anaUname", "监理意见"+i); map.put("anaTime", "监理意见"+i); analysisList.add(map); } Map textMap=new HashMap<>(); textMap.put("name", "新疆国泰新华化工有限责任公司动火作业"); textMap.put("footer", "此票一式三联,一联生产车间留存,二联作业单位留存,三联生产技术科留存"); //基础数据 Map baseDataMap=new HashMap<>(); baseDataMap.put("applydepName","人事部"); baseDataMap.put("applyUname","张三"); baseDataMap.put("workCode","GK-000001"); baseDataMap.put("workType","动火作业"); baseDataMap.put("workMethod","电焊"); baseDataMap.put("addressAndContent","动火地点及内容"); baseDataMap.put("workTime","作业时间"); baseDataMap.put("operatorNames","作业人"); baseDataMap.put("otherSpeicialWork","其他特殊作业"); baseDataMap.put("harm","危害辨识"); Map dataMap = new HashMap<>(); dataMap.put("baseData",baseDataMap); dataMap.put("text","You a pig ! emmmmmm"); dataMap.put("approval",approvalList); dataMap.put("measure",measureList); dataMap.put("analysis",analysisList); // 模板文件输入输出地址 String filePath = "F:/test/workhot.docx"; String outPath = "F:/test/workhot示例文档.docx"; WorkExportUtil workExportUtil = new WorkExportUtil(); workExportUtil.setTempLocalPath(filePath); //设置模板的路径 workExportUtil.init(); //初始化工具类 workExportUtil.export(dataMap); //写入相关数据 workExportUtil.generate(outPath); //导出到目标文档 } }