Updating Waterfall Chart with Apache POI

时光怂恿深爱的人放手 提交于 2021-02-08 10:47:29

问题


I am using the Apache POI Java library (v4.1.1) to create wonderfull Word documents including Excel Charts. For the Excel Charts I use an Excel template and via the Apache POI library access the charts in the Word document via

List<XWPFChart> chartList = wordDoc.getCharts();  

This is working fine for Bar / Column / Line etc. I can't get, however, the Waterfall chart via this method. So, I am wondering if anybody has got a solution for this? I see some options;

  1. Waiting until it is supported by Apache POI
  2. Creating another chart type as a template, access that chart via getCharts method and altering the chart type programmatically
  3. Creating my own code for generating a waterfall chart

I am hoping somebody already has experience with the second or even third option?


回答1:


XWPFChart is of type application/vnd.openxmlformats-officedocument.drawingml.chart+xml while waterfall chart is of type application/vnd.ms-office.chartex+xml. This is because the waterfall chart is an extended chart type which is not available in versions of Office Open XML which apache poi provides. I don't believe apache poi will provide such extended charts in near future since it not even provides all native charts of type application/vnd.openxmlformats-officedocument.drawingml.chart+xml until now.

So until now there only is the option to work on those chart types on very low level by changing the XML directly. I have shown this for a sunburst chart here How to change the graphical attributes of a point in an Excel sunburst chart through Apache POI and here How to set the text attributes of the individual data labels in an Excel Sunburst Chart through Apache POI?.

Following working draft provides a very basic class XWPFChartEx which up to now only provides method getChartExXmlObject which returns the plain XML of the extended chart as a XmlObject. This XML could be programmatically changed using low level XML methods. Because XWPFChartEx extends POIXMLDocumentPart, it's commit method will write that changes into the new Word document then while document.write(out). And it provides getWorkbookPart which returns the PackagePart of the XSSFWorkbook containing the chart's data. This workbook content also would mus be changed, if present.

The file Waterfall_Chart.docx must have at least one waterfall chart.

import java.io.IOException;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFRelation;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.*;

import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ooxml.POIXMLRelation;
import org.apache.poi.openxml4j.opc.PackagePart;

import org.apache.xmlbeans.XmlObject;

public class WordGetWaterfallChart {

 static XWPFChartEx getFirstXWPFChartEx(XWPFDocument document) throws Exception {
  XWPFChartEx xwpfChartEx = null;
  for (POIXMLDocumentPart dpart : document.getRelations()) {
   PackagePart ppart = dpart.getPackagePart();
   if ("application/vnd.ms-office.chartex+xml".equals(ppart.getContentType())) {
    xwpfChartEx = new XWPFChartEx(dpart);
    String rId = document.getRelationId(dpart);
    document.addRelation(
     rId, 
     new XSSFChartExRelation(
      "application/vnd.ms-office.chartex+xml",
      "http://schemas.microsoft.com/office/2014/relationships/chartEx",
      "/word/charts/chartEx#.xml"),
      xwpfChartEx
    );
    return xwpfChartEx;
   }
  }
  return xwpfChartEx;
 }

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

  XWPFDocument document = new XWPFDocument(new FileInputStream("Waterfall_Chart.docx"));

  XWPFChartEx waterfallChart = getFirstXWPFChartEx(document);
  System.out.println(waterfallChart.getChartExXmlObject());
  //TODO: change the XML

  System.out.println(waterfallChart.getWorkbookPart());
  if (waterfallChart.getWorkbookPart() != null) {
   XSSFWorkbook workbook = new XSSFWorkbook(waterfallChart.getWorkbookPart().getInputStream());
   for (Sheet sheet : workbook) {
    for (Row row : sheet) {
     for (Cell cell : row) {
      System.out.println(cell);
      //TODO: change the cell contents
     }
    }
   }
   OutputStream wbOut = waterfallChart.getWorkbookPart().getOutputStream();
   workbook.write(wbOut);
   wbOut.close();
   workbook.close();
  }

  FileOutputStream out = new FileOutputStream("Waterfall_Chart_Changed.docx");
  document.write(out);
  out.close();
  document.close();
 }

 private static class XWPFChartEx extends POIXMLDocumentPart {

  private XmlObject chartExXmlObject;
  private PackagePart workbookPart;

  private XWPFChartEx(POIXMLDocumentPart dpart) throws Exception {
   super(dpart.getPackagePart());
   this.chartExXmlObject = XmlObject.Factory.parse(dpart.getPackagePart().getInputStream());
   for (POIXMLDocumentPart.RelationPart rpart : dpart.getRelationParts()) {
    if ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
         .equals(rpart.getDocumentPart().getPackagePart().getContentType())) {
     this.addRelation(
      rpart.getRelationship().getId(), 
      XWPFRelation.getInstance(rpart.getRelationship().getRelationshipType()),
      rpart.getDocumentPart()
     );
     this.workbookPart = rpart.getDocumentPart().getPackagePart();
    }
   }
  }

  private XmlObject getChartExXmlObject() {
   return this.chartExXmlObject;
  }

  private PackagePart getWorkbookPart() {
   return this.workbookPart;
  }

  @Override
  protected void commit() throws IOException {
   PackagePart part = getPackagePart();
   OutputStream out = part.getOutputStream();
   chartExXmlObject.save(out);
   out.close();
  }
 }

 private static class XSSFChartExRelation extends POIXMLRelation {
  private XSSFChartExRelation(String type, String rel, String defaultName) {
   super(type, rel, defaultName);
  }
 }
}


来源:https://stackoverflow.com/questions/59510202/updating-waterfall-chart-with-apache-poi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!