1、需求
接到一個(gè)很蛋疼的需求:每天自動(dòng)查詢前一天的運(yùn)營數(shù)據(jù),生成power point圖表,發(fā)送給相關(guān)領(lǐng)導(dǎo)。
ppt有固定模板,只需要變更每張報(bào)表的數(shù)據(jù)源即可。
2、初步思路
操作office組件,第一個(gè)能想到的肯定是POI。
但實(shí)際上POI對(duì)于power point的操作封裝的還是很差的,畢竟沒誰閑的蛋疼用代碼來操作ppt。
POI操作power point更像是在操作XML的每個(gè)節(jié)點(diǎn),逐個(gè)取值、寫入(pptx文件格式本身就是xml)。
POI的maven依賴如下:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.0.0</version>
</dependency>
注意要用5.0版本,網(wǎng)上大多數(shù)資料里提到的3.17版本,無法讀取到圖表關(guān)聯(lián)的數(shù)據(jù)源。
3、Java實(shí)現(xiàn)
3-1、獲取圖表對(duì)象
// 讀PPT模板
File fileIn = new File(PPT_TEMPLATE);
FileInputStream fin = new FileInputStream(fileIn);
XMLSlideShow ppt = new XMLSlideShow(fin);
fin.close();
// 幻燈片對(duì)象
XSLFSlide slide = ppt.getSlides().get(0);
// ==============================================
// 處理下部圖表(下idx=4)
XSLFChart chart = slide.getRelationParts().get(4).getDocumentPart();
CTPlotArea plot = chart.getCTChart().getPlotArea();
CTBarChart barChart = plot.getBarChartArray(0);
至于為什么是slide.getRelationParts().get(4)?
呵呵,我特么也不知道我要操作的這個(gè)圖表的index是幾!
方法是debug一下,看slide.getRelationParts()對(duì)象的內(nèi)容,看它下面每一個(gè)item的documentPart的Name,是不是“/ppt/charts/chartn.xml”,這對(duì)應(yīng)的是圖表。
至于plot、barChart都是啥對(duì)象,鬼才知道,反正xml里面節(jié)點(diǎn)就是這樣的,就一層一層往下找吧,后面要用到。
3-2、更新圖表關(guān)聯(lián)的Excel數(shù)據(jù)源
要修改power point中報(bào)表的內(nèi)容,本質(zhì)上是修改跟報(bào)表綁定的Excel表的數(shù)據(jù)。
// 更新圖表關(guān)聯(lián)的Excel數(shù)據(jù)源
XSSFWorkbook workbook = chart.getWorkbook();
// "sheet1"
XSSFSheet sheet = workbook.getSheetAt(0);
for (int i = 0; i < 10; i++) {
sheet.getRow(i + 2).getCell(0).setCellValue(mapChartTitle.get(i));
sheet.getRow(i + 2).getCell(1).setCellValue(mapChartData.get(i));
}
workbook.write(chart.getPackagePart().getOutputStream());
3-3、更新ppt緩存
power point這玩意,最x蛋的就是只變更數(shù)據(jù)源沒用,它自己有緩存!幻燈片上展示的數(shù)據(jù)從緩存來讀,如果只改變Excel數(shù)據(jù)源中的數(shù)據(jù),必須點(diǎn)“編輯數(shù)據(jù)”以后才能觸發(fā)頁面刷新。
// 更新chart緩存數(shù)據(jù)
CTBarSer ser = barChart.getSerList().get(0);
// 變更坐標(biāo)軸標(biāo)題
for (int i = 0; i < 10; i++) {
CTStrVal cat = ser.getCat().getStrRef().getStrCache().getPtArray(i);
cat.setIdx(i);
cat.setV(mapChartBTitle.get(i));
}
// 變更數(shù)據(jù)
for (int i = 0; i < 10; i++) {
CTNumVal val = ser.getVal().getNumRef().getNumCache().getPtArray(i);
val.setIdx(i);
val.setV(mapChartBData.get(i).toString());
}