poi生成的折线图和柱形图,用wps打开正常,用office打开显示损坏,报错“已删除的部件: 部件 /xl/drawings/drawing1.xml。 (绘图形状)”修复

poi生成的折线图和柱形图,用wps打开正常,用office打开显示损坏,报错“已删除的部件: 部件 /xl/drawings/drawing1.xml。 (绘图形状)”

测试版本:poi 4.1.2  、Office 2021

1、修复思路

既然wps能正常打开,那就使用wps打开另存为新的excel,发现新的excel在office中能正常打开,对比新旧两个excel的xml,找出不同的地方的xml,最后根据这个xml使用代码来修复。

2、生成一个简单的折线图

复杂的折线图、柱状图、饼图可以参考我的另一篇文章: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<Integer> 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打开异常。

3、使用wps打开生成的excel自动修复

使用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如下:

<c:txPr>
        <a:bodyPr rot="-60000000" spcFirstLastPara="0" vertOverflow="ellipsis" vert="horz" wrap="square" anchor="ctr" anchorCtr="1"/>
        <a:lstStyle/>
        <a:p>
          <a:pPr>
            <a:defRPr lang="zh-CN" sz="1000" b="0" i="0" u="none" strike="noStrike" kern="1200" baseline="0">
              <a:solidFill>
                <a:schemeClr val="tx1"/>
              </a:solidFill>
              <a:latin typeface="+mn-lt"/>
              <a:ea typeface="+mn-ea"/>
              <a:cs typeface="+mn-cs"/>
            </a:defRPr>
          </a:pPr>
          <a:endParaRPr lang="zh-CN"/>
        </a:p>
      </c:txPr>

4、修复方式一:

使用xml直接转换后赋值。使用方式一,当xml非常大时非常方便,用来调试还是很方便的。

观察发现缺失标签里面有命名空间a,比如<a:bodyPr>,所以需要在xml外包裹一层命名空间a。最后效果如下:

<xml-fragment xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" >
    <a:bodyPr rot="-60000000" spcFirstLastPara="0" vertOverflow="ellipsis" vert="horz" wrap="square" anchor="ctr" anchorCtr="1"/>
    <a:lstStyle/>
    <a:p>
        <a:pPr>
            <a:defRPr lang="zh-CN" sz="1000" b="0" i="0" u="none" strike="noStrike" kern="1200" baseline="0">
                <a:solidFill>
                    <a:schemeClr val="tx1"/>
                </a:solidFill>
                <a:latin typeface="+mn-lt"/>
                <a:ea typeface="+mn-ea"/>
                <a:cs typeface="+mn-cs"/>
            </a:defRPr>
        </a:pPr>
        <a:endParaRPr lang="zh-CN"/>
    </a:p>
</xml-fragment>

修复代码如下:

    /**
     * 修复图表
     */
    private static void fixChart(XSSFChart chart) throws Exception {
        String xml = "<xml-fragment xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" >\n" +
                "    <a:bodyPr rot=\"-60000000\" spcFirstLastPara=\"0\" vertOverflow=\"ellipsis\" vert=\"horz\" wrap=\"square\" anchor=\"ctr\" anchorCtr=\"1\"/>\n" +
                "    <a:lstStyle/>\n" +
                "    <a:p>\n" +
                "        <a:pPr>\n" +
                "            <a:defRPr lang=\"zh-CN\" sz=\"1000\" b=\"0\" i=\"0\" u=\"none\" strike=\"noStrike\" kern=\"1200\" baseline=\"0\">\n" +
                "                <a:solidFill>\n" +
                "                    <a:schemeClr val=\"tx1\"/>\n" +
                "                </a:solidFill>\n" +
                "                <a:latin typeface=\"+mn-lt\"/>\n" +
                "                <a:ea typeface=\"+mn-ea\"/>\n" +
                "                <a:cs typeface=\"+mn-cs\"/>\n" +
                "            </a:defRPr>\n" +
                "        </a:pPr>\n" +
                "        <a:endParaRPr lang=\"zh-CN\"/>\n" +
                "    </a:p>\n" +
                "</xml-fragment>";
        XmlObject xmlObject = XmlObject.Factory.parse(xml);
        CTPlotArea plotArea = chart.getCTChart().getPlotArea();
        List<CTCatAx> 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<CTValAx> 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);
        }
    }

5、修复方式二(推荐):

根据方式一,不断缩减xml,最终精简只需几个xml标签就行了。

    /**
     * 修复图表
     */
    private static void fixChart(XSSFChart chart) {
        CTPlotArea plotArea = chart.getCTChart().getPlotArea();
        List<CTCatAx> 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<CTValAx> 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();
        }
    }

6、完整示例

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<Integer> 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<CTCatAx> 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<CTValAx> 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

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值