java 使用 apoi 更新 ppt 中图表的数据

本文源码:    1. git clone https://github.com/zhongchengyi/zhongcy.demos.git 下的 apoi-ppt-chart 目录

      2. 在第5节也有核心源码

1.    apoi简介

Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。

其中:

HSSF - 提供读写Microsoft Excel格式档案的功能。

XSSF - 提供读写Microsoft Excel OOXML格式档案的功能。

HWPF - 提供读写Microsoft Word格式档案的功能。

HSLF - 提供读写Microsoft PowerPoint格式档案的功能。

HDGF - 提供读写Microsoft Visio格式档案的功能。

这里主要用到 HSLF

2.    POI PPT特点

  • 比较原始,与 XSSF 不同,没有对ppt做太好的封装,基本全是操作xml的方法。
  • 关于poi ppt的文档比较少
  • 关于open-xml的文档也比较少
  • 为数不多的可以操作ppt的库

3.    POI接口操作PPT的办法

由于文档稀少,推荐自己创建简单的PPT,了解里面xml的结构,再根据其结构,通过代码读取,修改。

如:我自己创建了一个简单的ppt,只有一页,里面两个图表,我想找到图表数据所在的位置。

3.1   新建1.pptx内容如下

3.2   将1.pptx修改为1.zip

3.3   用解压工具对1.zip解压

3.4   ppt\slides     幻灯片

  • 里面是幻灯片的xml,每一个文件代表一页幻灯片
  • 一般是按照 slide1.xml , slide2.xml 命名的,后面的数字是页号
  • 每个xml都是压缩结构的文档(即内容只有两行)

使用idea打开slide1.xml,格式化后,如图:

slide.xml 是记录幻灯片的结构:其中 Shape会记录里面的文本,批注,图表,备注都是记录rid, 这些信息都是记录在p:spTree节点下。

3.5   ppt\charts 图表数据

  • 此目录记录以chartxx.xml图表信息
  • 每个图表一个文件
  • 所有幻灯片的图表都在这个目录,没有子目录了。

打开 chart1.xml

再打开1.pptx,找到第一张图表关联的数据,下图标注了系列具体的位置,其中,ser2代表A列和C列(c:cat部分与第一个c:ser共用)

3.5.1   c:ser / c:cat

  • c:f  图表与excel 的关联关系,Sheet1!$A$2:$A$4 代表是sheet1的A列2行,到A列4行
  • c:strCache 图表的缓存数据,是一个数组,c:ptCount是数组的长度,c:pt是数组里面的数据(如果更新图表时数据行与ppt原图表的长度不一样,需要更新 c:f, c:ptCount, c:pt)

3.5.2   c:ser / c:num

  • 结构上与 c:cat 是一样的。
  • c:numRef代表excel中的这一列是数字类型,
  • c:strRef代表excel中的这一列是字符类型。
  • 需要注意的是:c:cat和c:val下都有可能是c:numRef 或 c:strRef(我的源码这里没有判断)

3.5.3   相关接口

3.5.3.1            获取幻灯片的Chart

  1. XSLFSlide.getRelationParts();
  2. 遍历上面的数组
  3. 检查XSLFSlide.getRelationParts().get(n).getDocumentPart()的类型 instanceof XSLFChart

3.5.3.2            Chart关联的excel

  1. 读取:XSSFWookbook workbook = XSLFChart.getWorkBook()
  2. 修改:使用XSSFWookbook, XSSFSheet的相关接口
  3. 保存:步骤1返回的workbook.write(chart.getPackagepart().getOutputStream())

3.5.3.3            chart的缓存数据

  1. 通过 3.5.3.1 找到XSLFChart
  2. 找到绘图区域(xml中c:plotArea):XSLFChart.getCTChart().getPlotArea()
  3. 根据类型找到图表实例(可能是:CTPieChart, CTBarChart等):XSLFChart.getCTChart().getPlotArea().getXXXChartList()不为空的。
  4. 每个Chart实例都是同样的结构,以CTPieChart为例:CTPieChart.getCat获取c:cat, CTPieChart.getVal获取c:val

3.6   ppt\embeddings 嵌入的文档

4.    准备

  • 使用IDEA新建一个java 控制台程序
  • 新建一个 pom.xml 文件
  • 在 pom.xml 中增加 apache poi 的依赖
  • 使用 maven 安装依赖

4.1   poi的依赖如下

<dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>4.1.1</version>
    </dependency>

安装完成后,在idea的 libraies 里会增加以下:

