poi生成的折线图和柱形图,用wps打开正常,用office打开显示损坏,报错“已删除的部件: 部件 /xl/drawings/drawing1.xml。 (绘图形状)”
测试版本:poi 4.1.2 、Office 2021。
既然wps能正常打开,那就使用wps打开另存为新的excel,发现新的excel在office中能正常打开,对比新旧两个excel的xml,找出不同的地方的xml,最后根据这个xml使用代码来修复。
复杂的折线图、柱状图、饼图可以参考我的另一篇文章:https://blog.csdn.net/u014644574/article/details/105695787
package com.study.test;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* POI EXCEL 图表-折线图
*/
public class PoiLineChart {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet("Sheet1");
// 创建一个画布
XSSFDrawing drawing = sheet.createDrawingPatriarch();
// 画布起始位置(左上角):x=0,y=3。画布占列数:7-0=7列。画布占行数:26-3=23行
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 3, 7, 26);
// 创建一个chart对象
XSSFChart chart = drawing.createChart(anchor);
// X轴
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
// Y轴
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
// X轴数据
XDDFCategoryDataSource category = XDDFDataSourcesFactory.fromArray(new String[]{"俄罗斯", "加拿大", "美国", "中国", "巴西", "澳大利亚", "印度"});
// Y轴数据
XDDFNumericalDataSource values = XDDFDataSourcesFactory.fromArray(new Integer[]{17098242, 9984670, 9826675, 9596961, 8514877, 7741220, 3287263});
// 创建折线图
XDDFLineChartData line = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
// 折线图,加载数据
line.addSeries(category, values);
// 绘制
chart.plot(line);
// 打印图表的xml
System.out.println(chart.getCTChart());
// 将输出写入excel文件
String filename = "排行榜前七的国家.xlsx";
fos = new FileOutputStream(filename);
wb.write(fos);
wb.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
生成的excel在wps中正常打开,Office 2021打开异常。
使用wps打开生成的excel自动修复后,另存为一个新的excel,新的excel在Office 2021中打开依然报错,但是图表已经能正常打开,这时可以在Office再另存为一个新的excel。
对比新旧两个excel的xml,找出缺失的xml,使用代码来修复缺失的xml。
package com.study.test;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
import java.io.FileInputStream;
/**
* 打印EXCEL的XML
*/
public class PrintXml {
public static void main(String[] args) throws Exception {
String path = "D:\\poi\\排行榜前七的国家.xlsx";
FileInputStream fis = new FileInputStream(path);
XSSFWorkbook wb = new XSSFWorkbook(fis);
XSSFSheet sheet = wb.getSheetAt(0);
XSSFDrawing drawing = sheet.getDrawingPatriarch();
CTChart ctChart = drawing.getCharts().get(0).getCTChart();
System.out.println(ctChart);
fis.close();
wb.close();
}
}
这里我对比poi生成excel缺失xml如下:
使用xml直接转换后赋值。使用方式一,当xml非常大时非常方便,用来调试还是很方便的。
观察发现缺失标签里面有命名空间a,比如
修复代码如下:
/**
* 修复图表
*/
private static void fixChart(XSSFChart chart) throws Exception {
String xml = "\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" ";
XmlObject xmlObject = XmlObject.Factory.parse(xml);
CTPlotArea plotArea = chart.getCTChart().getPlotArea();
List catAxList = plotArea.getCatAxList();
for (int i = 0; i < catAxList.size(); i++) {
catAxList.get(i).getNumFmt().setFormatCode("General");
CTTextBody txPr = catAxList.get(i).addNewTxPr();
txPr.set(xmlObject);
}
List valAxList = plotArea.getValAxList();
for (int i = 0; i < valAxList.size(); i++) {
valAxList.get(i).addNewNumFmt().setFormatCode("General");
CTTextBody txPr = valAxList.get(i).addNewTxPr();
txPr.set(xmlObject);
}
}
根据方式一,不断缩减xml,最终精简只需几个xml标签就行了。
/**
* 修复图表
*/
private static void fixChart(XSSFChart chart) {
CTPlotArea plotArea = chart.getCTChart().getPlotArea();
List catAxList = plotArea.getCatAxList();
for (int i = 0; i < catAxList.size(); i++) {
catAxList.get(i).getNumFmt().setFormatCode("General");
CTTextBody txPr = catAxList.get(i).addNewTxPr();
txPr.addNewBodyPr();
txPr.addNewP().addNewPPr().addNewDefRPr();
}
List valAxList = plotArea.getValAxList();
for (int i = 0; i < valAxList.size(); i++) {
valAxList.get(i).addNewNumFmt().setFormatCode("General");
CTTextBody txPr = valAxList.get(i).addNewTxPr();
txPr.addNewBodyPr();
txPr.addNewP().addNewPPr().addNewDefRPr();
}
}
package com.study.test;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
/**
* POI EXCEL 图表-折线图
*/
public class PoiLineChart2 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet("Sheet1");
// 创建一个画布
XSSFDrawing drawing = sheet.createDrawingPatriarch();
// 画布起始位置(左上角):x=0,y=3。画布占列数:7-0=7列。画布占行数:26-3=23行
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 3, 7, 26);
// 创建一个chart对象
XSSFChart chart = drawing.createChart(anchor);
// X轴
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
// Y轴
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
// X轴数据
XDDFCategoryDataSource category = XDDFDataSourcesFactory.fromArray(new String[]{"俄罗斯", "加拿大", "美国", "中国", "巴西", "澳大利亚", "印度"});
// Y轴数据
XDDFNumericalDataSource values = XDDFDataSourcesFactory.fromArray(new Integer[]{17098242, 9984670, 9826675, 9596961, 8514877, 7741220, 3287263});
// 创建折线图
XDDFLineChartData line = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
// 折线图,加载数据
line.addSeries(category, values);
// 绘制
chart.plot(line);
// 修复Office打开异常
fixChart(chart);
// 打印图表的xml
System.out.println(chart.getCTChart());
// 将输出写入excel文件
String filename = "排行榜前七的国家.xlsx";
fos = new FileOutputStream(filename);
wb.write(fos);
wb.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 修复图表
*/
private static void fixChart(XSSFChart chart) {
CTPlotArea plotArea = chart.getCTChart().getPlotArea();
List catAxList = plotArea.getCatAxList();
for (int i = 0; i < catAxList.size(); i++) {
catAxList.get(i).getNumFmt().setFormatCode("General");
CTTextBody txPr = catAxList.get(i).addNewTxPr();
txPr.addNewBodyPr();
txPr.addNewP().addNewPPr().addNewDefRPr();
}
List valAxList = plotArea.getValAxList();
for (int i = 0; i < valAxList.size(); i++) {
valAxList.get(i).addNewNumFmt().setFormatCode("General");
CTTextBody txPr = valAxList.get(i).addNewTxPr();
txPr.addNewBodyPr();
txPr.addNewP().addNewPPr().addNewDefRPr();
}
}
}
测试版本:poi 4.1.2 、Office 2021。