package com.ots.project.tool.report.MAQTR.chart; import com.alibaba.fastjson.JSON; import com.ots.common.utils.StringUtils; import com.ots.project.tool.report.MAQ.base.RowData; import com.ots.project.tool.report.MAQ.base.SeriesData; import lombok.extern.slf4j.Slf4j; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ooxml.POIXMLDocument; import org.apache.poi.ooxml.POIXMLDocumentPart; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xwpf.usermodel.XWPFChart; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.openxmlformats.schemas.drawingml.x2006.chart.*; import java.io.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @description: MAQTR图表 带直线和数据标记散点图数据变更 * @author: zhijie * @create: 2022-06-16 22:01 **/ @Slf4j public class MAQTRChart { public static void main(String[] args) throws IOException { try { OutputStream os = new FileOutputStream("C:\\Users\\大头\\Desktop\\MAQ组别报告\\林_PAQ_IA_CN1.docx"); XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage("C:\\Users\\大头\\Desktop\\MAQ组别报告\\终稿【0607中文版】MAQ组别对比报告.docx")); String fileName = "C:\\Users\\大头\\Desktop\\MAQ组别报告\\MAQ组别导入数据模板.xlsx"; //变更图表数据 changeMAQTRChart(document,fileName); document.write(os); }catch (Exception e){ e.printStackTrace(); } } /** * 初始化报告数据 * @param fileName 数据源文件名 * @param index 模板sheet索引 * @return */ public static List initData(String fileName,Integer index){ List seriesDatas = new ArrayList<>(); try { XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(fileName)); XSSFSheet sheet1 = wb.getSheetAt(index); //数据处理 先获取表头长度 遍历表头行数、一一对应放进表头集合里 Map map = new HashMap<>(); XSSFRow row = sheet1.getRow(0); for (int i = 0; i < row.getLastCellNum(); i++) { //为空忽略 /*if(StringUtils.isEmpty(getName(row.getCell(i)))){ continue; }*/ SeriesData seriesData = new SeriesData(getName(row.getCell(i))); map.put(i,seriesData); //读取每一行每一列 for (int j = 1; j <= sheet1.getLastRowNum(); j++) { //每一行 XSSFRow row1 = sheet1.getRow(j); RowData rowData = new RowData(); if(seriesData.getValue() == null){ List rowDatas = new ArrayList<>(); //设置表头 rowData.setName(getName(row.getCell(i))); //根据类型判断赋值 switch (row1.getCell(i).getCellType()) { case STRING: rowData.setValueStr(row1.getCell(i).getStringCellValue()); break; case NUMERIC: rowData.setValue(row1.getCell(i).getNumericCellValue()); break; default: break; } rowDatas.add(rowData); seriesData.setValue(rowDatas); }else{ //设置表头 rowData.setName(getName(row.getCell(i))); //根据类型判断赋值 switch (row1.getCell(i).getCellType()) { case STRING: rowData.setValueStr(row1.getCell(i).getStringCellValue()); break; case NUMERIC: rowData.setValue(row1.getCell(i).getNumericCellValue()); break; default: break; } seriesData.getValue().add(rowData); } } seriesDatas.add(seriesData); } }catch (Exception e){ e.printStackTrace(); } return seriesDatas; } /** * 根据单元格类型取值 * @return 返回字符值 */ private static String getName(XSSFCell cell){ String name = null; switch (cell.getCellType()) { case STRING: name = cell.getStringCellValue(); break; case NUMERIC: name = cell.getNumericCellValue()+""; break; } return name; } /** * 替换MAQTR图表数据 * @param document 文档对象 * @param fileName 数据源excel */ public static void changeMAQTRChart(XWPFDocument document,String fileName) { if(StringUtils.isEmpty(fileName)){ return; } try { List relations = document.getRelations(); for (POIXMLDocumentPart part : relations) { if (part instanceof XWPFChart) { // 图表元素 XWPFChart chart = (XWPFChart) part; String partName = chart.getPackagePart().getPartName().getName(); Integer index = 0; if(partName.contains("chart1.xml")){ index = 0; }else if(partName.contains("chart2.xml")){ index = 1; } //获取模板数据 List seriesDatas = initData(fileName,index); //数据长度 int size = seriesDatas.size(); // 查看里面的图表数据,才能知道是什么图表 CTPlotArea plot = chart.getCTChart().getPlotArea(); // excel内置表格 XSSFWorkbook workbook = chart.getWorkbook(); XSSFSheet sheet = workbook.getSheetAt(0); //散点图 if (!plot.getScatterChartList().isEmpty()) { CTScatterChart scatterChart = plot.getScatterChartArray(0); //去除多余数据源 int remain = 8 - size; if(remain != 0){ for (int j = 1; j <= remain; j++) { int removeSize = scatterChart.getSerList().size() - j - 1; scatterChart.getSerList().remove(removeSize); //删除excel多余列 //deleteColumn(sheet,8 - remain); } } //刷新内置excel数据 refreshExcel(seriesDatas, sheet); workbook.write(chart.getPackagePart().getOutputStream()); //散点图数据源 XY坐标 for (int i = 0; i < scatterChart.getSerList().size(); i++) { //根据图表数据线获取对应数据源 数值是X轴 辅助Y轴 List scatterDatas = new ArrayList(); //最后一个数据源需拿取最后一列0000 作为x轴 if((i+1) == size-2){ scatterDatas.add(seriesDatas.get(size-1)); scatterDatas.add(seriesDatas.get(size-2)); }else{ scatterDatas.add(seriesDatas.get(i+1)); scatterDatas.add(seriesDatas.get(size-2)); } CTScatterSer ser = scatterChart.getSerList().get(i); updateScatterChart(scatterDatas, ser.getXVal(), ser.getYVal()); } } } } } catch (Exception e) { e.printStackTrace(); log.error("MAQTR替换word图表 散点图图形失败:{}",e); } } /** * 删除列 * @param sheet * @param columnToDelete */ public static void deleteColumn(XSSFSheet sheet, int columnToDelete) { for (int r = 0; r <= sheet.getLastRowNum(); r++) { XSSFRow row = sheet.getRow(r); for (int c = columnToDelete; c <= row.getLastCellNum(); c++) { XSSFCell cOld = row.getCell(c); if (cOld != null) { row.removeCell(cOld); } XSSFCell cNext = row.getCell(c + 1); if (cNext != null) { XSSFCell cNew = row.createCell(c, cNext.getCellType()); cloneCell(cNew, cNext); if (r == 0) { sheet.setColumnWidth(c, sheet.getColumnWidth(c + 1)); } } } } } /** * 右边列左移,样式值设置 * @param cNew * @param cOld */ private static void cloneCell(XSSFCell cNew, XSSFCell cOld) { cNew.setCellComment(cOld.getCellComment()); cNew.setCellStyle(cOld.getCellStyle()); if (CellType.BOOLEAN == cNew.getCellType()) { cNew.setCellValue(cOld.getBooleanCellValue()); } else if (CellType.NUMERIC == cNew.getCellType()) { cNew.setCellValue(cOld.getNumericCellValue()); } else if (CellType.STRING == cNew.getCellType()) { cNew.setCellValue(cOld.getStringCellValue()); } else if (CellType.ERROR == cNew.getCellType()) { cNew.setCellValue(cOld.getErrorCellValue()); } else if (CellType.FORMULA == cNew.getCellType()) { cNew.setCellValue(cOld.getCellFormula()); } } /** * 更新图表的关联 excel * * @param seriesDatas * @param sheet */ protected static void refreshExcel(List seriesDatas, XSSFSheet sheet) { XSSFRow title = sheet.getRow(0); //遍历数据数组进行excel赋值 for (int i = 0; i < seriesDatas.size(); i++) { SeriesData data = seriesDatas.get(i); if (data.name != null && !data.name.isEmpty()) { // 第一行标题 XSSFCell cell = title.getCell(i); if (cell == null) { cell = title.createCell(i + 1); } cell.setCellValue(data.name); } int size = data.value.size(); //遍历数据进行赋值 for (int j = 0; j < size; j++) { //从第二行开始赋值 XSSFRow row = sheet.getRow(j + 1); if (row == null) { row = sheet.createRow(j + 1); } RowData cellValu = data.value.get(j); //第一列开始赋值 XSSFCell cell = row.getCell(i); if (cell == null) { cell = row.createCell(i); } //优先使用字符串值 if(StringUtils.isNotEmpty(cellValu.valueStr)){ cell.setCellValue(cellValu.valueStr); }else{ cell.setCellValue(cellValu.value); } } //删除多余行数 int lastRowNum = sheet.getLastRowNum(); //log.error("lastRowNum:{},size:{}",lastRowNum,size); if (lastRowNum > size) { for (int idx = lastRowNum; idx > size; idx--) { sheet.removeRow(sheet.getRow(idx)); } } } } /** * 更新 散点图 缓存数据 * * @param seriesDatas 数据 * @param xDataSource X坐标数据缓存 * @param yDataSource Y坐标数据缓存 */ protected static void updateScatterChart(List seriesDatas, CTAxDataSource xDataSource, CTNumDataSource yDataSource) { //获取xy坐标数据条数 long xNumCnt = xDataSource.getNumRef().getNumCache().getPtCount().getVal(); long yNumCnt = yDataSource.getNumRef().getNumCache().getPtCount().getVal(); //获取XY最新数据 List xRowDatas = seriesDatas.get(0).getValue(); int xRowDataSize = xRowDatas.size(); List yRowDatas = seriesDatas.get(1).getValue(); int yRowDataSize = yRowDatas.size(); //更新X坐标缓存 for (int i = 0; i < xRowDatas.size(); i++) { RowData cellValu = xRowDatas.get(i); CTNumVal val = xNumCnt > i ? xDataSource.getNumRef().getNumCache().getPtArray(i) : xDataSource.getNumRef().getNumCache().addNewPt(); val.setIdx(i); val.setV(String.format("%.0f", cellValu.value)); } //更新Y坐标缓存 for (int i = 0; i < yRowDatas.size(); i++) { RowData cellValu = yRowDatas.get(i); CTNumVal val = yNumCnt > i ? yDataSource.getNumRef().getNumCache().getPtArray(i) : yDataSource.getNumRef().getNumCache().addNewPt(); val.setIdx(i); val.setV(String.format("%.0f", cellValu.value)); } // 更新对应excel的范围 xDataSource.getNumRef().setF( replaceRowEnd(xDataSource.getNumRef().getF(), xNumCnt, xRowDataSize)); yDataSource.getNumRef().setF( replaceRowEnd(yDataSource.getNumRef().getF(), yNumCnt, yRowDataSize)); // 删除多的 if (xNumCnt > xRowDataSize) { for (int idx = xRowDataSize; idx < xNumCnt; idx++) { xDataSource.getNumRef().getNumCache().removePt(xRowDataSize); } } if (yNumCnt > yRowDataSize) { for (int idx = yRowDataSize; idx < yNumCnt; idx++) { yDataSource.getNumRef().getNumCache().removePt(yRowDataSize); } } // 更新个数 xDataSource.getNumRef().getNumCache().getPtCount().setVal(xRowDataSize); // 更新个数 yDataSource.getNumRef().getNumCache().getPtCount().setVal(yRowDataSize); } /** * 替换 形如: Sheet1!$A$2:$A$4 的字符 * * @param range * @return */ public static String replaceRowEnd(String range, long oldSize, long newSize) { Pattern pattern = Pattern.compile("(:\\$[A-Z]+\\$)(\\d+)"); Matcher matcher = pattern.matcher(range); if (matcher.find()) { long old = Long.parseLong(matcher.group(2)); return range.replaceAll("(:\\$[A-Z]+\\$)(\\d+)", "$1" + Long.toString(old - oldSize + newSize)); } return range; } /** * 获取图表有哪些类型 * @param part */ public static void getChartType(POIXMLDocumentPart part){ XWPFChart chart = (XWPFChart) part; //根据属性第一列名称切换数据类型 CTChart ctChart = chart.getCTChart(); CTPlotArea plotArea = ctChart.getPlotArea(); if (!plotArea.getBarChartList().isEmpty()) { System.out.println("柱状图{"+plotArea.getBarChartList().size()+"}"); } if (!plotArea.getPieChartList().isEmpty()) { System.out.println("饼图{"+plotArea.getPieChartList().size()+"}"); } if (!plotArea.getLineChartList().isEmpty()) { System.out.println("线形图{"+plotArea.getLineChartList().size()+"}"); } if (!plotArea.getBar3DChartList().isEmpty()) { System.out.println("柱状图3D{"+plotArea.getBar3DChartList().size()+"}"); } if (!plotArea.getScatterChartList().isEmpty()) { System.out.println("散点图{"+plotArea.getScatterChartList().size()+"}"); } } }