I have developed a Word function that includes a Chart. When editing chart data in a Word file, it returns to the data defined in the form.
Here are the steps:
Using apache poi 4.0.1
changing XDDFChart data needs parallel updating all changes in underlying chart data workbook and the chart itself. The chart holds the cached data while the workbook holds the source data. But both is possible using the high level apache poi
classes. No access to underlying XML beans needed.
Example
Word template which has template chart having 2 series and 3 categories:
Code:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.CellRangeAddress;
public class WordChangeChartData {
public static void main(String[] args) throws Exception {
String filePath = "TEMP_Chart_SimpleBar.docx"; // has template chart having 2 series, 3 categories
String filePathNew = "New_Chart_Simple.docx";
Object[][] data = new Object[][] { // 2 series, 3 categories
{"", "male", "female"}, // series titles
{"health", 123d, 234d}, // category 1
{"amount", 345d, 123d}, // category 2
{"size", 180d, 160d} // category 3
};
XWPFDocument document = new XWPFDocument(new FileInputStream(filePath));
XWPFChart chart = document.getCharts().get(0);
XSSFWorkbook chartDataWorkbook = chart.getWorkbook();
String sheetName = chartDataWorkbook.getSheetName(0);
XSSFSheet chartDataSheet = chartDataWorkbook.getSheet(sheetName);
if (chart.getChartSeries().size() == 1) { // only one chart data
XDDFChartData chartData = chart.getChartSeries().get(0);
if (chartData.getSeries().size() == 2) { // exact two series
int rMin = 1;
int rMax = 3;
// set new category data (both series)
XDDFCategoryDataSource category = null;
int c = 0;
for (int r = rMin; r < rMax+1; r++) {
chartDataSheet.getRow(r).getCell(c).setCellValue((String)data[r][c]); // in sheet
}
category = XDDFDataSourcesFactory.fromStringCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c)); // in chart
// series 1
XDDFChartData.Series series1 = chartData.getSeries().get(0);
c = 1;
// set new title
String series1Title = (String)data[0][c];
chartDataSheet.getRow(0).getCell(c).setCellValue(series1Title); // in sheet
if (chartDataSheet.getTables().size() > 0) {
if (chartDataSheet.getTables().get(0).getCTTable().getTableColumns().getTableColumnList().size() > c)
chartDataSheet.getTables().get(0).getCTTable().getTableColumns().getTableColumnList().get(c).setName(series1Title);
}
series1.setTitle(series1Title, new CellReference(sheetName, 0, c, true, true)); // in chart
// set new values
XDDFNumericalDataSource<Double> values = null;
for (int r = rMin; r < rMax+1; r++) {
chartDataSheet.getRow(r).getCell(c).setCellValue((Double)data[r][c]); // in sheet
}
values = XDDFDataSourcesFactory.fromNumericCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c));
series1.replaceData(category, values);
series1.plot(); //in chart
// series 2
XDDFChartData.Series series2 = chartData.getSeries().get(1);
c = 2;
// set new title
String series2Title = (String)data[0][c];
chartDataSheet.getRow(0).getCell(c).setCellValue(series2Title); // in sheet
if (chartDataSheet.getTables().size() > 0) {
if (chartDataSheet.getTables().get(0).getCTTable().getTableColumns().getTableColumnList().size() > c)
chartDataSheet.getTables().get(0).getCTTable().getTableColumns().getTableColumnList().get(c).setName(series2Title);
}
series2.setTitle(series2Title, new CellReference(sheetName, 0, c, true, true)); // in chart
// set new values
for (int r = rMin; r < rMax+1; r++) {
chartDataSheet.getRow(r).getCell(c).setCellValue((Double)data[r][c]); // in sheet
}
values = XDDFDataSourcesFactory.fromNumericCellRange(chartDataSheet, new CellRangeAddress(rMin,rMax,c,c));
series2.replaceData(category, values);
series2.plot(); // in chart
}
}
FileOutputStream out = new FileOutputStream(filePathNew);
document.write(out);
out.close();
document.close();
}
}
Result: