-
2020-04-22 23:03:25
poi Excel 图表:https://blog.csdn.net/u014644574/article/details/105695787
1、pom.xml
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.4</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.13</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-compress</artifactId> <version>1.19</version> </dependency> <dependency> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>ooxml-schemas</artifactId> <version>1.4</version> </dependency>
2、poi Word生成图表-柱状图
package test; import java.io.FileOutputStream; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.Units; import org.apache.poi.xddf.usermodel.chart.AxisCrossBetween; import org.apache.poi.xddf.usermodel.chart.AxisCrosses; import org.apache.poi.xddf.usermodel.chart.AxisPosition; import org.apache.poi.xddf.usermodel.chart.BarDirection; import org.apache.poi.xddf.usermodel.chart.ChartTypes; import org.apache.poi.xddf.usermodel.chart.LegendPosition; import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis; import org.apache.poi.xddf.usermodel.chart.XDDFChartData; import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; 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; /** * poi Word生成图表-柱状图 */ public class CreateWordXDDFChart { // Methode to set title in the data sheet without creating a Table but using the sheet data only. // Creating a Table is not really necessary. static CellReference setTitleInDataSheet(XWPFChart chart, String title, int column) throws Exception { XSSFWorkbook workbook = chart.getWorkbook(); XSSFSheet sheet = workbook.getSheetAt(0); XSSFRow row = sheet.getRow(0); if (row == null) row = sheet.createRow(0); XSSFCell cell = row.getCell(column); if (cell == null) cell = row.createCell(column); cell.setCellValue(title); return new CellReference(sheet.getSheetName(), 0, column, true, true); } public static void main(String[] args) throws Exception { try (XWPFDocument document = new XWPFDocument()) { // create the data String[] categories = new String[] { "Lang 1", "Lang 2", "Lang 3" }; Double[] valuesA = new Double[] { 10d, 20d, 30d }; Double[] valuesB = new Double[] { 15d, 25d, 35d }; // create the chart XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER); // create data sources int numOfPoints = categories.length; String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0)); String valuesDataRangeA = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1)); String valuesDataRangeB = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2)); XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0); XDDFNumericalDataSource<Double> valuesDataA = XDDFDataSourcesFactory.fromArray(valuesA, valuesDataRangeA, 1); XDDFNumericalDataSource<Double> valuesDataB = XDDFDataSourcesFactory.fromArray(valuesB, valuesDataRangeB, 2); // create axis XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); // Set AxisCrossBetween, so the left axis crosses the category axis between the categories. // Else first and last category is exactly on cross points and the bars are only half visible. leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN); // create chart data XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis); ((XDDFBarChartData) data).setBarDirection(BarDirection.COL); // create series // if only one series do not vary colors for each bar ((XDDFBarChartData) data).setVaryColors(false); XDDFChartData.Series series = data.addSeries(categoriesData, valuesDataA); // XDDFChart.setSheetTitle is buggy. It creates a Table but only half way and incomplete. // Excel cannot opening the workbook after creatingg that incomplete Table. // So updating the chart data in Word is not possible. // series.setTitle("a", chart.setSheetTitle("a", 1)); series.setTitle("a", setTitleInDataSheet(chart, "a", 1)); /* // if more than one series do vary colors of the series ((XDDFBarChartData)data).setVaryColors(true); series = data.addSeries(categoriesData, valuesDataB); //series.setTitle("b", chart.setSheetTitle("b", 2)); series.setTitle("b", setTitleInDataSheet(chart, "b", 2)); */ // plot chart data chart.plot(data); // create legend XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.LEFT); legend.setOverlay(false); // 打印图表的xml // System.out.println(chart.getCTChart()); // Write the output to a file try (FileOutputStream fileOut = new FileOutputStream("CreateWordXDDFChart.docx")) { document.write(fileOut); } } } }
3、poi Word生成图表-折线图
package test; import java.io.FileOutputStream; import org.apache.poi.util.Units; import org.apache.poi.xddf.usermodel.chart.AxisPosition; import org.apache.poi.xddf.usermodel.chart.ChartTypes; import org.apache.poi.xddf.usermodel.chart.LegendPosition; import org.apache.poi.xddf.usermodel.chart.MarkerStyle; import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis; import org.apache.poi.xddf.usermodel.chart.XDDFCategoryDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; import org.apache.poi.xddf.usermodel.chart.XDDFLineChartData; import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; import org.apache.poi.xwpf.usermodel.XWPFChart; import org.apache.poi.xwpf.usermodel.XWPFDocument; /** * poi Word生成图表-折线图 */ public class CreateWordXDDFChart2 { public static void main(String[] args) throws Exception { try (XWPFDocument document = new XWPFDocument()) { // create the chart XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER); // 标题 chart.setTitleText("地区排名前七的国家"); // 标题覆盖 chart.setTitleOverlay(false); // 图例位置 XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.TOP); // 分类轴标(X轴),标题位置 XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); bottomAxis.setTitle("国家"); // 值(Y轴)轴,标题位置 XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); leftAxis.setTitle("面积和人口"); // CellRangeAddress(起始行号,终止行号, 起始列号,终止列号) // 分类轴标(X轴)数据,单元格范围位置[0, 0]到[0, 6] // XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(0, 0, 0, 6)); XDDFCategoryDataSource countries = XDDFDataSourcesFactory.fromArray(new String[] { "俄罗斯", "加拿大", "美国", "中国", "巴西", "澳大利亚", "印度" }); // 数据1,单元格范围位置[1, 0]到[1, 6] // XDDFNumericalDataSource<Double> area = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, 6)); XDDFNumericalDataSource<Integer> area = XDDFDataSourcesFactory.fromArray(new Integer[] { 17098242, 9984670, 9826675, 9596961, 8514877, 7741220, 3287263 }); // 数据1,单元格范围位置[2, 0]到[2, 6] // XDDFNumericalDataSource<Double> population = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, 6)); // LINE:折线图, XDDFLineChartData data = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis); // 图表加载数据,折线1 XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) data.addSeries(countries, area); // 折线图例标题 series1.setTitle("面积", null); // 直线 series1.setSmooth(false); // 设置标记大小 series1.setMarkerSize((short) 6); // 设置标记样式,星星 series1.setMarkerStyle(MarkerStyle.STAR); // 绘制 chart.plot(data); // 打印图表的xml // System.out.println(chart.getCTChart()); // Write the output to a file try (FileOutputStream fileOut = new FileOutputStream("CreateWordXDDFChart.docx")) { document.write(fileOut); } } } }
4、poi Word生成图表-饼图
package test; import java.io.FileOutputStream; import org.apache.poi.util.Units; import org.apache.poi.xddf.usermodel.chart.ChartTypes; import org.apache.poi.xddf.usermodel.chart.LegendPosition; import org.apache.poi.xddf.usermodel.chart.XDDFCategoryDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFChartData; import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; import org.apache.poi.xwpf.usermodel.XWPFChart; import org.apache.poi.xwpf.usermodel.XWPFDocument; /** * poi Word生成图表-饼图 */ public class CreateWordXDDFChart2 { public static void main(String[] args) throws Exception { try (XWPFDocument document = new XWPFDocument()) { // create the chart XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER); // 标题 chart.setTitleText("地区排名前七的国家"); // 标题是否覆盖图表 chart.setTitleOverlay(false); // 图例位置 XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.TOP_RIGHT); // CellRangeAddress(起始行号,终止行号, 起始列号,终止列号) // 分类轴标数据, // XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(0, 0, 0, 6)); XDDFCategoryDataSource countries = XDDFDataSourcesFactory.fromArray(new String[] { "俄罗斯", "加拿大", "美国", "中国", "巴西", "澳大利亚", "印度" }); // 数据1, // XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, 6)); XDDFNumericalDataSource<Integer> values = XDDFDataSourcesFactory.fromArray(new Integer[] { 17098242, 9984670, 9826675, 9596961, 8514877, 7741220, 3287263 }); // XDDFChartData data = chart.createData(ChartTypes.PIE3D, null, null); XDDFChartData data = chart.createData(ChartTypes.PIE, null, null); // 设置为可变颜色 data.setVaryColors(true); // 图表加载数据 data.addSeries(countries, values); // 绘制 chart.plot(data); // 打印图表的xml // System.out.println(chart.getCTChart()); // Write the output to a file try (FileOutputStream fileOut = new FileOutputStream("CreateWordXDDFChart.docx")) { document.write(fileOut); } } } }
更多相关内容 -
word使用过程中遇到的问题记录(二):如何画柱状图和折线图的组合图?
2019-12-09 15:27:53最近在写一些报告,要用到不少表格和图,一开始不知道如何画柱状图和折线图的结合体,查了一番后搞定,记录下,以备日后翻阅。 以GDP的数据为例说明: 1、在word中插入一张柱状图表,会自动生成一个excel,用来...最近在写一些报告,要用到不少表格和图,一开始不知道如何画柱状图和折线图的结合体,查了一番后搞定,记录下,以备日后翻阅。
以GDP的数据为例说明:
1、在word中插入一张柱状图表,会自动生成一个excel,用来填写数据,如下图所示:
根据自己的需求修改名称和数值,修改后会动态反映到word中图表上,很直观,如下图所示:
这时都是以柱状图的形式出现,都是在主坐标轴中。
2、修改 “国内生产总值增长率(%) ”的图表类型
在word里面图中,单独选中“国内生产总值增长率(%) ”,按右键,弹出右键菜单中选择“更改系列图表类型”,在弹出的窗口中选择折线图,如下图所示:
3、修改 “国内生产总值增长率(%) ”的坐标系
同样,在word里面图中,单独选中“国内生产总值增长率(%) ”,按右键,弹出右键菜单中选择“设置数据系列格式”,在弹出的窗口中选择次坐标轴,如下图所示:
修改完成后,最终想要的效果图就出来了,如下图所示:
-
java图表,柱状图,饼状图,折线图
2019-05-28 01:08:53NULL 博文链接:https://lipeixiaoyu.iteye.com/blog/622441 -
java实现向word文档中插入柱状图,并更改颜色
2017-10-27 22:01:33java实现向word文档中插入柱状图,并更改颜色。 将docx转化为xml文件在进行操作 -
POI_4.1.0 word模板导出 柱状图、折线图、饼图等等图表
2020-12-10 13:34:06POI_4.1.0 word模板导出 柱状图、折线图、饼图等等图表1. 这几个概念一定要搞清楚2.准备pom.xml3.编写PoiUtil工具类4.最最后一步,实践检验真理的最后一步 1. 这几个概念一定要搞清楚 本文主要介绍通过POI_4.1.0...POI_4.1.0 word模板导出 柱状图、折线图、饼图等等图表
1. 这几个概念一定要搞清楚
本文主要介绍通过POI_4.1.0导出柱状图、折线图、饼图等等图表,根据word模板导出word文件。
在开始前,你一定要知道,word中的图表,其实是通过一个内置excel来提供数据的。并且一定要搞清楚以下几个概念:系列,分类,值。下面以一个折线图来举例说明。
通过这个折线图可以很清楚的处分出系列,分类,值。图中有三条线,每条线都表示一个系列;横坐标对应的就是类别;每个类别都有相应的数据,而这个数据也就是值。如果还是不太清楚,下面通过图表对应的内置excel来分析这个三个概念。
选中word中的图表,右键菜单中有一个选项编辑数据,点击就会自动弹出内置的excel,具体格式请看下图:
通过这张图片,估计就不需要我再多说什么废话了,三个概念也就跃然纸上。搞清楚内置excel的结构和系列,分类,值这个三个概念,那你离成功只剩一步之遥。2.准备pom.xml
<dependencies> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.0</version> </dependency> <!--谷歌guava工具类--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>11.0.2</version> </dependency> </dependencies>
3.编写PoiUtil工具类
/** * @author : LCheng * @date : 2020-12-10 10:03 * description : poi工具 */ public class PoiUtil { /** * 根据word模板导出 针对图表(柱状图,折线图,饼图等)的处理 * * @param docChart 图表对象 * @param title 图表标题 * @param seriesNames 系列名称数组 * @param cats 分类信息数组 * @param values 值信息集合 与系列数组一一对应 * @return {@link XWPFChart} * @author LCheng * @date 2020/12/10 11:08 */ public static XWPFChart wordExportChar(XWPFChart docChart, String title, String[] seriesNames, String[] cats, List<Number[]> values) { //获取图表数据对象 XDDFChartData chartData = docChart.getChartSeries().get(0); //word图表均对应一个内置的excel,用于保存图表对应的数据 //excel中 第一列第二行开始的数据为分类信息 //CellRangeAddress(1, categories.size(), 0, 0) 四个参数依次为 起始行 截止行 起始列 截止列。 //excel中分类信息的范围 String catDataRange = docChart.formatRange(new CellRangeAddress(1, cats.length, 0, 0)); //根据分类信息的范围创建分类信息的数据源 XDDFDataSource<?> catDataSource = XDDFDataSourcesFactory.fromArray(cats, catDataRange, 0); //更新数据 for (int i = 0; i < seriesNames.length; i++) { //excel中各系列对应的数据的范围 String valDataRange = docChart.formatRange(new CellRangeAddress(1, cats.length, i + 1, i + 1)); //根据数据的范围创建值的数据源 Number[] val = values.get(i); XDDFNumericalDataSource<Number> valDataSource = XDDFDataSourcesFactory.fromArray(val, valDataRange, i + 1); //获取图表系列的数据对象 XDDFChartData.Series series = chartData.getSeries().get(i); //替换系列数据对象中的分类和值 series.replaceData(catDataSource, valDataSource); //修改系列数据对象中的标题 CellReference cellReference = docChart.setSheetTitle(seriesNames[i], 1); series.setTitle(seriesNames[i], cellReference); } //更新图表数据对象 docChart.plot(chartData); //图表整体的标题 传空值则不替换标题 if (!Strings.isNullOrEmpty(title)) { docChart.setTitleText(title); docChart.setTitleOverlay(false); } return docChart; } }
这是一个通用的方法,无论图表是柱状图、折线图还是饼图,都可以使用这个方法来进行word模板导出。代码的每一行代表的著述都已标注清楚,非常好理解,其实就是去修改图表对应的内置excel中的数据,将系列,分类,值的信息全部替换成我们想要的。
4.最最后一步,实践检验真理的最后一步
- 首先你需要一个word模板,具体模板请根据自己的需求太制定,测试我选用的模板如下:
- 接下来就是需要一个测试类,来修改模板中的图表,变成我们想要的样子。
/** * @author : LCheng * @date : 2020-12-10 13:21 * description : 测试类 */ public class Test { public static void main(String[] args) throws Exception { //获取word模板 InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("wordChartTemplate.docx"); XWPFDocument doc = new XWPFDocument(is); //获取word中所有图表对象 List<XWPFChart> charts = doc.getCharts(); //获取第一个单系列柱状图 XWPFChart singleBarChar = charts.get(0); //系列信息 String[] singleBarSeriesNames = {"运动情况"}; //分类信息 String[] singleBarCats = {"走路", "跑步", "游泳", "跳高", "闪现"}; //值信息 List<Number[]> singleBarValues = new ArrayList<>(); singleBarValues.add(new Number[]{100, 150, 50, 30, 200}); PoiUtil.wordExportChar(singleBarChar, "运动情况", singleBarSeriesNames, singleBarCats, singleBarValues); //获取第二个多系列折线图对象 XWPFChart multiLineChar = charts.get(1); //系列信息 String[] multiLineSeriesNames = {"吕布", "夏侯惇", "凯"}; //分类信息 String[] multiLineCats = {"走路", "跑步", "游泳", "跳高", "闪现"}; //值信息 List<Number[]> multiLineValues = new ArrayList<>(); multiLineValues.add(new Number[]{100, 150, 50, 30, 200}); multiLineValues.add(new Number[]{50, 100, 20, 150, 50}); multiLineValues.add(new Number[]{200, 130, 80, 50, 100}); PoiUtil.wordExportChar(multiLineChar, "多对象运动情况", multiLineSeriesNames, multiLineCats, multiLineValues); try (FileOutputStream fos = new FileOutputStream("D:\\test.docx")) { doc.write(fos); } } }
需要说明一点,本文将导出后的文件保存到了d盘下,请确认自己的电脑要有d盘哦。
- 接下来就是见证奇迹的时刻,看一下导出后的word长什么样吧
至此所有内容已完成,代码实现很简单,难的时候要先理解图表对应内置excel的表结构,和系列,分类,值这个三个概念,理解这些之后,一切就变得如此简单了
- 首先你需要一个word模板,具体模板请根据自己的需求太制定,测试我选用的模板如下:
-
vue组件封装echart折线图
2019-08-30 09:27:07使用Vue组件封装Echart柱状图,直接引用组件js,并且在需要展示的地方,进行Vue数据传值,支持多坐标轴。本案例非Webpack打包,直接引用JS即可。适用于部分页面使用Vue功能,或者想尝试Vue功能的同学。该组件使用... -
java使用poi在word中生成柱状图、折线图、饼图、柱状图+折线图组合图、动态表格、文本替换、图片替换、更新...
2019-09-12 14:33:13// 处理图表数据,柱状图、折线图、饼图啊之类的 } /** * 处理段落文字 * * @param doc * @throws InvalidFormatException * @throws FileNotFoundException * @throws IOException */ public static void ...本文参考地址:https://blog.csdn.net/wangxiaoyingWXY/article/details/95377533
在参考文章的基础上,增加了扩展。感谢被参考的妹子。另外该博客主要记录很多poi操作word中遇到的问题和解决方式,所以会一直维护下去。另外,我是在本地使用的wps,使用office的老哥(老姐)们可能在效果上有出入。
本文只是简单示例,仅做代码参考,在实际运用中可自行根据业务封装。
模板在示例代码中有的哈,在resource目录下面
还有就是博主真的很菜.....(手动滑稽)
关于示例代码:(码云地址)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依赖包
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.0</version> </dependency>
完整pom.xml内容
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.0</version> </dependency> <!--2.poi官网指出需要poi4.x.x版本抛弃了jdk1.7之前的版本,所以适应此版本需要将jdk升级,如果不想升级还有另一种办法就是,使用springBoot单独做一个服务为你的主项目提供一个接口,让主项目去调用生成word流让主项目去接收即可。--> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
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<String, String> textMap = new HashMap<String, String>(); textMap.put("var", "我是被替换的文本内容"); // 图片数据 Map<String, String> imgMap = new HashMap<String, String>(); imgMap.put("img", "D:/youxi/ddd.png"); /**----------------------------处理段落------------------------------------**/ List<XWPFParagraph> paragraphList = doc.getParagraphs(); if (paragraphList != null && paragraphList.size() > 0) { for (XWPFParagraph paragraph : paragraphList) { List<XWPFRun> 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<String> titleArr = new ArrayList<String>();// 标题 titleArr.add("title"); titleArr.add("金额"); List<String> fldNameArr = new ArrayList<String>();// 字段名 fldNameArr.add("item1"); fldNameArr.add("item2"); // 数据集合 List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>(); // 第一行数据 Map<String, String> base1 = new HashMap<String, String>(); base1.put("item1", "材料费用"); base1.put("item2", "500"); // 第二行数据 Map<String, String> base2 = new HashMap<String, String>(); base2.put("item1", "出差费用"); base2.put("item2", "300"); // 第三行数据 Map<String, String> base3 = new HashMap<String, String>(); base3.put("item1", "住宿费用"); base3.put("item2", "300"); listItemsByType.add(base1); listItemsByType.add(base2); listItemsByType.add(base3); // 获取word模板中的所有图表元素,用map存放 // 为什么不用list保存:查看doc.getRelations()的源码可知,源码中使用了hashMap读取文档图表元素, // 对relations变量进行打印后发现,图表顺序和文档中的顺序不一致,也就是说relations的图表顺序不是文档中从上到下的顺序 Map<String, POIXMLDocumentPart> chartsMap = new HashMap<String, POIXMLDocumentPart>(); //动态刷新图表 List<POIXMLDocumentPart> relations = doc.getRelations(); for (POIXMLDocumentPart poixmlDocumentPart : relations) { if (poixmlDocumentPart instanceof XWPFChart) { // 如果是图表元素 String str = poixmlDocumentPart.toString(); System.out.println("str:" + str); String key = str.replaceAll("Name: ", "") .replaceAll(" - Content Type: application/vnd\\.openxmlformats-officedocument\\.drawingml\\.chart\\+xml", "").trim(); System.out.println("key:" + key); chartsMap.put(key, poixmlDocumentPart); } } System.out.println("\n图表数量:" + chartsMap.size() + "\n"); // 第一个图表-条形图 POIXMLDocumentPart poixmlDocumentPart0 = chartsMap.get("/word/charts/chart1.xml"); new PoiWordTools().replaceBarCharts(poixmlDocumentPart0, titleArr, fldNameArr, listItemsByType); // 第二个-柱状图 POIXMLDocumentPart poixmlDocumentPart1 = chartsMap.get("/word/charts/chart2.xml"); new PoiWordTools().replaceBarCharts(poixmlDocumentPart1, titleArr, fldNameArr, listItemsByType); // 第三个图表-多列柱状图 doCharts3(chartsMap); // 第四个图表-折线图 doCharts4(chartsMap); // 第五个图表-饼图 POIXMLDocumentPart poixmlDocumentPart4 = chartsMap.get("/word/charts/chart5.xml"); new PoiWordTools().replacePieCharts(poixmlDocumentPart4, titleArr, fldNameArr, listItemsByType); doCharts6(chartsMap); } public static void doCharts3(Map<String, POIXMLDocumentPart> chartsMap) { // 数据准备 List<String> titleArr = new ArrayList<String>();// 标题 titleArr.add("姓名"); titleArr.add("欠款"); titleArr.add("存款"); List<String> fldNameArr = new ArrayList<String>();// 字段名 fldNameArr.add("item1"); fldNameArr.add("item2"); fldNameArr.add("item3"); // 数据集合 List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>(); // 第一行数据 Map<String, String> base1 = new HashMap<String, String>(); base1.put("item1", "老张"); base1.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base1.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第二行数据 Map<String, String> base2 = new HashMap<String, String>(); base2.put("item1", "老李"); base2.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base2.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第三行数据 Map<String, String> base3 = new HashMap<String, String>(); base3.put("item1", "老刘"); base3.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base3.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); listItemsByType.add(base1); listItemsByType.add(base2); listItemsByType.add(base3); POIXMLDocumentPart poixmlDocumentPart2 = chartsMap.get("/word/charts/chart3.xml"); new PoiWordTools().replaceBarCharts(poixmlDocumentPart2, titleArr, fldNameArr, listItemsByType); } public static void doCharts4(Map<String, POIXMLDocumentPart> chartsMap) { // 数据准备 List<String> titleArr = new ArrayList<String>();// 标题 titleArr.add("title"); titleArr.add("占基金资产净值比例22222(%)"); titleArr.add("额外的(%)"); titleArr.add("额外的(%)"); List<String> fldNameArr = new ArrayList<String>();// 字段名 fldNameArr.add("item1"); fldNameArr.add("item2"); fldNameArr.add("item3"); fldNameArr.add("item4"); // 数据集合 List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>(); // 第一行数据 Map<String, String> base1 = new HashMap<String, String>(); base1.put("item1", "材料费用"); 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<String, String> base2 = new HashMap<String, String>(); base2.put("item1", "出差费用"); 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<String, String> base3 = new HashMap<String, String>(); base3.put("item1", "住宿费用"); 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); POIXMLDocumentPart poixmlDocumentPart2 = chartsMap.get("/word/charts/chart4.xml"); new PoiWordTools().replaceLineCharts(poixmlDocumentPart2, titleArr, fldNameArr, listItemsByType); } /** * 对应文档中的第6个图表(预处理—分公司情况) */ public static void doCharts6(Map<String, POIXMLDocumentPart> chartsMap) { // 数据准备 List<String> titleArr = new ArrayList<String>();// 标题 titleArr.add("title"); titleArr.add("投诉受理量(次)"); titleArr.add("预处理拦截工单量(次)"); titleArr.add("拦截率"); List<String> fldNameArr = new ArrayList<String>();// 字段名 fldNameArr.add("item1"); fldNameArr.add("item2"); fldNameArr.add("item3"); fldNameArr.add("item4"); // 数据集合 List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>(); // 第一行数据 Map<String, String> base1 = new HashMap<String, String>(); base1.put("item1", "通辽"); 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<String, String> base2 = new HashMap<String, String>(); base2.put("item1", "呼和浩特"); 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<String, String> base3 = new HashMap<String, String>(); base3.put("item1", "锡林郭勒"); 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)) + ""); // 第四行数据 Map<String, String> base4 = new HashMap<String, String>(); base4.put("item1", "阿拉善"); base4.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base4.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base4.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第五行数据 Map<String, String> base5 = new HashMap<String, String>(); base5.put("item1", "巴彦淖尔"); base5.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base5.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base5.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第六行数据 Map<String, String> base6 = new HashMap<String, String>(); base6.put("item1", "兴安"); base6.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base6.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base6.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第七行数据 Map<String, String> base7 = new HashMap<String, String>(); base7.put("item1", "乌兰察布"); base7.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base7.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base7.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第八行数据 Map<String, String> base8 = new HashMap<String, String>(); base8.put("item1", "乌海"); base8.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base8.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base8.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第九行数据 Map<String, String> base9 = new HashMap<String, String>(); base9.put("item1", "赤峰"); base9.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base9.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base9.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第十行数据 Map<String, String> base10 = new HashMap<String, String>(); base10.put("item1", "包头"); base10.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base10.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base10.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第十一行数据 Map<String, String> base11 = new HashMap<String, String>(); base11.put("item1", "呼伦贝尔"); base11.put("item2", (int)(int)(1 + Math.random() * (100 - 1 + 1)) + ""); base11.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base11.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); // 第十二行数据 Map<String, String> base12 = new HashMap<String, String>(); base12.put("item1", "鄂尔多斯"); base12.put("item2", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base12.put("item3", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); base12.put("item4", (int)(1 + Math.random() * (100 - 1 + 1)) + ""); listItemsByType.add(base1); listItemsByType.add(base2); listItemsByType.add(base3); listItemsByType.add(base4); listItemsByType.add(base5); listItemsByType.add(base6); listItemsByType.add(base7); listItemsByType.add(base8); listItemsByType.add(base9); listItemsByType.add(base10); listItemsByType.add(base11); listItemsByType.add(base12); // 下标0的图表-折线图 POIXMLDocumentPart poixmlDocumentPart5 = chartsMap.get("/word/charts/chart6.xml"); new PoiWordTools().replaceCombinationCharts(poixmlDocumentPart5, titleArr, fldNameArr, listItemsByType); } }
类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<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) { XWPFChart chart = (XWPFChart) poixmlDocumentPart; chart.getCTChart(); //根据属性第一列名称切换数据类型 CTChart ctChart = chart.getCTChart(); CTPlotArea plotArea = ctChart.getPlotArea(); CTBarChart barChart = plotArea.getBarChartArray(0); List<CTBarSer> BarSerList = barChart.getSerList(); // 获取柱状图单位 //刷新内置excel数据 new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr); //刷新页面显示数据 refreshBarStrGraphContent(barChart, BarSerList, listItemsByType, fldNameArr, 1); } /** * 调用替换折线图数据 */ public static void replaceLineCharts(POIXMLDocumentPart poixmlDocumentPart, List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) { XWPFChart chart = (XWPFChart) poixmlDocumentPart; chart.getCTChart(); //根据属性第一列名称切换数据类型 CTChart ctChart = chart.getCTChart(); CTPlotArea plotArea = ctChart.getPlotArea(); CTLineChart lineChart = plotArea.getLineChartArray(0); List<CTLineSer> 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<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) { XWPFChart chart = (XWPFChart) poixmlDocumentPart; chart.getCTChart(); //根据属性第一列名称切换数据类型 CTChart ctChart = chart.getCTChart(); CTPlotArea plotArea = ctChart.getPlotArea(); CTPieChart pieChart = plotArea.getPieChartArray(0); List<CTPieSer> 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<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) { XWPFChart chart = (XWPFChart) poixmlDocumentPart; chart.getCTChart(); //根据属性第一列名称切换数据类型 CTChart ctChart = chart.getCTChart(); CTPlotArea plotArea = ctChart.getPlotArea(); CTBarChart barChart = plotArea.getBarChartArray(0); List<CTBarSer> 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<CTLineSer> 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<Map<String, String>> dataList, List<String> 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<Map<String, String>> dataList, List<String> 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<Map<String, String>> dataList, List<String> 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<Map<String, String>> dataList, List<String> fldNameArr, List<String> 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<String, String> 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();//获取单元格里的<w:tcPr> if (tcPr == null) {//没有<w:tcPr>,创建 tcPr = tc.addNewTcPr(); } // --vjc开始-->> CTVerticalJc vjc = tcPr.getVAlign();//获取<w:tcPr> 的<w:vAlign w:val="center"/> if (vjc == null) {//没有<w:w:vAlign/>,创建 vjc = tcPr.addNewVAlign(); } //设置单元格对齐方式 vjc.setVal(vertical.equals("top") ? STVerticalJc.TOP : vertical.equals("bottom") ? STVerticalJc.BOTTOM : STVerticalJc.CENTER); //垂直对齐 CTShd shd = tcPr.getShd();//获取<w:tcPr>里的<w:shd w:val="clear" w:color="auto" w:fill="C00000"/> if (shd == null) {//没有<w:shd>,创建 shd = tcPr.addNewShd(); } // 设置背景颜色 shd.setFill(bgColor.substring(1)); //《《《《====tcPr结束==== //====p开始====》》》》 CTP p = tc.getPList().get(0);//获取单元格里的<w:p w:rsidR="00C36068" w:rsidRPr="00B705A0" w:rsidRDefault="00C36068" w:rsidP="00C36068"> //---ppr开始--->>> CTPPr ppr = p.getPPr();//获取<w:p>里的<w:pPr> if (ppr == null) {//没有<w:pPr>,创建 ppr = p.addNewPPr(); } // --jc开始-->> CTJc jc = ppr.getJc();//获取<w:pPr>里的<w:jc w:val="left"/> if (jc == null) {//没有<w:jc/>,创建 jc = ppr.addNewJc(); } //设置单元格对齐方式 jc.setVal(alignment.equals("left") ? STJc.LEFT : alignment.equals("right") ? STJc.RIGHT : STJc.CENTER); //水平对齐 // <<--jc结束-- // --pRpr开始-->> CTParaRPr pRpr = ppr.getRPr(); //获取<w:pPr>里的<w:rPr> if (pRpr == null) {//没有<w:rPr>,创建 pRpr = ppr.addNewRPr(); } CTFonts pfont = pRpr.getRFonts();//获取<w:rPr>里的<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体"/> if (pfont == null) {//没有<w:rPr>,创建 pfont = pRpr.addNewRFonts(); } //设置字体 pfont.setAscii(fontName); pfont.setEastAsia(fontName); pfont.setHAnsi(fontName); CTOnOff pb = pRpr.getB();//获取<w:rPr>里的<w:b/> if (pb == null) {//没有<w:b/>,创建 pb = pRpr.addNewB(); } //设置字体是否加粗 pb.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF); CTHpsMeasure psz = pRpr.getSz();//获取<w:rPr>里的<w:sz w:val="32"/> if (psz == null) {//没有<w:sz w:val="32"/>,创建 psz = pRpr.addNewSz(); } // 设置单元格字体大小 psz.setVal(bFontSize); CTHpsMeasure pszCs = pRpr.getSzCs();//获取<w:rPr>里的<w:szCs w:val="32"/> if (pszCs == null) {//没有<w:szCs w:val="32"/>,创建 pszCs = pRpr.addNewSzCs(); } // 设置单元格字体大小 pszCs.setVal(bFontSize); // <<--pRpr结束-- //<<<---ppr结束--- //---r开始--->>> List<CTR> rlist = p.getRList(); //获取<w:p>里的<w:r w:rsidRPr="00B705A0"> CTR r = null; if (rlist != null && rlist.size() > 0) {//获取第一个<w:r> r = rlist.get(0); } else {//没有<w:r>,创建 r = p.addNewR(); } //--rpr开始-->> CTRPr rpr = r.getRPr();//获取<w:r w:rsidRPr="00B705A0">里的<w:rPr> if (rpr == null) {//没有<w:rPr>,创建 rpr = r.addNewRPr(); } //->- CTFonts font = rpr.getRFonts();//获取<w:rPr>里的<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体" w:hint="eastAsia"/> if (font == null) {//没有<w:rFonts>,创建 font = rpr.addNewRFonts(); } //设置字体 font.setAscii(fontName); font.setEastAsia(fontName); font.setHAnsi(fontName); CTOnOff b = rpr.getB();//获取<w:rPr>里的<w:b/> if (b == null) {//没有<w:b/>,创建 b = rpr.addNewB(); } //设置字体是否加粗 b.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF); CTColor color = rpr.getColor();//获取<w:rPr>里的<w:color w:val="FFFFFF" w:themeColor="background1"/> if (color == null) {//没有<w:color>,创建 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<CTText> tlist = r.getTList(); CTText t = null; if (tlist != null && tlist.size() > 0) {//获取第一个<w:r> t = tlist.get(0); } else {//没有<w:r>,创建 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<Map<String, String>> dataList = new ArrayList<Map<String, String>>(); // 模拟数据 static { // 模拟数据 for (int i = 0; i < 10; i++) { Map<String, String> map = new HashMap<String, String>(); 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<PackagePart> 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<String, String> 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<String, String> 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<String, String> 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<String> 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<String, String> textMap = new HashMap<String, String>(); textMap.put("texttext", "我是被替换的普通文本内容"); textMap.put("name", "我是被替换的文本框内容"); textMap.put("zuoyou", "左右"); changeTextBox(doc, textMap); } public static void changeTextBox(XWPFDocument document, Map<String, String> 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<String, String> 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<String, POIXMLDocumentPart> chartsMap = new HashMap<String, POIXMLDocumentPart>(); //动态刷新图表 List<POIXMLDocumentPart> 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<String, POIXMLDocumentPart> chartsMap) { // 数据准备 List<String> titleArr = new ArrayList<String>();// 标题,也就是对图表选择编辑数据后显示的表格数据的第一行 titleArr.add(""); titleArr.add("存款$"); titleArr.add("欠款$"); titleArr.add("还款$"); List<String> fldNameArr = new ArrayList<String>();// 字段名(数据有多少列,就多少个) fldNameArr.add("item1"); fldNameArr.add("item2"); fldNameArr.add("item3"); fldNameArr.add("item4"); // 数据集合 List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>(); // 数据的话随便整都行 // 第一行数据 Map<String, String> base1 = new HashMap<String, String>(); 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<String, String> base2 = new HashMap<String, String>(); 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<String, String> base3 = new HashMap<String, String>(); 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<String> titleArr, List<String> fldNameArr, List<Map<String, String>> 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<String> titleArr, List<String> fldNameArr, List<Map<String, String>> 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<Map<String, String>> dataList, List<String> fldNameArr, int position, int culomnNum, List<String> 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<Map<String, String>> dataList, List<String> fldNameArr, int position, int culomnNum, List<String> 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<Map<String, String>> dataList, List<String> fldNameArr, List<String> 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<String, String> 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<STSchemeColorVal.Enum> 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<XWPFParagraph> paragraphList = doc.getParagraphs(); if (paragraphList != null && paragraphList.size() > 0) { for (XWPFParagraph paragraph : paragraphList) { List<XWPFRun> 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<XWPFParagraph> paragraphList = doc.getParagraphs(); if (paragraphList != null && paragraphList.size() > 0) { for (XWPFParagraph paragraph : paragraphList) { List<XWPFRun> 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
-
java使用poi操作world生成饼图,柱状图,折线图,组合图:一
2021-07-22 17:09:38java使用poi操作world 生成饼图,柱状图,折线图,组合图开发前准备准备模板本文可用操作组合图操作java填充数据操作maven!!!WordUtil 饼图 替换入口 开发前准备 准备模板 插入模板中对应图表 本文演示饼图 折线... -
Poi 如何使用Java和POI技术生成折线图,柱状图,饼状图导出到word文档
2022-06-17 17:48:44主要介绍三种图表:折线图、柱状图、饼状图。使用Java和POI技术生成的折线图,柱状图,饼状图的效果如下图所示: 主要使用的技术有如下两个:Apache POI依赖代码如下所示: 三、具体实现 (1)折线图 折线图主要有X... -
java使用poi在word模板中替换柱状图、折线图、饼图、表格、文本、图片
2021-11-26 15:31:20java使用poi在word模板中替换柱状图、折线图、饼图、表格、文本、图片 -
java使用poi操作world生成饼图,柱状图,折线图,组合图:二
2021-07-25 17:52:55java使用poi操作world生成饼图,柱状图,折线图,组合图:二上文链接:[java使用poi操作world生成饼图,柱状图,...上文链接:java使用poi操作world生成饼图,柱状图,折线图,组合图:一 直接上代码 maven <!-- -
Apache POI 生成折线图+柱状图设置双Y轴并导出word文档
2022-05-25 17:02:42左侧为折线图Y轴,右侧为柱状图Y轴。 直接上代码。 // An highlighted block public void hydrographAndRainfallFigureWordExport(HttpServletResponse response) { //创建文本对象 XWPFDocument document = new ... -
WPS 2019专业版制作柱状图含折线图图表(本次为年终数据PPT制作)
2022-01-14 08:48:46摘要:前端时间开始使用的WPS,对于我这种轻度使用的印象还可以。在使用office的时候遇到一个问题,在进行时间筛选的时候...自己WPS 为 专业版 2019版, 这里主要记录一下表单中图表已柱状图、折线图双Y轴的制作方式。 -
使用poi-tl导出动态word模板,包含折线图、柱状图、饼状图
2020-11-17 18:59:191 依赖引入 <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId>...2 新建word模板 ...Word模板中所有变量以{{开头,以}}结尾,不同内容变量声明... -
python-pyecharts绘图:折线柱状图+堆叠柱状图+嵌套饼图+词云图
2021-11-18 14:31:53折线+柱状图 别看这个图挺复杂的,其实只要准备好数据,设置好,这些响应式的浮窗、阴影全都是自动配置的,非常省心。 # name_list, bofang, danmu, zhuifan, pts_list都是list类型 # 它们的元素类型可以是'123'... -
四步教你在excel柱形图上添加折线图
2021-07-14 00:43:47相信很多小伙伴都已经知道如何建立Excel柱形图,但对于在excel柱形图上添加折线图大家又会不会呢?在excel柱形图上添加折线图能让我们的图表更加清晰,而且这还是一个提问率非常高的Excel图表问题,今天小编就以实例... -
java使用poi在word中生成柱状图、折线图、饼图、柱状图+折线图组合图、动态表格、文本替换、图片替换 ...
2021-04-17 07:44:18} /** * 调用替换柱状图、折线图组合数据 */ public static void replaceCombinationCharts(POIXMLDocumentPart poixmlDocumentPart, List titleArr, List fldNameArr, List> listItemsByType) { XWPFChart chart =... -
在Axure中怎么做柱状图、折线图啊?
2020-12-28 19:25:32下面就来介绍一种比较简单的Word折线图制法。Word做折线图方法步骤如下:1. 打开Word,点“插入”——“图表”,弹出插入图表对话框。2. 在插入图表对话框内,我们可以看到有很多类型的图表,这里我们选择“XY散点图... -
用python画折线图、柱状图、饼图
2022-01-01 23:55:51In [1]: import numpy as np ...: import pandas as pd ...: import matplotlib.pyplot as plt ...: from pylab import * ...: mpl.rcParams['font.sans-serif'] = ['SimHei'] ...: from matplotlib.pyplot ... -
java实现图表,饼状图,柱状图,折线图,
2010-08-27 16:28:09java实现的 生成饼状图 生成单组柱状图 生成多组柱状图 生成堆积柱状图 生成折线图 里面包括了所需要的 jcommon-1.0.12.jar jfreechart-1.0.8a.jar -
Excel折线图面积图模板-销量面积累计统计表Excel图表
2020-11-24 20:11:55Excel折线图面积图模板-销量面积累计统计表Excel图表 -
POI EXCEL 图表、折线图、条形图,柱状图、饼图、散点图
2020-04-22 23:17:07POI EXCEL 图表、折线图、条形图,柱状图、饼图、散点图 -
java用ftl模板循环导出word生成echarts图表折线图柱状图饼图横向表格竖向表格
2020-05-20 15:16:06写了一个月 4月20-5月20,终于完成 …一个日报 大多es查的数据,有一点mysql查的数据 先占个坑,回头有空记录一下 -
Word或Excel里画柱状图和折线图组合体
2017-03-04 20:51:00不多说,直接上干货! 最近,在帮导师,干此项目。其中需要 现在,我带你来一步一步地画出来。 第一步:插入 -> 图表 第二步: ...... -
用python的matplotlib库绘制柱状图、折线图、饼图
2020-08-26 12:09:23用python的matplotlib库绘制柱状图、折线图、饼图 -
Freemarker导出word示例,包括jfreechart图表
2020-05-22 14:49:40通过freemarker,实现word模板中占位符的赋值以及赋值后的word文档生成。通过jfreechart生成柱状图和饼图,并在word文档中显示。 -
利用Aspose.words .ZedGraph 生成折线图 报表
2011-11-01 09:37:09利用Aspose.words .ZedGraph 生成折线图 报表 -
NPOI导word动态统计图.zip
2019-10-30 17:15:40导出word可以生成图表,饼图和柱状图和nopi的文本替换对Nopi版本有要求,我用来做的一个产品的年报 -
python数据可视化之pyecharts(一)——柱状图、折线图、散点图、饼图、词云的绘制
2020-08-24 20:44:31也可保存为jpg,png,gif等 2、折线图+柱状图 # 将柱状图和折线图放在一个图中 from pyecharts import Bar, Line, Overlap name_list = ["冰箱", "电视", "空调", "电脑"]#属性名称 value_list0 = [15, 25, 5, 10]#...