当前位置: 首页 > 知识库问答 >
问题:

Java在ppt中使用poi编辑条形图

鲁成天
2023-03-14

我是工作中使用POI的新手。现在我要用java中的POI来读取PPT中的条形图。我事先在其中添加了几个系列x,它们是条形图所属的excel的列标题。但我默认只能用POI读取前三列。另外,一旦我修改了条形图的列标题,或者想在只有三列的条形图中添加第四列(thar is, add a Color),当我打开PPT时,条形图无法编辑,表明条形图的节点损坏。那么有没有大师可以帮助谈论如何使用POI为条形图添加颜色(添加系列)?

例如:当我调试到"long ptCatCnt=catDataSource.getStrRef(). getStrCache(). getPtCount(). getVal();它显示nullpointerxecption,我不知道结构在ppt条形图是。所以我想知道如何更新条形图。

代码为:

公共类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() {
    }

}

}

共有1个答案

齐高寒
2023-03-14

使用新的XDDF类可以使用当前的apache poi 5.0.0PowerPoint中更新图表。这避免了直接使用ooxml-Schemas类(CT...类)。直接使用CT类容易出错,需要非常了解Office Open XML的内部XML结构。

需要知道的是,图表数据存储在嵌入式Excel工作簿中。因此,在更新数据时,需要始终更新工作簿中的数据和图表中的数据。

下面的例子是如何做到这一点的一个最小可重复的例子。

代码如下:

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.AreaReference;

import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumns;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn;

public class PowerPointChangeChartData {

 //patched version of XSSFTable.updateHeaders, see https://stackoverflow.com/questions/55532006/renaming-headers-of-xssftable-with-apache-poi-leads-to-corrupt-xlsx-file/55539181#55539181
 static void updateHeaders(XSSFTable table) {
  XSSFSheet sheet = (XSSFSheet)table.getParent();
  CellReference ref = table.getStartCellReference();

  if (ref == null) return;

  int headerRow = ref.getRow();
  int firstHeaderColumn = ref.getCol();
  XSSFRow row = sheet.getRow(headerRow);
  DataFormatter formatter = new DataFormatter();

  if (row != null /*&& row.getCTRow().validate()*/) {
   int cellnum = firstHeaderColumn;
   CTTableColumns ctTableColumns = table.getCTTable().getTableColumns();
   if(ctTableColumns != null) {
    for (CTTableColumn col : ctTableColumns.getTableColumnList()) {
     XSSFCell cell = row.getCell(cellnum);
     if (cell != null) {
      col.setName(formatter.formatCellValue(cell));
     }
     cellnum++;
    }
   }
  }
 }

