Reputation: 1
I'm using Apache POI 4.01 and trying to use a powerpoint slide with a single line chart as template and then using that to generate a new slide with some different values in the chart but I'm getting the same slide copied without any value change. Kindly help.
I'm trying the below code using a simple line chart in a powerpoint slide.
public static void main(String[] args) throws IOException {
// creating presentation
XMLSlideShow ppt = new XMLSlideShow();
File file1 = new File("MyDrive://LineSample.pptx");
FileInputStream inputstream;
try {
inputstream = new FileInputStream(file1);
XMLSlideShow template = new XMLSlideShow(inputstream);
XMLSlideShow testReport = new XMLSlideShow();
XSLFSlide xslfSlide = template.getSlides().get(0);
// TODO Auto-generated method stub
// adding slides to the slideshow
XSLFSlide slide1 = testReport.createSlide();
XSLFSlideLayout src_sl = xslfSlide.getSlideLayout();
XSLFSlideMaster src_sm = xslfSlide.getSlideMaster();
XSLFSlideLayout new_sl = slide1.getSlideLayout();
XSLFSlideMaster new_sm = slide1.getSlideMaster();
// copy source layout to the new layout
new_sl.importContent(src_sl);
// copy source master to the new master
new_sm.importContent(src_sm);
slide1.importContent(xslfSlide);
XSLFSlide slide = xslfSlide;
// find chart in the slide
XSLFChart chart = null;
for (POIXMLDocumentPart part : slide.getRelations()) {
if (part instanceof XSLFChart) {
chart = (XSLFChart) part;
break;
}
}
if (chart == null) {
throw new IllegalStateException("chart not found in the template");
} else {
System.out.println("Chart Found");
}
// Series Text
List<XDDFChartData> series = chart.getChartSeries();
XDDFLineChartData linechart = (XDDFLineChartData) series.get(0);
// Category
List<String> listCategories = new ArrayList<>(3);
listCategories.add("Test1");
listCategories.add("Test2");
listCategories.add("Test3");
// Values
List<Double> listValues = new ArrayList<>(3);
listValues.add(10.00);
listValues.add(20.00);
listValues.add(30.00);
String[] categories = listCategories.toArray(new String[listCategories.size()]);
Double[] values = listValues.toArray(new Double[listValues.size()]);
final int numOfPoints = categories.length;
final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange);
final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(values,
valuesDataRange);
XDDFLineChartData.Series firstSeries = (XDDFLineChartData.Series) linechart.getSeries().get(0);
firstSeries.replaceData(categoriesData, valuesData);
// firstSeries.setTitle("chartTitle",
// chart.setSheetTitle("chartTitle", 0));
firstSeries.setMarkerSize((short) 70);
firstSeries.setMarkerStyle(MarkerStyle.DASH);
firstSeries.setShowLeaderLines(true);
firstSeries.setSmooth(true);
// firstSeries.setShapeProperties(XDDFShapeProperties);
chart.plot(linechart);
FileOutputStream out = new FileOutputStream("MyDrive");
testReport.write(out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
No errors but chart values remain the same, actual result needed is a line chart with different values.
Upvotes: 0
Views: 938
Reputation: 61945
Main problem is that the data source of a PowerPoint
chart is a embedded Excel
worksheet. And this worksheet needs changed too additional to the cached data in the chart. That means we need get the chart's data source which is a Excel sheet. And then we need set all new category data, new series titles and new data values always in that sheet as well as in the chart.
Complete example:
Lets start with that LineSample.pptx
template:
Code:
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.util.CellReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ooxml.POIXMLDocumentPart;
public class PowerPointCopySlideChangeChartData {
public static void main(String[] args) throws Exception {
String filePath = "LineSample.pptx"; // has template line chart
String filePathNew = "LineSample_New.pptx";
Object[][] data = new Object[][] { // new data 1 series, 4 categories
{"", "Amount"}, // series title
{"Jan", 123d}, // category 1
{"Feb", 345d}, // category 2
{"Mar", 180d}, // category 3
{"Apr", 180d} // category 4
};
XMLSlideShow slideShowTemplate = new XMLSlideShow(new FileInputStream(filePath));
XSLFSlide slideTemplate = slideShowTemplate.getSlides().get(0);
XSLFSlideLayout slideLayoutTemplate = slideTemplate.getSlideLayout();
XSLFSlideMaster slideMasterTemplate = slideTemplate.getSlideMaster();
XMLSlideShow slideShowNew = new XMLSlideShow();
XSLFSlide slideNew = slideShowNew.createSlide();
XSLFSlideLayout slideLayoutNew = slideNew.getSlideLayout();
XSLFSlideMaster slideMasterNew = slideNew.getSlideMaster();
slideLayoutNew.importContent(slideLayoutTemplate);
slideMasterNew.importContent(slideMasterTemplate);
slideNew.importContent(slideTemplate);
slideShowTemplate.close();
XSLFChart chart = null;
for (POIXMLDocumentPart part : slideNew.getRelations()) {
if (part instanceof XSLFChart) {
chart = (XSLFChart) part;
break;
}
}
if (chart == null) {
throw new Exception("chart not found in the template");
} else {
System.out.println("Chart Found");
}
XSSFWorkbook chartDataWorkbook = chart.getWorkbook();
String sheetName = chartDataWorkbook.getSheetName(0);
XSSFSheet chartDataSheet = chartDataWorkbook.getSheet(sheetName);
if (chart.getChartSeries().size() == 1) { // we will process only one chart data
XDDFChartData chartData = chart.getChartSeries().get(0);
if (chartData.getSeries().size() == 1) { // we will process exact one series
int rMin = 1;
int rMax = data.length - 1;
// set new category data
XDDFCategoryDataSource category = null;
int c = 0;
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((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
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++) {
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));
series1.replaceData(category, values);
series1.plot(); //in chart
series1.setShowLeaderLines(true);
if (series1 instanceof XDDFLineChartData.Series) {
((XDDFLineChartData.Series)series1).setMarkerSize((short) 70);
((XDDFLineChartData.Series)series1).setMarkerStyle(MarkerStyle.DASH);
((XDDFLineChartData.Series)series1).setSmooth(true);
}
}
}
FileOutputStream out = new FileOutputStream(filePathNew);
slideShowNew.write(out);
out.close();
slideShowNew.close();
}
}
Result:
The question is whether the copying of the slide from the template to a new created slide show is really necessary since this leads to formatting issues as you see. In my opinion simply opening the whole template, changing the needed things and then saving that changed template as a new file will be better.
Start with same template.
Code:
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.util.CellReference;
import org.apache.poi.ss.util.CellRangeAddress;
public class PowerPointChangeChartData {
public static void main(String[] args) throws Exception {
String filePath = "LineSample.pptx"; // has template line chart
String filePathNew = "LineSample_New.pptx";
Object[][] data = new Object[][] { // new data 1 series, 4 categories
{"", "Amount"}, // series title
{"Jan", 123d}, // category 1
{"Feb", 345d}, // category 2
{"Mar", 180d}, // category 3
{"Apr", 180d} // category 4
};
XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream(filePath));
XSLFChart chart = slideShow.getCharts().get(0);
// get chart's data source which is a Excel sheet
XSSFWorkbook chartDataWorkbook = chart.getWorkbook();
String sheetName = chartDataWorkbook.getSheetName(0);
XSSFSheet chartDataSheet = chartDataWorkbook.getSheet(sheetName);
if (chart.getChartSeries().size() == 1) { // we will process only one chart data
XDDFChartData chartData = chart.getChartSeries().get(0);
if (chartData.getSeries().size() == 1) { // we will process exact one series
int rMin = 1;
int rMax = data.length - 1;
// set new category data
XDDFCategoryDataSource category = null;
int c = 0;
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((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
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++) {
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));
series1.replaceData(category, values);
series1.plot(); //in chart
series1.setShowLeaderLines(true);
if (series1 instanceof XDDFLineChartData.Series) {
((XDDFLineChartData.Series)series1).setMarkerSize((short) 70);
((XDDFLineChartData.Series)series1).setMarkerStyle(MarkerStyle.DASH);
((XDDFLineChartData.Series)series1).setSmooth(true);
}
}
}
FileOutputStream out = new FileOutputStream(filePathNew);
slideShow.write(out);
out.close();
slideShow.close();
}
}
Result:
Because of depreciation in current version apache poi 5.2.2
, following needs to bechanged:
...
//if (chartData.getSeries().size() == 1) { // we will process exact one series
if (chartData.getSeriesCount() == 1) { // we will process only templates having one series
...
// series 1
//XDDFChartData.Series series1 = chartData.getSeries().get(0);
XDDFChartData.Series series1 = chartData.getSeries(0);
...
Upvotes: 1