本文参考地址:https://blog.csdn.net/wangxiaoyingWXY/article/details/95377533
在参考文章的基础上,增加了扩展。感谢被参考的妹子。另外该博客主要记录很多poi操作word中遇到的问题和解决方式,所以会一直维护下去。
代码直接从日记中复制过来的,不方便复制的可以私信发示例代码,不知道怎么上传附件了。另外,我是在本地使用的wps,使用office的老哥(老姐)们可能在效果上有出入。
本文只是简单示例,仅做代码参考,在实际运用中可自行根据业务封装,出现问题欢迎大家讨论学习。
还有就是博主真的很菜.....(手动滑稽)
关于示例代码:(码云地址)https://gitee.com/loverer/poi-demo,私信或者评论都可以
另外如果遇到模板标签不完整的情况,请参考:https://blog.csdn.net/weixin_43770790/article/details/86672962
说明:在poi3.x的版本中,没有对图表的支持,至少目前为止没有找到相应的文章介绍;在poi4.x的版本中加入了对图表的支持,所以需要引入poi4.x的依赖包,并且JDK最低版本要求8;
2019-12-04:新增:动态刷新内置Excel数据
2020-04-07:新增:更新插入的文本框内容(感谢博友:输过败过但不曾怕过啊)
2020-04-10:新增:新增一个定位图表的方式(感谢博友:输过败过但不曾怕过啊)
2020-04-11:新增:柱状图动态列,优化修改(兼容wps和office)(感谢:谈个长长的恋爱可好)
2020-04-21:新增:表格进行合并单元格,横竖都行,两种方式(感谢刘老师)
2020-04-23:解决问题:组合图页面显示的数据,跟第二列对应不上(感谢博友:吹古拉朽)
2020-06-06:解决问题:图表设置系列标题方式错误导致office无法打开的问题(感谢博友: qq_18900373)
// 设置标题
CTSerTx tx = ser.getTx();
tx.setV("阿里嘎痛"); // wps可以打开,office无法打开
tx.getStrRef().getStrCache().getPtList().get(0).setV("阿里嘎痛"); // wps和office都能打开
1.引入maven依赖包
org.apache.poi
poi
4.1.0
org.apache.poi
poi-ooxml
4.1.0
完整pom.xml内容
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.8.RELEASE
com.example
demo
0.0.1-SNAPSHOT
demo
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.apache.poi
poi
4.1.0
org.apache.poi
poi-ooxml
4.1.0
org.springframework.boot
spring-boot-maven-plugin
2.准备word模板文件,注意后缀是docx,模板中的图表可以有数据,但是最终会被代码替换,所以不重要
模板文件: 不知道怎么上传了,需要的私信吧,感谢;
3.两个类
类1:PoiDemoWordTable
package com.example.demo;
import com.example.demo.util.PoiWordTools;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.springframework.util.StringUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PoiDemoWordTable {
public static void main(String[] args) throws Exception {
final String returnurl = "D:\\youxi\\jx\\result.docx"; // 结果文件
final String templateurl = "D:\\youxi\\jx\\test.docx"; // 模板文件
InputStream is = new FileInputStream(new File(templateurl));
XWPFDocument doc = new XWPFDocument(is);
// 替换word模板数据
replaceAll(doc);
// 保存结果文件
try {
File file = new File(returnurl);
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(returnurl);
doc.write(fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 替换段落和表格中
*/
public static void replaceAll(XWPFDocument doc) throws InvalidFormatException, IOException {
doParagraphs(doc); // 处理段落文字数据,包括文字和表格、图片
doCharts(doc); // 处理图表数据,柱状图、折线图、饼图啊之类的
}
/**
* 处理段落文字
*
* @param doc
* @throws InvalidFormatException
* @throws FileNotFoundException
* @throws IOException
*/
public static void doParagraphs(XWPFDocument doc) throws InvalidFormatException, IOException {
// 文本数据
Map textMap = new HashMap();
textMap.put("var", "我是被替换的文本内容");
// 图片数据
Map imgMap = new HashMap();
imgMap.put("img", "D:/youxi/ddd.png");
/**----------------------------处理段落------------------------------------**/
List paragraphList = doc.getParagraphs();
if (paragraphList != null && paragraphList.size() > 0) {
for (XWPFParagraph paragraph : paragraphList) {
List runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null) {
// 替换文本信息
String tempText = text;
String key = tempText.replaceAll("\\{\\{", "").replaceAll("}}", "");
if (!StringUtils.isEmpty(textMap.get(key))) {
run.setText(textMap.get(key), 0);
}
// 替换图片内容 参考:https://blog.csdn.net/a909301740/article/details/84984445
String tempImgText = text;
String imgkey = tempImgText.replaceAll("\\{\\{@", "").replaceAll("}}", "");
if (!StringUtils.isEmpty(imgMap.get(imgkey))) {
String imgPath = imgMap.get(imgkey);
try {
run.setText("", 0);
run.addPicture(new FileInputStream(imgPath), Document.PICTURE_TYPE_PNG, "img.png", Units.toEMU(200), Units.toEMU(200));
} catch (Exception e) {
e.printStackTrace();
}
}
// 动态表格
if (text.contains("${table1}")) {
run.setText("", 0);
XmlCursor cursor = paragraph.getCTP().newCursor();
XWPFTable tableOne = doc.insertNewTbl(cursor);// ---这个是关键
// 设置表格宽度,第一行宽度就可以了,这个值的单位,目前我也还不清楚,还没来得及研究
tableOne.setWidth(8500);
// 表格第一行,对于每个列,必须使用createCell(),而不是getCell(),因为第一行嘛,肯定是属于创建的,没有create哪里来的get呢
XWPFTableRow tableOneRowOne = tableOne.getRow(0);//行
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "序号");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(英文)");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(中文)");
// 表格第二行
XWPFTableRow tableOneRowTwo = tableOne.createRow();//行
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "一行一列");
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(1), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "一行一列");
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(2), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "一行一列");
// ....... 可动态添加表格
}
}
}
}
}
}
/**
* 处理图表
*
* @param doc
* @throws FileNotFoundException
*/
public static void doCharts(XWPFDocument doc) throws FileNotFoundException {
/**----------------------------处理图表------------------------------------**/
// 数据准备
List titleArr = new ArrayList();// 标题
titleArr.add("title");
titleArr.add("金额");
List fldNameArr = new ArrayList();// 字段名
fldNameArr.add("item1");
fldNameArr.add("item2");
// 数据集合
List
类2:PoiWordTools
package com.example.demo.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTColor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHpsMeasure;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTParaRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVerticalJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;
/**
* poi生成word的工具类
* 针对于模板中的图表是静态的,也就是模板中的图表长什么样子不会根据数据而改变
*/
public class PoiWordTools {
private static final BigDecimal bd2 = new BigDecimal("2");
/**
* 调用替换柱状图数据
*/
public static void replaceBarCharts(POIXMLDocumentPart poixmlDocumentPart,
List titleArr, List fldNameArr, List> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
CTBarChart barChart = plotArea.getBarChartArray(0);
List BarSerList = barChart.getSerList(); // 获取柱状图单位
//刷新内置excel数据
new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据
refreshBarStrGraphContent(barChart, BarSerList, listItemsByType, fldNameArr, 1);
}
/**
* 调用替换折线图数据
*/
public static void replaceLineCharts(POIXMLDocumentPart poixmlDocumentPart,
List titleArr, List fldNameArr, List> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
CTLineChart lineChart = plotArea.getLineChartArray(0);
List lineSerList = lineChart.getSerList(); // 获取折线图单位
//刷新内置excel数据
new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据
new PoiWordTools().refreshLineStrGraphContent(lineChart, lineSerList, listItemsByType, fldNameArr, 1);
}
/**
* 调用替换饼图数据
*/
public static void replacePieCharts(POIXMLDocumentPart poixmlDocumentPart,
List titleArr, List fldNameArr, List> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
CTPieChart pieChart = plotArea.getPieChartArray(0);
List pieSerList = pieChart.getSerList(); // 获取饼图单位
//刷新内置excel数据
new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据
new PoiWordTools().refreshPieStrGraphContent(pieChart, pieSerList, listItemsByType, fldNameArr, 1);
}
/**
* 调用替换柱状图、折线图组合数据
*/
public static void replaceCombinationCharts(POIXMLDocumentPart poixmlDocumentPart,
List titleArr, List fldNameArr, List> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
CTBarChart barChart = plotArea.getBarChartArray(0);
List barSerList = barChart.getSerList(); // 获取柱状图单位
//刷新内置excel数据
new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据 数据中下标1开始的是柱状图数据,所以这个是1
refreshBarStrGraphContent(barChart, barSerList, listItemsByType, fldNameArr, 1);
CTLineChart lineChart = plotArea.getLineChartArray(0);
List lineSerList = lineChart.getSerList(); // 获取折线图单位
//刷新内置excel数据 有一个就可以了 有一个就可以了 有一个就可以了
//new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据 数据中下标3开始的是折线图的数据,所以这个是3
new PoiWordTools().refreshLineStrGraphContent(lineChart, lineSerList, listItemsByType, fldNameArr, 3);
}
/**
* 刷新折线图数据方法
*
* @param typeChart
* @param serList
* @param dataList
* @param fldNameArr
* @param position
* @return
*/
public static boolean refreshLineStrGraphContent(Object typeChart,
List> serList, List> dataList, List fldNameArr, int position) {
boolean result = true;
//更新数据区域
for (int i = 0; i < serList.size(); i++) {
//CTSerTx tx=null;
CTAxDataSource cat = null;
CTNumDataSource val = null;
CTLineSer ser = ((CTLineChart) typeChart).getSerArray(i);
//tx= ser.getTx();
// Category Axis Data
cat = ser.getCat();
// 获取图表的值
val = ser.getVal();
// strData.set
CTStrData strData = cat.getStrRef().getStrCache();
CTNumData numData = val.getNumRef().getNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
numData.getPtCount().setVal(idx);
strData.getPtCount().setVal(idx);
//赋值横坐标数据区域
String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
.formatAsString("Sheet1", false);
cat.getStrRef().setF(axisDataRange);
//数据区域
String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
.formatAsString("Sheet1", false);
val.getNumRef().setF(numDataRange);
// 设置系列生成方向
}
return result;
}
/**
* 刷新柱状图数据方法
*
* @param typeChart
* @param serList
* @param dataList
* @param fldNameArr
* @param position
* @return
*/
public static boolean refreshBarStrGraphContent(Object typeChart,
List> serList, List> dataList, List fldNameArr, int position) {
boolean result = true;
//更新数据区域
for (int i = 0; i < serList.size(); i++) {
// CTSerTx tx=null;
CTAxDataSource cat = null;
CTNumDataSource val = null;
CTBarSer ser = ((CTBarChart) typeChart).getSerArray(i);
// tx= ser.getTx();
// Category Axis Data
cat = ser.getCat();
// 获取图表的值
val = ser.getVal();
// strData.set
CTStrData strData = cat.getStrRef().getStrCache();
CTNumData numData = val.getNumRef().getNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
numData.getPtCount().setVal(idx);
strData.getPtCount().setVal(idx);
//赋值横坐标数据区域
String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
.formatAsString("Sheet1", true);
cat.getStrRef().setF(axisDataRange);
//数据区域
String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
.formatAsString("Sheet1", true);
val.getNumRef().setF(numDataRange);
}
return result;
}
/**
* 刷新饼图数据方法
*
* @param typeChart
* @param serList
* @param dataList
* @param fldNameArr
* @param position
* @return
*/
public static boolean refreshPieStrGraphContent(Object typeChart,
List> serList, List> dataList, List fldNameArr, int position) {
boolean result = true;
//更新数据区域
for (int i = 0; i < serList.size(); i++) {
//CTSerTx tx=null;
CTAxDataSource cat = null;
CTNumDataSource val = null;
CTPieSer ser = ((CTPieChart) typeChart).getSerArray(i);
//tx= ser.getTx();
// Category Axis Data
cat = ser.getCat();
// 获取图表的值
val = ser.getVal();
// strData.set
CTStrData strData = cat.getStrRef().getStrCache();
CTNumData numData = val.getNumRef().getNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
numData.getPtCount().setVal(idx);
strData.getPtCount().setVal(idx);
//赋值横坐标数据区域
String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
.formatAsString("Sheet1", true);
cat.getStrRef().setF(axisDataRange);
//数据区域
String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
.formatAsString("Sheet1", true);
val.getNumRef().setF(numDataRange);
}
return result;
}
/**
* 刷新内置excel数据
*
* @param chart
* @param dataList
* @param fldNameArr
* @param titleArr
* @return
*/
public static boolean refreshExcel(XWPFChart chart,
List> dataList, List fldNameArr, List titleArr) {
boolean result = true;
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
//根据数据创建excel第一行标题行
for (int i = 0; i < titleArr.size(); i++) {
if (sheet.getRow(0) == null) {
sheet.createRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));
} else {
sheet.getRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));
}
}
//遍历数据行
for (int i = 0; i < dataList.size(); i++) {
Map baseFormMap = dataList.get(i);//数据行
//fldNameArr字段属性
for (int j = 0; j < fldNameArr.size(); j++) {
if (sheet.getRow(i + 1) == null) {
if (j == 0) {
try {
sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)) == null ? "" : baseFormMap.get(fldNameArr.get(j)));
} catch (Exception e) {
if (baseFormMap.get(fldNameArr.get(j)) == null) {
sheet.createRow(i + 1).createCell(j).setCellValue("");
} else {
sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)));
}
}
}
} else {
BigDecimal b = new BigDecimal(baseFormMap.get(fldNameArr.get(j)));
double value = 0d;
if (b != null) {
value = b.doubleValue();
}
if (value == 0) {
sheet.getRow(i + 1).createCell(j);
} else {
sheet.getRow(i + 1).createCell(j).setCellValue(b.doubleValue());
}
}
}
}
// 更新嵌入的workbook
POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();
try {
wb.write(xlsOut);
xlsOut.close();
} catch (IOException e) {
e.printStackTrace();
result = false;
} finally {
if (wb != null) {
try {
wb.close();
} catch (IOException e) {
e.printStackTrace();
result = false;
}
}
}
return result;
}
/**
* 设置表格样式
*
* @param cell
* @param fontName
* @param fontSize
* @param fontBlod
* @param alignment
* @param vertical
* @param fontColor
* @param bgColor
* @param cellWidth
* @param content
*/
public static void setWordCellSelfStyle(XWPFTableCell cell, String fontName, String fontSize, int fontBlod,
String alignment, String vertical, String fontColor,
String bgColor, String cellWidth, String content) {
//poi对字体大小设置特殊,不支持小数,但对原word字体大小做了乘2处理
BigInteger bFontSize = new BigInteger("24");
if (fontSize != null && !fontSize.equals("")) {
//poi对字体大小设置特殊,不支持小数,但对原word字体大小做了乘2处理
BigDecimal fontSizeBD = new BigDecimal(fontSize);
fontSizeBD = bd2.multiply(fontSizeBD);
fontSizeBD = fontSizeBD.setScale(0, BigDecimal.ROUND_HALF_UP);//这里取整
bFontSize = new BigInteger(fontSizeBD.toString());// 字体大小
}
// 设置单元格宽度
cell.setWidth(cellWidth);
//=====获取单元格
CTTc tc = cell.getCTTc();
//====tcPr开始====》》》》
CTTcPr tcPr = tc.getTcPr();//获取单元格里的
if (tcPr == null) {//没有,创建
tcPr = tc.addNewTcPr();
}
// --vjc开始-->>
CTVerticalJc vjc = tcPr.getVAlign();//获取 的
if (vjc == null) {//没有 ,创建
vjc = tcPr.addNewVAlign();
}
//设置单元格对齐方式
vjc.setVal(vertical.equals("top") ? STVerticalJc.TOP : vertical.equals("bottom") ? STVerticalJc.BOTTOM : STVerticalJc.CENTER); //垂直对齐
CTShd shd = tcPr.getShd();//获取里的
if (shd == null) {//没有,创建
shd = tcPr.addNewShd();
}
// 设置背景颜色
shd.setFill(bgColor.substring(1));
//《《《《====tcPr结束====
//====p开始====》》》》
CTP p = tc.getPList().get(0);//获取单元格里的
//---ppr开始--->>>
CTPPr ppr = p.getPPr();//获取里的
if (ppr == null) {//没有,创建
ppr = p.addNewPPr();
}
// --jc开始-->>
CTJc jc = ppr.getJc();//获取里的
if (jc == null) {//没有 ,创建
jc = ppr.addNewJc();
}
//设置单元格对齐方式
jc.setVal(alignment.equals("left") ? STJc.LEFT : alignment.equals("right") ? STJc.RIGHT : STJc.CENTER); //水平对齐
// <<--jc结束--
// --pRpr开始-->>
CTParaRPr pRpr = ppr.getRPr(); //获取里的
if (pRpr == null) {//没有,创建
pRpr = ppr.addNewRPr();
}
CTFonts pfont = pRpr.getRFonts();//获取里的
if (pfont == null) {//没有,创建
pfont = pRpr.addNewRFonts();
}
//设置字体
pfont.setAscii(fontName);
pfont.setEastAsia(fontName);
pfont.setHAnsi(fontName);
CTOnOff pb = pRpr.getB();//获取里的
if (pb == null) {//没有 ,创建
pb = pRpr.addNewB();
}
//设置字体是否加粗
pb.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF);
CTHpsMeasure psz = pRpr.getSz();//获取里的
if (psz == null) {//没有 ,创建
psz = pRpr.addNewSz();
}
// 设置单元格字体大小
psz.setVal(bFontSize);
CTHpsMeasure pszCs = pRpr.getSzCs();//获取里的
if (pszCs == null) {//没有 ,创建
pszCs = pRpr.addNewSzCs();
}
// 设置单元格字体大小
pszCs.setVal(bFontSize);
// <<--pRpr结束--
//<<<---ppr结束---
//---r开始--->>>
List rlist = p.getRList(); //获取里的
CTR r = null;
if (rlist != null && rlist.size() > 0) {//获取第一个
r = rlist.get(0);
} else {//没有,创建
r = p.addNewR();
}
//--rpr开始-->>
CTRPr rpr = r.getRPr();//获取里的
if (rpr == null) {//没有,创建
rpr = r.addNewRPr();
}
//->-
CTFonts font = rpr.getRFonts();//获取里的
if (font == null) {//没有,创建
font = rpr.addNewRFonts();
}
//设置字体
font.setAscii(fontName);
font.setEastAsia(fontName);
font.setHAnsi(fontName);
CTOnOff b = rpr.getB();//获取里的
if (b == null) {//没有 ,创建
b = rpr.addNewB();
}
//设置字体是否加粗
b.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF);
CTColor color = rpr.getColor();//获取里的
if (color == null) {//没有,创建
color = rpr.addNewColor();
}
// 设置字体颜色
if (content.contains("↓")) {
color.setVal("43CD80");
} else if (content.contains("↑")) {
color.setVal("943634");
} else {
color.setVal(fontColor.substring(1));
}
CTHpsMeasure sz = rpr.getSz();
if (sz == null) {
sz = rpr.addNewSz();
}
sz.setVal(bFontSize);
CTHpsMeasure szCs = rpr.getSzCs();
if (szCs == null) {
szCs = rpr.addNewSz();
}
szCs.setVal(bFontSize);
//-<-
//<<--rpr结束--
List tlist = r.getTList();
CTText t = null;
if (tlist != null && tlist.size() > 0) {//获取第一个
t = tlist.get(0);
} else {//没有,创建
t = r.addNewT();
}
t.setStringValue(content);
//<<<---r结束---
}
/**
* 获取内置表格数据,拿到第一行第一列格子数据
* 有时候模板设计太复杂,对于图表不能精准定位,可以通过设置图表表格数据的第一行第一列格子数据来区分,这个数据不影响图表显示,所以用来区分每个图表
*/
public static String getZeroData(POIXMLDocumentPart poixmlDocumentPart){
String text = "";
try {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
InputStream xlsin = xlsPart.getPackagePart().getInputStream();
Workbook workbook = new XSSFWorkbook(xlsin);
// 获取第一个sheet
Sheet sheet = workbook.getSheetAt(0);
// 第一行
Row row = sheet.getRow(0);
// 第一列
Cell cell = row.getCell(0);
cell.setCellType(CellType.STRING); // 设置一下格子类型为字符串,不然如果是数字或者时间的话,获取很麻烦
text = cell.getStringCellValue(); // 获取格子内容
System.out.println("(0,0)格子值:" + text);
// 关闭流
xlsin.close();
} catch (Exception e) {
e.printStackTrace();
}
return text;
}
}
4.直接运行PoiDemoWordTable类中的main方法即可
该示例中,实现了:文本替换、动态添加表格、柱状图、折线图、饼图、柱状+折线组合图,后续有其它的示例,会继续在此基础上扩展。代码都很简单。
5.运行结果
有个问题(已解决):运行代码之后查看生成的word结果,发现可能部分图表显示数据和实际代码中设置的数据不一致,这种情况都是word模板引起的。
比方说在这个图中只显示了一条折线图,但是实际上代码数据中有3个折线图数据;这里我们右键该图表:点击编辑数据,然后会出来一个excel表格,这就是图表内置的数据,打开后我们可以看到一个蓝色的框框:,这个蓝色框框包含的数据,图表才会显示,这里是图表真正的数据源;对于这种情况,需要手动的把所有数据被蓝色框框包住才行,直接鼠标托就行。
对于这个问题,word应该是有办法解决的,也就是图表的数据源能够动态更新,这个有需要的童鞋可以研究下。
2019-12-04:新增:动态刷新内置Excel数据
1:在原有模板的基础上,新加内置Excel表格文件,如图所示:图片看得清哈......
说明:尝试过使用xlsx后缀的文件,但是代码报错了,报错大概意思是:你确定这是个excel文件?
所以改成xls就好了,至于xlsx为什么不行,下来继续琢磨;
2:一个类代码
该类是在原有的demo代码里面写的,只有一个类,和之前的不冲突,但是用的word模板是同一个;
看着不明白的地方,多瞅瞅,欢迎提出,我也是看的官网来着;
/**
* 代码参考了poi官网文档的api示例
* poi官方api:http://poi.apache.org/components/document/quick-guide-xwpf.html
* 官方示例源码:http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xwpf/usermodel/examples/UpdateEmbeddedDoc.java
*/
package com.example.demo;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 更新模板word中的内置Excel数据
* 代码参考了poi官网文档的api示例
* poi官方api:http://poi.apache.org/components/document/quick-guide-xwpf.html
* 官方示例源码:http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xwpf/usermodel/examples/UpdateEmbeddedDoc.java
*/
public class UpdateEmbeddedDoc {
private static final String templateurl = "D:\\youxi\\jx\\test.docx"; // 模板文件
private static final String returnurl = "D:\\youxi\\jx\\result.docx"; // 结果文件
private XWPFDocument doc; // 模板文档对象
private File docFile;
private static final int SHEET_NUM = 0; // xls中的sheet页数
private static List> dataList = new ArrayList>(); // 模拟数据
static {
// 模拟数据
for (int i = 0; i < 10; i++) {
Map map = new HashMap();
map.put("index-0", "老王吧" + i);
map.put("index-1", "老李吧" + i);
map.put("index-2", "老张吧" + i);
map.put("index-3", "老刘吧" + i);
map.put("index-4", "老杨吧" + i);
map.put("index-5", "老乌龟吧" + i);
dataList.add(map);
}
}
public static void main(String[] args) throws Exception {
UpdateEmbeddedDoc ued = new UpdateEmbeddedDoc(templateurl);
ued.updateEmbeddedDoc();
}
/**
* 构造函数,加载模板文档
* @param filename
* @throws IOException
*/
public UpdateEmbeddedDoc(String filename) throws IOException {
this.docFile = new File(filename);
if (!this.docFile.exists()) {
throw new FileNotFoundException("The Word document " + filename + " does not exist.");
}
try (FileInputStream fis = new FileInputStream(this.docFile)) {
// Open the Word document file and instantiate the XWPFDocument class.
this.doc = new XWPFDocument(fis);
}
}
/**
* 更新word文档中内置的Excel工作簿
* Excel是事先已知的,并且在模板word中已经插入成功了的
* @param filename
* @throws IOException
*/
public void updateEmbeddedDoc() throws Exception {
// 获取模板文档中的内置部分,也就是获取到插入的Excel文档集合
List embeddedDocs = this.doc.getAllEmbeddedParts();
for (PackagePart pPart : embeddedDocs) {
// 这里打印出来文档中所有的内置对象,可以看到每个内置对象都有一个唯一标识,通过这个标识可以做相应的业务逻辑;
// 这里打印的oleFlag可能因为机器环境原因有所不同,建议先打印出oleFlag的值,对比后再做if判断;
// 反正这里输出的内容看一下,就明白了;
String oleFlag = pPart.toString();
System.out.println(oleFlag);
// 记得if中的条件“oleObject1”要和上面的oleFlag对比一下,能够唯一标识就好,我不敢保证每台机器的值都是“oleObject1”
// 第一个内置对象,
if (oleFlag.indexOf("oleObject1") > 0) {
doXls1(pPart);
} else if (oleFlag.indexOf("oleObject2") > 0) {
doXls2(pPart); // 第二个内置对象,
} else if (oleFlag.indexOf("oleObject3") > 0) {
doXls3(pPart); // 第三个内置对象,
}
// TODO.......
}
if (!embeddedDocs.isEmpty()) {
// Finally, write the newly modified Word document out to file.
try (FileOutputStream fos = new FileOutputStream(returnurl)) {
File file = new File(returnurl);
if (file.exists()) {
file.delete();
}
this.doc.write(fos);
fos.close();
}
}
}
/**
* 第一个内置对象,数据的话自己造吧,这里我懒,用了一个数据
* @param pPart
* @throws Exception
*/
public void doXls1(PackagePart pPart) throws Exception {
try (InputStream is = pPart.getInputStream();
Workbook workbook = WorkbookFactory.create(is);
OutputStream os = pPart.getOutputStream()) {
Sheet sheet = workbook.getSheetAt(SHEET_NUM);
int ROW_NUM = 1;
// 设置内置xlsx文件的数据,这里示例代码写的很傻瓜,看明白就好了;
for (int i = 0; i < dataList.size(); i++) {
Row row = sheet.createRow(ROW_NUM); // 创建行
Map colMap = dataList.get(i);
Cell cell0 = row.createCell(0);
cell0.setCellValue(colMap.get("index-0")); // 第0列数据
Cell cell1 = row.createCell(1);
cell1.setCellValue(colMap.get("index-1")); // 第1列数据
Cell cell2 = row.createCell(2);
cell2.setCellValue(colMap.get("index-2")); // 第2列数据
Cell cell3 = row.createCell(3);
cell3.setCellValue(colMap.get("index-3")); // 第3列数据
Cell cell4 = row.createCell(4);
cell4.setCellValue(colMap.get("index-4")); // 第4列数据
Cell cell5 = row.createCell(5);
cell5.setCellValue(colMap.get("index-5")); // 第5列数据
ROW_NUM++;
}
workbook.write(os);
}
}
/**
* 第二个内置对象,数据的话自己造吧,这里我懒,用了一个数据
* @param pPart
* @throws Exception
*/
public void doXls2(PackagePart pPart) throws Exception {
try (InputStream is = pPart.getInputStream();
Workbook workbook = WorkbookFactory.create(is);
OutputStream os = pPart.getOutputStream()) {
Sheet sheet = workbook.getSheetAt(SHEET_NUM);
int ROW_NUM = 1;
// 设置内置xlsx文件的数据,这里示例代码写的很傻瓜,看明白就好了;
for (int i = 0; i < dataList.size(); i++) {
Row row = sheet.createRow(ROW_NUM); // 创建行
Map colMap = dataList.get(i);
Cell cell0 = row.createCell(0);
cell0.setCellValue(colMap.get("index-0")); // 第0列数据
Cell cell1 = row.createCell(1);
cell1.setCellValue(colMap.get("index-1")); // 第1列数据
Cell cell2 = row.createCell(2);
cell2.setCellValue(colMap.get("index-2")); // 第2列数据
Cell cell3 = row.createCell(3);
cell3.setCellValue(colMap.get("index-3")); // 第3列数据
Cell cell4 = row.createCell(4);
cell4.setCellValue(colMap.get("index-4")); // 第4列数据
Cell cell5 = row.createCell(5);
cell5.setCellValue(colMap.get("index-5")); // 第5列数据
ROW_NUM++;
}
workbook.write(os);
}
}
/**
* 第三个内置对象,数据的话自己造吧,这里我懒,用了一个数据
* @param pPart
* @throws Exception
*/
public void doXls3(PackagePart pPart) throws Exception {
try (InputStream is = pPart.getInputStream();
Workbook workbook = WorkbookFactory.create(is);
OutputStream os = pPart.getOutputStream()) {
Sheet sheet = workbook.getSheetAt(SHEET_NUM);
int ROW_NUM = 1;
// 设置内置xlsx文件的数据,这里示例代码写的很傻瓜,看明白就好了;
for (int i = 0; i < dataList.size(); i++) {
Row row = sheet.createRow(ROW_NUM); // 创建行
Map colMap = dataList.get(i);
Cell cell0 = row.createCell(0);
cell0.setCellValue(colMap.get("index-0")); // 第0列数据
Cell cell1 = row.createCell(1);
cell1.setCellValue(colMap.get("index-1")); // 第1列数据
Cell cell2 = row.createCell(2);
cell2.setCellValue(colMap.get("index-2")); // 第2列数据
Cell cell3 = row.createCell(3);
cell3.setCellValue(colMap.get("index-3")); // 第3列数据
Cell cell4 = row.createCell(4);
cell4.setCellValue(colMap.get("index-4")); // 第4列数据
Cell cell5 = row.createCell(5);
cell5.setCellValue(colMap.get("index-5")); // 第5列数据
ROW_NUM++;
}
workbook.write(os);
}
}
}
生成结果:双击结果文档中的内置excel表格,结果如图
2020-04-07:新增:更新插入的文本框内容
1:在博友的帮助下,添加了对文本框内容的替换,方式很简单,模板截图如下:随意位置插入文本框即可
2:主要类:
该方法参考地址:https://blog.csdn.net/yousun4688/article/details/90108335(感谢老哥)
package com.example.demo.text;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 对文本框的替换,对文本框的替换,不是正常输入的文本
*/
public class TextBox {
public static List patternList = new ArrayList();
//需要处理的节点名称
static {
patternList.add("mc:AlternateContent");
patternList.add("mc:Choice");
patternList.add("w:drawing");
patternList.add("wp:anchor");
patternList.add("a:graphic");
patternList.add("a:graphicData");
patternList.add("wps:wsp");
patternList.add("wps:txbx");
patternList.add("w:txbxContent");
patternList.add("w:p");
patternList.add("w:r");
patternList.add("w:t");
}
public static void main(String[] args) throws Exception {
final String returnurl = "D:\\youxi\\jx\\textbox.docx"; // 结果文件
final String templateurl = "D:\\直真科技工作相关\\demo\\src\\main\\resources\\textbox.docx"; // 模板文件
InputStream is = new FileInputStream(new File(templateurl));
XWPFDocument doc = new XWPFDocument(is);
// 替换word模板数据
replaceAll(doc);
// 保存结果文件
try {
File file = new File(returnurl);
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(returnurl);
doc.write(fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 替换段落和表格中
*/
public static void replaceAll(XWPFDocument doc) throws Exception {
doParagraphs(doc); // 处理段落文字数据,包括文字和表格、图片
}
/**
* 处理段落文字
*
* @param doc
* @throws InvalidFormatException
* @throws FileNotFoundException
* @throws IOException
*/
public static void doParagraphs(XWPFDocument doc) throws Exception {
// 文本数据
Map textMap = new HashMap();
textMap.put("texttext", "我是被替换的普通文本内容");
textMap.put("name", "我是被替换的文本框内容");
textMap.put("zuoyou", "左右");
changeTextBox(doc, textMap);
}
public static void changeTextBox(XWPFDocument document, Map map) {
for (XWPFParagraph paragraph : document.getParagraphs())
for (XmlObject object : paragraph.getCTP().getRArray()) {
XmlCursor cursor = object.newCursor();
eachchild(cursor, 0, map);
}
}
public static void eachchild(XmlCursor cursor, int start, Map map) {
for (int i = 0; i < 10; i++) {
if (cursor.toChild(i)) {
if (cursor.getDomNode().getNodeName().equals(patternList.get(start))) {
if (start == patternList.size() - 1) {
String reString = cursor.getTextValue();
System.out.println(reString);
reString = reString.replaceAll("\\{\\{", "").replaceAll("}}", "");
for (String e : map.keySet()) {
if (reString.equals(e)) {
// 执行替换
reString = reString.replaceAll(e, map.get(e));
}
}
cursor.setTextValue(reString);
}
eachchild(cursor, start + 1, map);
} else {
cursor.toParent();
}
}
}
cursor.toParent();
}
}
3:替换后的效果,这个方法对普通文本不会生效,只会对文本框有效,并且替换的逻辑可以自己控制
2020-04-10:新增:新增一个定位图表的方式
问题描述:
在使用word模板中,如果存在很多个图表,如何获取到正确的图表,按照我现有的方式是如图这样。
通过把图表对象toString后获得“/word/charts/chart1.xml”这样的key值,这种方式对于图表在word模板中的位置是从上到下的很好用。
但是如果现在word模板中的图表位置很乱,位置是随意插入的,有横着排,也有竖着排,布局很乱的情况,这种方式就不好用了。
1:解决方案
对图表编辑数据的表格进行了观察,发现第一行第一列数据,无论怎样设置都不会推图表显示有任何影响,如下图所示的这个格子数据。所以在word模板中,修改每个图表的这个格子数据,保证全局唯一,相当于图表的唯一标识。然后在读取word模板时,读取一下图表的数据,获取第一行第一列格子的值,用来当作key。
2:修改模板
右键图表,选择编辑数据,出来的表格,修改第一行第一列的数据,要求全局唯一不重复。然后保存。
3:新增处理方法
该方法拿到POIXMLDocumentPart 对象,拿到Workbook对象,返回的text就是第一行第一列的数据。
/**
* 获取内置表格数据,拿到第一行第一列格子数据
* 有时候模板设计太复杂,对于图表不能精准定位,可以通过设置图表表格数据的第一行第一列格子数据来区分,这个数据不影响图表显示,所以用来区分每个图表
*/
public static String getZeroData(POIXMLDocumentPart poixmlDocumentPart){
String text = "";
try {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
InputStream xlsin = xlsPart.getPackagePart().getInputStream();
Workbook workbook = new XSSFWorkbook(xlsin);
// 获取第一个sheet
Sheet sheet = workbook.getSheetAt(0);
// 第一行
Row row = sheet.getRow(0);
// 第一列
Cell cell = row.getCell(0);
cell.setCellType(CellType.STRING); // 设置一下格子类型为字符串,不然如果是数字或者时间的话,获取很麻烦
text = cell.getStringCellValue(); // 获取格子内容
System.out.println("(0,0)格子值:" + text);
// 关闭流
xlsin.close();
} catch (Exception e) {
e.printStackTrace();
}
return text;
}
4:设置key
5:通过key获取图表对象
6:代码对应类:com.example.demo.example.barchart.BarChart
2020-04-11:新增:柱状图动态列,解决需要对图表编辑数据才能有效显示的问题
1:主类
package com.example.demo.example.barchart;
import com.example.demo.util.PoiWordTools;
import com.example.demo.util.PoiWordToolsDynamic;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 动态的柱状图,也就是列不确定,由数据决定
* 示例代码仅提供实现的参考,可根据自己的业务修改逻辑
*/
public class BarChartDynamic {
public static void main(String[] args) throws Exception {
final String returnurl = "D:\\youxi\\jx\\barchartdynamicresult.docx"; // 结果文件
final String templateurl = "D:\\直真科技工作相关\\demo\\src\\main\\resources\\barchartdynamic.docx"; // 模板文件
InputStream is = new FileInputStream(new File(templateurl));
XWPFDocument doc = new XWPFDocument(is);
// 替换word模板数据
replaceAll(doc);
// 保存结果文件
try {
File file = new File(returnurl);
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(returnurl);
doc.write(fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 替换段落和表格和图表等内容
*/
public static void replaceAll(XWPFDocument doc) throws Exception {
doCharts(doc); // 处理图表数据,柱状图
}
/**
* 处理图表
*
* @param doc
* @throws FileNotFoundException
*/
public static void doCharts(XWPFDocument doc) throws FileNotFoundException {
/**----------------------------处理图表------------------------------------**/
// 获取word模板中的所有图表元素,用map存放
// 为什么不用list保存:查看doc.getRelations()的源码可知,源码中使用了hashMap读取文档图表元素,
// 对relations变量进行打印后发现,图表顺序和文档中的顺序不一致,也就是说relations的图表顺序不是文档中从上到下的顺序
Map chartsMap = new HashMap();
//动态刷新图表
List relations = doc.getRelations();
for (POIXMLDocumentPart poixmlDocumentPart : relations) {
if (poixmlDocumentPart instanceof XWPFChart) { // 如果是图表元素
// 获取图表对应的表格数据里面的第一行第一列数据,可以拿来当作key值
String key = new PoiWordTools().getZeroData(poixmlDocumentPart).trim();
System.out.println("key:" + key);
chartsMap.put(key, poixmlDocumentPart);
}
}
System.out.println("\n图表数量:" + chartsMap.size() + "\n");
// 第一个图表-柱状图
doCharts1(chartsMap);
}
/**
* 封装图表数据
* @param chartsMap
*/
public static void doCharts1(Map chartsMap) {
// 数据准备
List titleArr = new ArrayList();// 标题,也就是对图表选择编辑数据后显示的表格数据的第一行
titleArr.add("");
titleArr.add("存款$");
titleArr.add("欠款$");
titleArr.add("还款$");
List fldNameArr = new ArrayList();// 字段名(数据有多少列,就多少个)
fldNameArr.add("item1");
fldNameArr.add("item2");
fldNameArr.add("item3");
fldNameArr.add("item4");
// 数据集合
List> listItemsByType = new ArrayList>();
// 数据的话随便整都行
// 第一行数据
Map base1 = new HashMap();
base1.put("item1", "2020-05");
base1.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
base1.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
base1.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
// 第二行数据
Map base2 = new HashMap();
base2.put("item1", "2020-06");
base2.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
base2.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
base2.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
// 第三行数据
Map base3 = new HashMap();
base3.put("item1", "2020-07");
base3.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
base3.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
base3.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + "");
listItemsByType.add(base1);
listItemsByType.add(base2);
listItemsByType.add(base3);
// 注意这里的key值
POIXMLDocumentPart poixmlDocumentPart = chartsMap.get("嘻嘻嘻嘻"); // 图表对象
new PoiWordToolsDynamic().replaceBarCharts(poixmlDocumentPart, titleArr, fldNameArr, listItemsByType);
}
}
2:辅助类1
package com.example.demo.util;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
/**
* poi生成word的工具类
* 可以动态生成图表,可以自己设置图表列的数量
*/
public class PoiWordToolsDynamic {
private static final BigDecimal bd2 = new BigDecimal("2");
/**
* 调用替换柱状图数据-可以实现动态列
* 其它的折线图、饼图大同小异
*/
public static void replaceBarCharts(POIXMLDocumentPart poixmlDocumentPart,
List titleArr,
List fldNameArr,
List> listItemsByType) {
// 很重要的参数,图表系列的数量,由这里的数据决定
int culomnNum = titleArr.size() - 1;
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
// 柱状图用plotArea.getBarChartArray,
// 折线图用plotArea.getLineChartArray,
// 饼图用plotArea.getPieChartArray.....
// 还有很多其它的
CTBarChart barChart = plotArea.getBarChartArray(0);
// 清除图表的样式,由代码自己设置
barChart.getSerList().clear();
//刷新内置excel数据
new PoiWordToolsDynamic().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据-也就是刷新图表的数据源范围
refreshBarStrGraphContent(barChart, listItemsByType, fldNameArr, 1, culomnNum, titleArr);
}
/**
* 调用替换柱状图数据-可以实现动态列
* 其它的折线图、饼图大同小异
*/
public static void replaceLineCharts(POIXMLDocumentPart poixmlDocumentPart,
List titleArr,
List fldNameArr,
List> listItemsByType) {
// 很重要的参数,图表系列的数量,由这里的数据决定
int culomnNum = titleArr.size() - 1;
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
// 柱状图用plotArea.getBarChartArray,
// 折线图用plotArea.getLineChartArray,
// 饼图用plotArea.getPieChartArray.....
// 还有很多其它的
CTLineChart ctLineChart = plotArea.getLineChartArray(0);
// 清除图表的样式,由代码自己设置
ctLineChart.getSerList().clear();
//刷新内置excel数据
new PoiWordToolsDynamic().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据-也就是刷新图表的数据源范围
refreshLineStrGraphContent(ctLineChart, listItemsByType, fldNameArr, 1, culomnNum, titleArr);
}
/**
* 动态添加列-柱状图的
*/
public static boolean refreshBarStrGraphContent(CTBarChart barChart,
List> dataList,
List fldNameArr,
int position,
int culomnNum,
List titleArr) {
boolean result = true;
//更新数据区域
for (int i = 0; i < culomnNum; i++) {
CTBarSer ctBarSer = barChart.addNewSer();
ctBarSer.addNewIdx().setVal(i);
ctBarSer.addNewOrder().setVal(i);
// 设置柱状图的系列名称
// 设置标题 用以下这个方式,可以兼容office和wps(因为是动态添加,不可以直接get到,需要这样写)
CTSerTx tx = ctBarSer.addNewTx();
CTStrRef ctStrRef = tx.addNewStrRef();
CTStrData ctStrData = ctStrRef.addNewStrCache();
ctStrData.addNewPtCount().setVal(1);
CTStrVal ctStrVal = ctStrData.addNewPt();
ctStrVal.setIdx(0);
ctStrVal.setV(titleArr.get(i + 1)); // 设置系列的名称
// 设置柱状图系列的颜色,就是显示的柱子的颜色,不设置的话会默认都是黄色
// 必须使用ACCENT_x系列的才行
CTSchemeColor ctSchemeColor = ctBarSer.addNewSpPr().addNewSolidFill().addNewSchemeClr();
ctSchemeColor.setVal(STSchemeColorValTools.get(i));
CTAxDataSource cat = ctBarSer.addNewCat();
CTNumDataSource val = ctBarSer.addNewVal();
CTStrData strData = cat.addNewStrRef().addNewStrCache();
CTNumData numData = val.addNewNumRef().addNewNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
numData.addNewPtCount().setVal(idx);
strData.addNewPtCount().setVal(idx);
}
return result;
}
/**
* 动态添加列-折线图的
*/
public static boolean refreshLineStrGraphContent(CTLineChart ctLineChart,
List> dataList,
List fldNameArr,
int position,
int culomnNum,
List titleArr) {
boolean result = true;
//更新数据区域
for (int i = 0; i < culomnNum; i++) {
CTLineSer ctLineSer = ctLineChart.addNewSer();
ctLineSer.addNewIdx().setVal(i);
ctLineSer.addNewOrder().setVal(i);
// 设置柱状图的系列名称
// 设置标题 用以下这个方式,可以兼容office和wps(因为是动态添加,不可以直接get到,需要这样写)
CTSerTx tx = ctLineSer.addNewTx();
CTStrRef ctStrRef = tx.addNewStrRef();
CTStrData ctStrData = ctStrRef.addNewStrCache();
ctStrData.addNewPtCount().setVal(1);
CTStrVal ctStrVal = ctStrData.addNewPt();
ctStrVal.setIdx(0);
ctStrVal.setV(titleArr.get(i + 1)); // 设置系列的名称
// 设置柱状图系列的颜色,就是显示的柱子的颜色,不设置的话会默认都是黄色
// 必须使用ACCENT_x系列的才行
CTSchemeColor ctSchemeColor = ctLineSer.addNewSpPr().addNewLn().addNewSolidFill().addNewSchemeClr();
ctSchemeColor.setVal(STSchemeColorValTools.get(i));
CTAxDataSource cat = ctLineSer.addNewCat();
CTNumDataSource val = ctLineSer.addNewVal();
CTStrData strData = cat.addNewStrRef().addNewStrCache();
CTNumData numData = val.addNewNumRef().addNewNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
numData.addNewPtCount().setVal(idx);
strData.addNewPtCount().setVal(idx);
}
return result;
}
/**
* 刷新内置excel数据
*
* @param chart
* @param dataList
* @param fldNameArr
* @param titleArr
* @return
*/
public static boolean refreshExcel(XWPFChart chart,
List> dataList, List fldNameArr, List titleArr) {
boolean result = true;
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
//根据数据创建excel第一行标题行
for (int i = 0; i < titleArr.size(); i++) {
if (sheet.getRow(0) == null) {
sheet.createRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));
} else {
sheet.getRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));
}
}
//遍历数据行
for (int i = 0; i < dataList.size(); i++) {
Map baseFormMap = dataList.get(i);//数据行
//fldNameArr字段属性
for (int j = 0; j < fldNameArr.size(); j++) {
if (sheet.getRow(i + 1) == null) {
if (j == 0) {
try {
sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)) == null ? "" : baseFormMap.get(fldNameArr.get(j)));
} catch (Exception e) {
if (baseFormMap.get(fldNameArr.get(j)) == null) {
sheet.createRow(i + 1).createCell(j).setCellValue("");
} else {
sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)));
}
}
}
} else {
BigDecimal b = new BigDecimal(baseFormMap.get(fldNameArr.get(j)));
double value = 0d;
if (b != null) {
value = b.doubleValue();
}
if (value == 0) {
sheet.getRow(i + 1).createCell(j);
} else {
sheet.getRow(i + 1).createCell(j).setCellValue(b.doubleValue());
}
}
}
}
// 更新嵌入的workbook
POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();
try {
wb.write(xlsOut);
xlsOut.close();
} catch (IOException e) {
e.printStackTrace();
result = false;
} finally {
if (wb != null) {
try {
wb.close();
} catch (IOException e) {
e.printStackTrace();
result = false;
}
}
}
return result;
}
}
3:辅助类2
package com.example.demo.util;
import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;
import java.util.ArrayList;
import java.util.List;
/**
* 官方对于颜色的解释: http://poi.apache.org/apidocs/dev/org/apache/poi/sl/draw/binding/STSchemeColorVal.html
*/
public final class STSchemeColorValTools {
static List colorEnum = new ArrayList<>();
static {
// colorEnum.add(STSchemeColorVal.BG_1);
// colorEnum.add(STSchemeColorVal.BG_2);
// colorEnum.add(STSchemeColorVal.TX_1);
// colorEnum.add(STSchemeColorVal.TX_2);
colorEnum.add(STSchemeColorVal.ACCENT_1);
colorEnum.add(STSchemeColorVal.ACCENT_2);
colorEnum.add(STSchemeColorVal.ACCENT_3);
colorEnum.add(STSchemeColorVal.ACCENT_4);
colorEnum.add(STSchemeColorVal.ACCENT_5);
colorEnum.add(STSchemeColorVal.ACCENT_6);
// colorEnum.add(STSchemeColorVal.HLINK);
// colorEnum.add(STSchemeColorVal.FOL_HLINK);
// colorEnum.add(STSchemeColorVal.PH_CLR);
// colorEnum.add(STSchemeColorVal.DK_1);
// colorEnum.add(STSchemeColorVal.DK_2);
// colorEnum.add(STSchemeColorVal.LT_1);
// colorEnum.add(STSchemeColorVal.LT_2);
}
public static STSchemeColorVal.Enum get(int i){
return colorEnum.get(i);
}
}
4:模板
5:结果
6:如果要显示数字的话,直接在模板里面设置显示就好了
2020-04-21:新增:表格进行合并单元格,横竖都行,两种方式(感谢刘老师)
场景:在word中插入表格,并对表格进行合并,这里提供两种方式,分别对应两个类
1:模板文档内容:
2:类1
package com.example.demo.example.table;
import com.example.demo.util.PoiWordTools;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.springframework.util.StringUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 表格
*/
public class Table {
public static void main(String[] args) throws Exception {
final String returnurl = "D:\\youxi\\jx\\table1.docx"; // 结果文件
final String templateurl = "D:\\POI\\demo\\src\\main\\resources\\table1.docx"; // 模板文件
InputStream is = new FileInputStream(new File(templateurl));
XWPFDocument doc = new XWPFDocument(is);
// 替换word模板数据
replaceAll(doc);
// 保存结果文件
try {
File file = new File(returnurl);
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(returnurl);
doc.write(fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 替换段落和表格中
*/
public static void replaceAll(XWPFDocument doc) throws Exception {
doParagraphs(doc); // 处理段落文字数据,包括文字和表格、图片
}
/**
* 处理段落文字
*
* @param doc
* @throws InvalidFormatException
* @throws FileNotFoundException
* @throws IOException
*/
public static void doParagraphs(XWPFDocument doc) throws Exception {
/**----------------------------处理段落------------------------------------**/
List paragraphList = doc.getParagraphs();
if (paragraphList != null && paragraphList.size() > 0) {
for (XWPFParagraph paragraph : paragraphList) {
List runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null) {
// 动态表格
if (text.contains("${table1}")) {
run.setText("", 0);
XmlCursor cursor = paragraph.getCTP().newCursor();
XWPFTable tableOne = doc.insertNewTbl(cursor);// ---这个是关键
// 设置表格宽度,第一行宽度就可以了,这个值的单位,目前我也还不清楚,还没来得及研究
tableOne.setWidth(8500);
// 表格第一行,对于每个列,必须使用createCell(),而不是getCell(),因为第一行嘛,肯定是属于创建的,没有create哪里来的get呢
XWPFTableRow tableOneRowOne = tableOne.getRow(0);//行
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "序号");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(英文)");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(中文)");
for (int i = 0; i < 10; i ++) {
// 表格第二行
XWPFTableRow tableOneRowTwo = tableOne.createRow();//行
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "10%", "一行一列");
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(1), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "45%", "一行一列");
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(2), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "45%", "一行一列");
}
// 横着合并单元格 -----------------------------
CTHMerge cthMergeStart = CTHMerge.Factory.newInstance();
cthMergeStart.setVal(STMerge.RESTART);
CTHMerge cthMergeEnd = CTHMerge.Factory.newInstance();
cthMergeEnd.setVal(STMerge.CONTINUE);
XWPFTableCell cell71 = tableOne.getRow(6).getCell(0); // 第7行的第1列
XWPFTableCell cell72 = tableOne.getRow(6).getCell(1); // 第7行的第2列
XWPFTableCell cell73 = tableOne.getRow(6).getCell(2); // 第7行的第3列
cell71.getCTTc().getTcPr().setHMerge(cthMergeStart);
cell72.getCTTc().getTcPr().setHMerge(cthMergeEnd);
cell73.getCTTc().getTcPr().setHMerge(cthMergeEnd);
// 竖着合并单元格 -----------------------------
CTVMerge vmergeStart = CTVMerge.Factory.newInstance();
vmergeStart.setVal(STMerge.RESTART);
CTVMerge vmergeEnd = CTVMerge.Factory.newInstance();
vmergeEnd.setVal(STMerge.CONTINUE);
XWPFTableCell cell1 = tableOne.getRow(1).getCell(0); // 第2行第1列 第1行是表头
XWPFTableCell cell2 = tableOne.getRow(2).getCell(0); // 第3行第1列
XWPFTableCell cell3 = tableOne.getRow(3).getCell(0); // 第4行第1列
XWPFTableCell cell4 = tableOne.getRow(4).getCell(0); // 第5行第1列
cell1.getCTTc().getTcPr().setVMerge(vmergeStart);
cell2.getCTTc().getTcPr().setVMerge(vmergeEnd);
cell3.getCTTc().getTcPr().setVMerge(vmergeEnd);
cell4.getCTTc().getTcPr().setVMerge(vmergeEnd);
// .......
}
}
}
}
}
}
}
3:类2
package com.example.demo.example.table;
import com.example.demo.util.PoiWordTools;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import java.io.*;
import java.util.List;
/**
* 表格
*/
public class Table2 {
public static void main(String[] args) throws Exception {
final String returnurl = "D:\\youxi\\jx\\table1.docx"; // 结果文件
final String templateurl = "D:\\POI\\demo\\src\\main\\resources\\table2.docx"; // 模板文件
InputStream is = new FileInputStream(new File(templateurl));
XWPFDocument doc = new XWPFDocument(is);
// 替换word模板数据
replaceAll(doc);
// 保存结果文件
try {
File file = new File(returnurl);
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(returnurl);
doc.write(fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 替换段落和表格中
*/
public static void replaceAll(XWPFDocument doc) throws Exception {
doParagraphs(doc); // 处理段落文字数据,包括文字和表格、图片
}
/**
* 处理段落文字
*
* @param doc
* @throws InvalidFormatException
* @throws FileNotFoundException
* @throws IOException
*/
public static void doParagraphs(XWPFDocument doc) throws Exception {
/**----------------------------处理段落------------------------------------**/
List paragraphList = doc.getParagraphs();
if (paragraphList != null && paragraphList.size() > 0) {
for (XWPFParagraph paragraph : paragraphList) {
List runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null) {
// 动态表格
if (text.contains("${table1}")) {
run.setText("", 0);
XmlCursor cursor = paragraph.getCTP().newCursor();
XWPFTable tableOne = doc.insertNewTbl(cursor);// ---这个是关键
// 设置表格宽度,第一行宽度就可以了,这个值的单位,目前我也还不清楚,还没来得及研究
tableOne.setWidth(8500);
// 表格第一行,对于每个列,必须使用createCell(),而不是getCell(),因为第一行嘛,肯定是属于创建的,没有create哪里来的get呢
XWPFTableRow tableOneRowOne = tableOne.getRow(0);//行
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "序号");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(英文)");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "45%", "公司名称(中文)");
for (int i = 0; i < 10; i ++) {
// 表格第二行
XWPFTableRow tableOneRowTwo = tableOne.createRow();//行
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "10%", "一行一列");
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(1), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "45%", "一行一列");
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(2), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "45%", "一行一列");
}
// 横着合并单元格 -----------------------------
XWPFTableCell cell71 = tableOne.getRow(6).getCell(0); // 第7行的第1列
XWPFTableCell cell72 = tableOne.getRow(6).getCell(1); // 第7行的第2列
XWPFTableCell cell73 = tableOne.getRow(6).getCell(2); // 第7行的第3列
cell71.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
cell72.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
cell73.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
// 竖着合并单元格 -----------------------------
XWPFTableCell cell1 = tableOne.getRow(1).getCell(0); // 第2行第1列 第1行是表头
XWPFTableCell cell2 = tableOne.getRow(2).getCell(0); // 第3行第1列
XWPFTableCell cell3 = tableOne.getRow(3).getCell(0); // 第4行第1列
XWPFTableCell cell4 = tableOne.getRow(4).getCell(0); // 第5行第1列
cell1.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
cell2.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
cell3.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
cell4.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
// .......
}
}
}
}
}
}
}
4:结果:
两种方式本质上是一样的!
2020-04-23:解决问题:组合图页面显示的数据,跟第二列对应不上(感谢博友:吹古拉朽)
事情是这样的,博客网友吹古拉朽说,组合图页面显示的数据,跟第二列对应不上,柱状图+折线图的图表,折线图会显示柱状图的数据,显示结果如下图:很明显是不对的;
如果你已经运行过我的示例代码中的PoiDemoWordTable.java这个类,生成的第6个图表,就是这个;
解决方案:在第六个图表对应的代码中,修改position参数的值,如图:这个参数代表数据下标,从0开始的。
所以我这里改成3就好了。
本文示例源代码:(码云地址)https://gitee.com/loverer/poi-demo,私信或者评论都可以