 static void updateChart(XSLFChart chart, Object[][] data) throws Exception {
  // get chart's data source which is a Excel sheet
  XSSFWorkbook chartDataWorkbook = chart.getWorkbook();
  String sheetName = chartDataWorkbook.getSheetName(0);
  XSSFSheet chartDataSheet = chartDataWorkbook.getSheet(sheetName);
  // current Office uses a table as data source
  // so get that table if present
  XSSFTable chartDataTable = null;
  if (chartDataSheet.getTables().size() > 0) {
   chartDataTable = chartDataSheet.getTables().get(0);
  }

  if (chart.getChartSeries().size() == 1) { // we will process only one chart data
   XDDFChartData chartData = chart.getChartSeries().get(0);
   if (chartData.getSeriesCount() == 1) { // we will process only templates having one series

    int rMin = 1; // first row (0) is headers row
    int rMax = data.length - 1;

    // set new category data
    XDDFCategoryDataSource category = null;
    int c = 0;
    for (int r = rMin; r <= rMax; r++) {
     XSSFRow row = chartDataSheet.getRow(r); if (row == null) row = chartDataSheet.createRow(r);
     XSSFCell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
     cell.setCellValue((String)data[r][c]); // in sheet
    }
    category = XDDFDataSourcesFactory.fromStringCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c)); // in chart

    // series 1, is present already 
    c = 1;

    // set new values in sheet and in chart
    XDDFNumericalDataSource<Double> values = null;
    for (int r = rMin; r < rMax+1; r++) {
     XSSFRow row = chartDataSheet.getRow(r); if (row == null) row = chartDataSheet.createRow(r);
     XSSFCell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
     cell.setCellValue((Double)data[r][c]); // in sheet
    }
    values = XDDFDataSourcesFactory.fromNumericCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c)); 
    XDDFChartData.Series series1 = chartData.getSeries(0);
    series1.replaceData(category, values); // in chart

    // set new title in sheet and in chart
    String series1Title = (String)data[0][c];
    chartDataSheet.getRow(0).getCell(c).setCellValue(series1Title); // in sheet
    series1.setTitle(series1Title, new CellReference(sheetName, 0, c, true, true)); // in chart

    series1.plot(); 

    //further series, all new created
    int seriesCount = data[0].length - 1;
    for (int s = 2; s <= seriesCount; s++) {
     c++;

     // set new values
     for (int r = rMin; r < rMax+1; r++) {
      XSSFRow row = chartDataSheet.getRow(r); if (row == null) row = chartDataSheet.createRow(r);
      XSSFCell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
      cell.setCellValue((Double)data[r][c]); // in sheet
     }
     values = XDDFDataSourcesFactory.fromNumericCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c)); 
     XDDFChartData.Series series = chartData.addSeries(category, values); // in chart
 
     // set new title
     String seriesTitle = (String)data[0][c];
     XSSFCell cell = chartDataSheet.getRow(0).getCell(c); if (cell == null) cell = chartDataSheet.getRow(0).createCell(c);
     cell.setCellValue(seriesTitle); // in sheet
     series.setTitle(seriesTitle, new CellReference(sheetName, 0, c, true, true)); // in chart

     series.plot();
    }

    // update the table if present
    if (chartDataTable != null) {
     CellReference topLeft = new CellReference(chartDataSheet.getRow(0).getCell(0));
     CellReference bottomRight = new CellReference(chartDataSheet.getRow(rMax).getCell(c));
     AreaReference tableArea = chartDataWorkbook.getCreationHelper().createAreaReference(topLeft, bottomRight);
     chartDataTable.setArea(tableArea);
     updateHeaders(chartDataTable);
    }

   }
  }
 }  

 public static void main(String[] args) throws Exception {

  String filePath = "BarChartSample.pptx"; // has template bar chart
  String filePathNew = "BarChartSample_New.pptx";

  Object[][] data = new Object[][] { // new data 3 series, 5 categories
   {"", "Amount", "Values", "Others"}, // series title
   {"Jan", 321d, 456d, 222d}, // category 1
   {"Feb", 543d, 567d, 111d}, // category 2
   {"Mar", 432d, 123d, 333d}, // category 3
   {"Apr", 210d, 234d, 444d}, // category 4
   {"May", 198d, 345d, 444d} // category 5
  };

  XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream(filePath));

  XSLFChart chart = slideShow.getCharts().get(0);

  updateChart(chart, data);

  FileOutputStream out = new FileOutputStream(filePathNew); 
  slideShow.write(out);
  out.close();
  slideShow.close();
 }

}

结果如下:

提示:代码使用了XSSFTable的补丁版本。updateHeaders,因为当前版本无法更新表标题。请参阅使用Apache Poi重命名XSSFTable的标头会导致XLSX文件损坏。

 类似资料:
  • 了解如何在Java编程中使用POI PPT。 以下是示例 - 如何使用java创建空白PPT文档。 如何使用java将图像添加到PPT中的幻灯片。 如何使用java在PPT中的幻灯片上创建超链接。 如何使用java格式化PPT中幻灯片上的文本。 如何使用java合并两个PPT。 如何将PPT的幻灯片转换为图像。

  • 问题内容: 我正在尝试使用ApachePOI在XLSX电子表格中创建条形图,但是Excel一直在说内容存在问题,并在尝试打开文件时删除该图。这是我要执行的操作的完整代码: 谁能帮助我找到(并且很好地解决)这个问题?提前致谢! 问题答案: 对于不了解背景的用户,ApachePOI仅支持ScatterCharts和LineCharts为什么?。原则上描述了如何进行。 就像我说的。首先进行 最简单 的条

  • 我试图使用Apache POI在XLSX电子表格中创建一个条形图,但Excel一直说内容有问题,当我试图打开该文件时会删除该图。下面是我正在尝试的全部代码: 有谁能帮我找到(并解决)这个问题吗?提前道谢!

  • 本教程提供了对 Apache POI 库及其功能的基本了解。 在这里,我们将学习如何使用 Java 程序读取,编写和管理 MS-PowerPoint 文档。

  • 我试图在我创建的相同PPTX模板上基于不同的输入/为不同的用户生成几个报告(即N个PPTX文件)。 我在PPTX模板上有几个预格式化的XSLFTTextShape,其中包含一个已经格式化的XSLFTTextPartagle(即形状和文本)。每个形状都包含一个特定的占位符,我需要用dynimic值替换它。我在地图中有这个值(占位符,newValue)。我成功地使用新值更新了占位符: 提前感谢!

  • 我正在尝试使用Apache POI编辑包含数据的excel文件。我编写了以下代码: 当我运行代码时,当我尝试打开Excel文件时,会出现以下错误:“我们发现‘file.xlsx’中的某些内容有问题。您希望我们尽可能多地恢复吗?如果您信任此工作簿的来源,请单击是。” 如果我单击是,Excel确实会使用我指定的值进行更新;但是,我不希望出现此错误。我如何解决这个问题?