5.   
流程及源码

  1. 获取 SlideShow
  2. 遍历 XSLFSlide
  3. 遍历 XSLFSlide的依赖部分
  4. 找到依赖部分为图表 (XSLFChart)的
  5. 根据图表标题、类型找到对应图表
  6. 更新图表关联的excel
  7. 更新图表的界面缓存数据
  8. 更新图表与关联excel的关系
  9. 保存新文件

代码如下:调用 run 方法

package zhongcy.demos;

import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.sl.usermodel.SlideShowFactory;
import org.apache.poi.xslf.usermodel.XSLFChart;
import org.apache.poi.xslf.usermodel.XSLFSlide;
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.openxmlformats.schemas.drawingml.x2006.chart.*;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PPTDemo {

    public void run() {
        try {
            SlideShow slideShow = SlideShowFactory.create(new File("./res/1.pptx"));

            for (Object o : slideShow.getSlides()) {
                XSLFSlide slider = (XSLFSlide) o;

                // 第一页
                if (slider.getSlideNumber() == 1) {
                    for (POIXMLDocumentPart.RelationPart part : slider.getRelationParts()) {
                        POIXMLDocumentPart documentPart = part.getDocumentPart();
                        // 是图表
                        if (documentPart instanceof XSLFChart) {
                            XSLFChart chart = (XSLFChart) documentPart;

                            // 查看里面的图表数据,才能知道是什么图表
                            CTPlotArea plot = chart.getCTChart().getPlotArea();

                            // 测试数据
                            List<SeriesData> seriesDatas = Arrays.asList(
                                    new SeriesData("", Arrays.asList(
                                            new NameDouble("行1", Math.random() * 100),
                                            new NameDouble("行2", Math.random() * 100),
                                            new NameDouble("行3", Math.random() * 100),
                                            new NameDouble("行4", Math.random() * 100),
                                            new NameDouble("行5", Math.random() * 100)
                                    )),
                                    new SeriesData("", Arrays.asList(
                                            new NameDouble("行1", Math.random() * 100),
                                            new NameDouble("行2", Math.random() * 100),
                                            new NameDouble("行3", Math.random() * 100),
                                            new NameDouble("行4", Math.random() * 100),
                                            new NameDouble("行5", Math.random() * 100)
                                    ))
                            );
                            XSSFWorkbook workbook = chart.getWorkbook();
                            XSSFSheet sheet = workbook.getSheetAt(0);

                            // 柱状图
                            if (!plot.getBarChartList().isEmpty()) {
                                CTBarChart barChart = plot.getBarChartArray(0);
                                updateChartExcelV(seriesDatas, workbook, sheet);
                                workbook.write(chart.getPackagePart().getOutputStream());

                                int i = 0;
                                for (CTBarSer ser : barChart.getSerList()) {
                                    updateChartCatAndNum(seriesDatas.get(i), ser.getTx(), ser.getCat(), ser.getVal());
                                    ++i;
                                }
                            }

                            // 饼图
                            else if (!plot.getPieChartList().isEmpty()) {
                                // 示例饼图只有一列数据
                                updateChartExcelV(Arrays.asList(seriesDatas.get(0)), workbook, sheet);
                                workbook.write(chart.getPackagePart().getOutputStream());

                                CTPieChart pieChart = plot.getPieChartArray(0);
                                int i = 0;
                                for (CTPieSer ser : pieChart.getSerList()) {
                                    updateChartCatAndNum(seriesDatas.get(i), ser.getTx(), ser.getCat(), ser.getVal());
                                    ++i;
                                }
                            }
                        }
                    }
                }

            }

            try {
                try (FileOutputStream out = new FileOutputStream("./res/o1.pptx")) {
                    slideShow.write(out);
                }
            } catch (FileNotFoundException e1) {
                e1.printStackTrace();
            } catch (IOException e1) {
                e1.printStackTrace();
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新图表的关联 excel, 值是纵向的
     *
     * @param param
     * @param workbook
     * @param sheet
     */
    protected void updateChartExcelV(List<SeriesData> seriesDatas, XSSFWorkbook workbook, XSSFSheet sheet) {
        XSSFRow title = sheet.getRow(0);
        for (int i = 0; i < seriesDatas.size(); i++) {
            SeriesData data = seriesDatas.get(i);
            if (data.name != null && !data.name.isEmpty()) {
                // 系列名称,不能修改,修改后无法打开 excel
                //                title.getCell(i + 1).setCellValue(data.name);
            }
            int size = data.value.size();
            for (int j = 0; j < size; j++) {
                XSSFRow row = sheet.getRow(j + 1);
                if (row == null) {
                    row = sheet.createRow(j + 1);
                }
                NameDouble cellValu = data.value.get(j);
                XSSFCell cell = row.getCell(0);
                if (cell == null) {
                    cell = row.createCell(0);
                }
                cell.setCellValue(cellValu.name);

                cell = row.getCell(i + 1);
                if (cell == null) {
                    cell = row.createCell(i + 1);
                }
                cell.setCellValue(cellValu.value);
            }
            int lastRowNum = sheet.getLastRowNum();
            if (lastRowNum > size) {
                for (int idx = lastRowNum; idx > size; idx--) {
                    sheet.removeRow(sheet.getRow(idx));
                }
            }
        }
    }

    /**
     * 更新 chart 的缓存数据
     *
     * @param data          数据
     * @param serTitle      系列的标题缓存
     * @param catDataSource 条目的数据缓存
     * @param numDataSource 数据的缓存
     */
    protected void updateChartCatAndNum(SeriesData data, CTSerTx serTitle, CTAxDataSource catDataSource,
                                        CTNumDataSource numDataSource) {

        // 更新系列标题
        //        serTitle.getStrRef().setF(serTitle.getStrRef().getF()); //
        //        serTitle.getStrRef().getStrCache().getPtArray(0).setV(data.name);

        // TODO cat 也可能是 numRef
        long ptCatCnt = catDataSource.getStrRef().getStrCache().getPtCount().getVal();
        long ptNumCnt = numDataSource.getNumRef().getNumCache().getPtCount().getVal();
        int dataSize = data.value.size();
        for (int i = 0; i < dataSize; i++) {
            NameDouble cellValu = data.value.get(i);
            CTStrVal cat = ptCatCnt > i ? catDataSource.getStrRef().getStrCache().getPtArray(i)
                    : catDataSource.getStrRef().getStrCache().addNewPt();
            cat.setIdx(i);
            cat.setV(cellValu.name);

            CTNumVal val = ptNumCnt > i ? numDataSource.getNumRef().getNumCache().getPtArray(i)
                    : numDataSource.getNumRef().getNumCache().addNewPt();
            val.setIdx(i);
            val.setV(String.format("%.2f", cellValu.value));

        }

        // 更新对应 excel 的range
        catDataSource.getStrRef().setF(
                replaceRowEnd(catDataSource.getStrRef().getF(),
                        ptCatCnt,
                        dataSize));
        numDataSource.getNumRef().setF(
                replaceRowEnd(numDataSource.getNumRef().getF(),
                        ptNumCnt,
                        dataSize));

        // 删除多的
        if (ptNumCnt > dataSize) {
            for (int idx = dataSize; idx < ptNumCnt; idx++) {
                catDataSource.getStrRef().getStrCache().removePt(dataSize);
                numDataSource.getNumRef().getNumCache().removePt(dataSize);
            }
        }
        // 更新个数
        catDataSource.getStrRef().getStrCache().getPtCount().setVal(dataSize);
        numDataSource.getNumRef().getNumCache().getPtCount().setVal(dataSize);
    }

    /**
     * 替换 形如: Sheet1!$A$2:$A$4 的字符
     *
     * @param range
     * @return
     */
    public static String replaceRowEnd(String range, long oldSize, long newSize) {
        Pattern pattern = Pattern.compile("(:\\$[A-Z]+\\$)(\\d+)");
        Matcher matcher = pattern.matcher(range);
        if (matcher.find()) {
            long old = Long.parseLong(matcher.group(2));
            return range.replaceAll("(:\\$[A-Z]+\\$)(\\d+)", "$1" + Long.toString(old - oldSize + newSize));
        }
        return range;
    }

    /**
     * 一个系列的数据
     */
    public static class SeriesData {

        /**
         * value 系列的名字
         */
        public String name;

        public List<NameDouble> value;

        public SeriesData(java.util.List<NameDouble> value) {
            this.value = value;
        }

        public SeriesData(String name, List<NameDouble> value) {
            this.name = name;
            this.value = value;
        }

        public SeriesData() {
        }
    }

    /**
     *
     */
    public class NameDouble {

        public String name;

        /**
         */
        public double value;

        public NameDouble(String name, double value) {
            this.name = name;
            this.value = value;
        }

        @SuppressWarnings("unused")
        public NameDouble() {
        }

    }
}

6.    运行示例

原文地址:https://www.cnblogs.com/zhongchengyi/p/12101960.html

时间: 2024-10-10 18:58:04

java 使用 apoi 更新 ppt 中图表的数据的相关文章

Java读取excel指定sheet中的各行数据,存入二维数组,包括首行,并打印

1. 读取 //读取excel指定sheet中的各行数据,存入二维数组,包括首行 public static String[][] getSheetData(XSSFSheet sheet) throws IOException { String[][] testArray = new String[sheet.getPhysicalNumberOfRows()][]; for(int rowId =0;rowId<sheet.getPhysicalNumberOfRows();rowId++)

Java 在PPT中绘制图形

Microsoft PowerPoint可支持在幻灯片中插入各种类型的图形并且可设置图形填充.线条颜色.图形大小.位置等.下面将通过Java编程来演示在PPT中绘制图形的方法. 工具:Free Spire.Presentation for Java v 2.2.3 Jar文件导入方法1:通过官网下载,并导入 Step1:创建目录文件lib,并将Spire.Presentation.jar文件导入(可直接复制该文件到lib) Step2: 选中Spire.Presentation.jar,右键,选

java新手在实际开发中所遇到的问题及解决方法小结,(持续更新遇到的问题)

?从事开发一年有余,想到自己初入公司时的困窘,在此把我记忆中在实际开发中所遇到的问题做一总结性的小结,为自己以后方便查阅,以及后来者遇到相同问题时解决更加方便快捷,希望大家集思广益把自己遇到的问题及解决方法写出来,添砖加瓦.为后来者给予一点帮助! 实用案例 如何使用Java实现汉诺塔问题 Java中定时器的使用方法 Java打印杨辉三角的具体实现代码 Java中如何实现分页功能 Java读取大文件如何高效率 Java中生成随机数的几种方法 Java zip压缩单个文件实现方法 如何计算Java对

Java三大框架之——Hibernate中的三种数据持久状态和缓存机制

Hibernate中的三种状态   瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没有这个对象对应的数据为瞬时状态这个时候是没有OID. 持久状态:对象经过Session持久化操作,缓存中存在这个对象的数据为持久状态并且数据库中存在这个对象对应的数据为持久状态这个时候有OID. 游离状态:当Session关闭,缓存中不存在这个对象数据而数据库中有这个对象的数据并且有OID为游离状态. 注:OID为了在系统中能够找到所需对象,我们需要为每一个对象分配一个

MathType可以在Word、PPT中插入矩阵吗

工科学生或者老师在写论文时最头痛的就是编辑公式,因为word自带的公式编辑器往往满足不了专业的公式需求,MathType就很好的解决了这个问题.在进行公式编辑时,难免会遇到输入矩阵的情况,那么怎么输入矩阵呢?下面我们就以word为例来给大家讲解MathType可以在Word.PPT中插入矩阵吗? 具体操作步骤如下: 1. 在需要输入矩阵处打开MathType公式编辑器: a.如果在Word菜单栏上有MathType选项,我们可以直接从这里打开MathType公式编辑窗口(如下图所示)  在wor

在Java中Highcharts前后台数据交互传输

最近在项目中要添加一个Highcharts数据图表显示.看过官方的Ajax交互事例,可惜好像使用的是PHP语言,而且没有显示后台的代码.百度查看了很多前辈们的事例,发现没一样是我所要的效果...最后还是自己试着写写.今天却成功了!我后台用的是SSH框架.在此把此经验分享给大家. Highcharts其实还是满简单的,有点像一个框架一样,因为步骤单一而简单,只要自己在各个步骤中改一改自己想要的效果就出来了,在此我就不介绍这方面的知识了,有兴趣的可以上中文官方查看事例或学习.Highcharts中文

好程序员Java学习路线分享JS中的面向对象

好程序员Java学习路线分享JS中的面向对象,在JS中,一般情况下我们都是直接写函数,直接调用,但是发现JS中也有new关键字,那么new关键字作为创建对象的关键字,给我们的感觉就是在JS中可以定义一个类,然后用new创建对象,那么在JS中如何做呢?我们先看如下案例,下面的案例是写一个简单的喷泉效果的. window.onload = function(){ // 创建一个画布对象var canvas = document.createElement("canvas");// 设置大小

[Java Web]2\Web开发中的一些架构

1.企业开发架构: 企业平台开发大量采用B/S开发模式,不管采用何种动态Web实现手段,其操作形式都是一样的,其核心操作的大部分都是围绕着数据库进行的.但是如果使用编程语言进行数据库开发,要涉及很多诸如事务.安全等操作问题,所以现在开发往往要通过中间件进行过渡,即,程序运行在中间件上,并通过中间件进行操作系统的操作,而具体一些相关的处理,如事务.安全等完全由中间件来负责,这样程序员只要完成具体的功能开发即可. 2.Java EE架构: Java EE 是在 Java SE 的基础上构建的,.NE

Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过start方法启动线程--->线程变为可运行可执行状态,然后通过数据产生共享,线程产生互斥---->线程状态变为阻塞状态---->阻塞状态想打开的话可以调用notify方法. 这里Java5中提供了封装好的类,可以直接调用然后构造阻塞状态,以保证数据的原子性. 2.如何实现? 主要是实现Blo