git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1825521 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_0_0_FINAL
@@ -0,0 +1,129 @@ | |||
/* | |||
* ==================================================================== | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.xwpf.usermodel.examples; | |||
import java.io.BufferedReader; | |||
import java.io.FileInputStream; | |||
import java.io.FileOutputStream; | |||
import java.io.FileReader; | |||
import java.io.OutputStream; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.xddf.usermodel.chart.AxisOrientation; | |||
import org.apache.poi.xddf.usermodel.chart.AxisPosition; | |||
import org.apache.poi.xddf.usermodel.chart.BarDirection; | |||
import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; | |||
import org.apache.poi.xddf.usermodel.chart.XDDFChartData; | |||
import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; | |||
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; | |||
import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; | |||
import org.apache.poi.xwpf.usermodel.XWPFChart; | |||
import org.apache.poi.xwpf.usermodel.XWPFDocument; | |||
/** | |||
* Build a bar chart from a template docx | |||
*/ | |||
public class BarChartExampleDOCX { | |||
private static void usage(){ | |||
System.out.println("Usage: BarChartDemo <bar-chart-template.docx> <bar-chart-data.txt>"); | |||
System.out.println(" bar-chart-template.docx template with a bar chart"); | |||
System.out.println(" bar-chart-data.txt the model to set. First line is chart title, " + | |||
"then go pairs {axis-label value}"); | |||
} | |||
public static void main(String[] args) throws Exception { | |||
if(args.length < 2) { | |||
usage(); | |||
return; | |||
} | |||
try (FileInputStream argIS = new FileInputStream(args[0]); | |||
BufferedReader modelReader = new BufferedReader(new FileReader(args[1]))) { | |||
String chartTitle = modelReader.readLine(); // first line is chart title | |||
// Category Axis Data | |||
List<String> listCategories = new ArrayList<String>(3); | |||
// Values | |||
List<Double> listValues = new ArrayList<Double>(3); | |||
// set model | |||
String ln; | |||
while((ln = modelReader.readLine()) != null){ | |||
String[] vals = ln.split("\\s+"); | |||
listCategories.add(vals[0]); | |||
listValues.add(Double.valueOf(vals[1])); | |||
} | |||
String[] categories = listCategories.toArray(new String[listCategories.size()]); | |||
Double[] values = listValues.toArray(new Double[listValues.size()]); | |||
try (XWPFDocument doc = new XWPFDocument(argIS)) { | |||
XWPFChart chart = doc.getCharts().get(0); | |||
setBarData(chart, chartTitle, categories, values); | |||
chart = doc.getCharts().get(1); | |||
setColumnData(chart, "Column variant"); | |||
// save the result | |||
try (OutputStream out = new FileOutputStream("bar-chart-demo-output.docx")) { | |||
doc.write(out); | |||
} | |||
} | |||
} | |||
System.out.println("Done"); | |||
} | |||
private static void setBarData(XWPFChart chart, String chartTitle, String[] categories, Double[] values) { | |||
final List<XDDFChartData> series = chart.getChartSeries(); | |||
final XDDFBarChartData bar = (XDDFBarChartData) series.get(0); | |||
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 String valuesDataRange2 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2)); | |||
final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0); | |||
final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(values, valuesDataRange, 1); | |||
values[2] = 10.0; | |||
final XDDFNumericalDataSource<? extends Number> valuesData2 = XDDFDataSourcesFactory.fromArray(values, valuesDataRange2, 2); | |||
bar.getSeries().get(0).replaceData(categoriesData, valuesData); | |||
bar.addSeries(categoriesData, valuesData2); | |||
bar.getSeries().get(0).setTitle(chartTitle, chart.setSheetTitle(chartTitle)); | |||
chart.plot(bar); | |||
} | |||
private static void setColumnData(XWPFChart chart, String chartTitle) { | |||
// Series Text | |||
List<XDDFChartData> series = chart.getChartSeries(); | |||
XDDFBarChartData bar = (XDDFBarChartData) series.get(0); | |||
bar.getSeries().get(0).setTitle(chartTitle, chart.setSheetTitle(chartTitle)); | |||
// in order to transform a bar chart into a column chart, you just need to change the bar direction | |||
bar.setBarDirection(BarDirection.COL); | |||
// additionally, you can adjust the axes | |||
bar.getCategoryAxis().setOrientation(AxisOrientation.MAX_MIN); | |||
bar.getValueAxes().get(0).setPosition(AxisPosition.TOP); | |||
} | |||
} | |||
@@ -0,0 +1,4 @@ | |||
My Bar or Column Chart | |||
First 1.0 | |||
Second 3.0 | |||
Third 4.0 |
@@ -21,6 +21,8 @@ package org.apache.poi.xddf.usermodel.chart; | |||
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.util.ArrayList; | |||
@@ -41,11 +43,14 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||
import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.openxml4j.opc.PackageRelationship; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.ss.util.CellReference; | |||
import org.apache.poi.util.Beta; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.TempFile; | |||
import org.apache.poi.xddf.usermodel.XDDFShapeProperties; | |||
import org.apache.poi.xssf.usermodel.XSSFCell; | |||
import org.apache.poi.xssf.usermodel.XSSFRow; | |||
import org.apache.poi.xssf.usermodel.XSSFSheet; | |||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
@@ -67,6 +72,9 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.CTSurface; | |||
import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; | |||
import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTable; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumn; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumns; | |||
@Beta | |||
public abstract class XDDFChart extends POIXMLDocumentPart { | |||
@@ -78,6 +86,8 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
private int chartIndex = 0; | |||
private POIXMLDocumentPart documentPart = null; | |||
protected List<XDDFChartAxis> axes = new ArrayList<>(); | |||
/** | |||
@@ -412,8 +422,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
* @since POI 4.0.0 | |||
*/ | |||
public PackageRelationship createRelationshipInChart(POIXMLRelation chartRelation, POIXMLFactory chartFactory, int chartIndex) { | |||
POIXMLDocumentPart documentPart = createRelationship(chartRelation, chartFactory, chartIndex, true).getDocumentPart(); | |||
documentPart.setCommited(true); | |||
documentPart = createRelationship(chartRelation, chartFactory, chartIndex, true).getDocumentPart(); | |||
return this.addRelation(null, chartRelation, documentPart).getRelationship(); | |||
} | |||
@@ -442,7 +451,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
* @since POI 4.0.0 | |||
*/ | |||
public void saveWorkbook(XSSFWorkbook workbook) throws IOException, InvalidFormatException { | |||
PackagePart worksheetPart = getWorksheetPart(true); | |||
PackagePart worksheetPart = getWorksheetPart(); | |||
if (worksheetPart == null) { | |||
POIXMLRelation chartRelation = getChartRelation(); | |||
POIXMLRelation chartWorkbookRelation = getChartWorkbookRelation(); | |||
@@ -454,6 +463,7 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
} | |||
} | |||
try (OutputStream xlsOut = worksheetPart.getOutputStream()) { | |||
setWorksheetPartCommitted(); | |||
workbook.write(xlsOut); | |||
} | |||
} | |||
@@ -490,9 +500,43 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
protected void fillSheet(XSSFSheet sheet, XDDFDataSource<?> categoryData, XDDFNumericalDataSource<?> valuesData) { | |||
int numOfPoints = categoryData.getPointCount(); | |||
for (int i = 0; i < numOfPoints; i++) { | |||
XSSFRow row = sheet.createRow(i + 1); // first row is for title | |||
row.createCell(0).setCellValue(categoryData.getPointAt(i).toString()); | |||
row.createCell(1).setCellValue(valuesData.getPointAt(i).doubleValue()); | |||
XSSFRow row = this.getRow(sheet, i + 1); // first row is for title | |||
this.getCell(row, categoryData.getColIndex()).setCellValue(categoryData.getPointAt(i).toString()); | |||
this.getCell(row, valuesData.getColIndex()).setCellValue(valuesData.getPointAt(i).doubleValue()); | |||
} | |||
} | |||
/** | |||
* this method return row on given index | |||
* if row is null then create new row | |||
* | |||
* @param sheet current sheet object | |||
* @param index index of current row | |||
* @return this method return sheet row on given index | |||
* @since POI 4.0.0 | |||
*/ | |||
private XSSFRow getRow(XSSFSheet sheet,int index){ | |||
if (sheet.getRow(index) != null) { | |||
return sheet.getRow(index); | |||
} else { | |||
return sheet.createRow(index); | |||
} | |||
} | |||
/** | |||
* this method return cell on given index | |||
* if cell is null then create new cell | |||
* | |||
* @param row current row object | |||
* @param index index of current cell | |||
* @return this method return sheet cell on given index | |||
* @since POI 4.0.0 | |||
*/ | |||
private XSSFCell getCell(XSSFRow row,int index){ | |||
if (row.getCell(index) != null) { | |||
return row.getCell(index); | |||
} else { | |||
return row.createCell(index); | |||
} | |||
} | |||
@@ -537,10 +581,33 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
*/ | |||
public CellReference setSheetTitle(String title) { | |||
XSSFSheet sheet = getSheet(); | |||
sheet.createRow(0).createCell(1).setCellValue(title); | |||
XSSFRow row = this.getRow(sheet, 0); | |||
XSSFCell cell = this.getCell(row, 1); | |||
cell.setCellValue(title); | |||
this.updateSheetTable(sheet.getTables().get(0).getCTTable(), title, 1); | |||
return new CellReference(sheet.getSheetName(), 0, 1, true, true); | |||
} | |||
/** | |||
* this method update column header of sheet into table | |||
* | |||
* @param ctTable xssf table object | |||
* @param title title of column | |||
* @param index index of column | |||
*/ | |||
private void updateSheetTable(CTTable ctTable, String title, int index) { | |||
CTTableColumns tableColumnList = ctTable.getTableColumns(); | |||
CTTableColumn column = null; | |||
if(tableColumnList.getCount() >= index) { | |||
column = tableColumnList.getTableColumnArray(index); | |||
} | |||
else { | |||
column = tableColumnList.addNewTableColumn(); | |||
column.setId(index); | |||
} | |||
column.setName(title); | |||
} | |||
/** | |||
* @param range | |||
* @return | |||
@@ -566,17 +633,6 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
return sheet; | |||
} | |||
/** | |||
* default method for worksheet part | |||
* | |||
* @return return embedded worksheet part | |||
* @throws InvalidFormatException | |||
* @since POI 4.0.0 | |||
*/ | |||
private PackagePart getWorksheetPart() throws InvalidFormatException { | |||
return getWorksheetPart(false); | |||
} | |||
/** | |||
* this method is used to get worksheet part | |||
* if call is from saveworkbook method then check isCommitted | |||
@@ -587,18 +643,24 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
* @throws InvalidFormatException | |||
* @since POI 4.0.0 | |||
*/ | |||
private PackagePart getWorksheetPart(boolean isCommitted) throws InvalidFormatException { | |||
private PackagePart getWorksheetPart() throws InvalidFormatException { | |||
for (RelationPart part : getRelationParts()) { | |||
if (POIXMLDocument.PACK_OBJECT_REL_TYPE.equals(part.getRelationship().getRelationshipType())) { | |||
if (isCommitted) { | |||
part.getDocumentPart().setCommited(true); | |||
} | |||
return getTargetPart(part.getRelationship()); | |||
} | |||
} | |||
return null; | |||
} | |||
private void setWorksheetPartCommitted() throws InvalidFormatException { | |||
for (RelationPart part : getRelationParts()) { | |||
if (POIXMLDocument.PACK_OBJECT_REL_TYPE.equals(part.getRelationship().getRelationshipType())) { | |||
part.getDocumentPart().setCommited(true); | |||
break; | |||
} | |||
} | |||
} | |||
/** | |||
* @return returns the workbook object of embedded excel file | |||
* @throws IOException | |||
@@ -631,7 +693,19 @@ public abstract class XDDFChart extends POIXMLDocumentPart { | |||
* @since POI 4.0.0 | |||
*/ | |||
public void setWorkbook(XSSFWorkbook workbook) { | |||
this.workbook = workbook; | |||
File file; | |||
FileOutputStream fos; | |||
try { | |||
file = TempFile.createTempFile("TempEmbedded",".xlsx"); | |||
fos = new FileOutputStream(file); | |||
workbook.write(fos); | |||
fos.close(); | |||
this.workbook = new XSSFWorkbook(file); | |||
} catch (IOException e) { | |||
} catch (InvalidFormatException e) { | |||
} | |||
} | |||
/** |
@@ -31,5 +31,7 @@ public interface XDDFDataSource<T> { | |||
boolean isNumeric(); | |||
int getColIndex(); | |||
String getDataRangeReference(); | |||
} |
@@ -68,6 +68,11 @@ public class XDDFDataSourcesFactory { | |||
public String getDataRangeReference() { | |||
return categoryDS.getStrRef().getF(); | |||
} | |||
@Override | |||
public int getColIndex() { | |||
return 0; | |||
} | |||
}; | |||
} | |||
@@ -110,6 +115,11 @@ public class XDDFDataSourcesFactory { | |||
public String getDataRangeReference() { | |||
return valuesDS.getNumRef().getF(); | |||
} | |||
@Override | |||
public int getColIndex() { | |||
return 0; | |||
} | |||
}; | |||
} | |||
@@ -121,6 +131,14 @@ public class XDDFDataSourcesFactory { | |||
return new StringArrayDataSource(elements, dataRange); | |||
} | |||
public static <T extends Number> XDDFNumericalDataSource<T> fromArray(T[] elements, String dataRange, int col) { | |||
return new NumericalArrayDataSource<T>(elements, dataRange, col); | |||
} | |||
public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange, int col) { | |||
return new StringArrayDataSource(elements, dataRange, col); | |||
} | |||
public static XDDFNumericalDataSource<Double> fromNumericCellRange(XSSFSheet sheet, | |||
CellRangeAddress cellRangeAddress) { | |||
return new NumericalCellRangeDataSource(sheet, cellRangeAddress); | |||
@@ -133,12 +151,19 @@ public class XDDFDataSourcesFactory { | |||
private abstract static class AbstractArrayDataSource<T> implements XDDFDataSource<T> { | |||
private final T[] elements; | |||
private final String dataRange; | |||
private int col = 0; | |||
public AbstractArrayDataSource(T[] elements, String dataRange) { | |||
this.elements = elements.clone(); | |||
this.dataRange = dataRange; | |||
} | |||
public AbstractArrayDataSource(T[] elements, String dataRange, int col) { | |||
this.elements = elements.clone(); | |||
this.dataRange = dataRange; | |||
this.col = col; | |||
} | |||
@Override | |||
public int getPointCount() { | |||
return elements.length; | |||
@@ -168,6 +193,11 @@ public class XDDFDataSourcesFactory { | |||
return dataRange; | |||
} | |||
} | |||
@Override | |||
public int getColIndex() { | |||
return col; | |||
} | |||
} | |||
private static class NumericalArrayDataSource<T extends Number> extends AbstractArrayDataSource<T> | |||
@@ -178,6 +208,10 @@ public class XDDFDataSourcesFactory { | |||
super(elements, dataRange); | |||
} | |||
public NumericalArrayDataSource(T[] elements, String dataRange, int col) { | |||
super(elements, dataRange, col); | |||
} | |||
@Override | |||
public String getFormatCode() { | |||
return formatCode; | |||
@@ -194,6 +228,10 @@ public class XDDFDataSourcesFactory { | |||
public StringArrayDataSource(String[] elements, String dataRange) { | |||
super(elements, dataRange); | |||
} | |||
public StringArrayDataSource(String[] elements, String dataRange, int col) { | |||
super(elements, dataRange, col); | |||
} | |||
} | |||
private abstract static class AbstractCellRangeDataSource<T> implements XDDFDataSource<T> { | |||
@@ -220,6 +258,11 @@ public class XDDFDataSourcesFactory { | |||
return true; | |||
} | |||
@Override | |||
public int getColIndex() { | |||
return cellRangeAddress.getFirstColumn(); | |||
} | |||
@Override | |||
public String getDataRangeReference() { | |||
return cellRangeAddress.formatAsString(sheet.getSheetName(), true); | |||
@@ -262,7 +305,7 @@ public class XDDFDataSourcesFactory { | |||
@Override | |||
public Double getPointAt(int index) { | |||
CellValue cellValue = getCellValueAt(index); | |||
if (cellValue != null && cellValue.getCellTypeEnum() == CellType.NUMERIC) { | |||
if (cellValue != null && cellValue.getCellType() == CellType.NUMERIC) { | |||
return Double.valueOf(cellValue.getNumberValue()); | |||
} else { | |||
return null; | |||
@@ -284,7 +327,7 @@ public class XDDFDataSourcesFactory { | |||
@Override | |||
public String getPointAt(int index) { | |||
CellValue cellValue = getCellValueAt(index); | |||
if (cellValue != null && cellValue.getCellTypeEnum() == CellType.STRING) { | |||
if (cellValue != null && cellValue.getCellType() == CellType.STRING) { | |||
return cellValue.getStringValue(); | |||
} else { | |||
return null; |
@@ -189,7 +189,7 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFSh | |||
XSLFChart srcChart = (XSLFChart) src.getRelationById(id); | |||
XSLFChart chartCopy = slide.getSlideShow().createChart(slide); | |||
chartCopy.importContent(srcChart); | |||
chartCopy.saveWorkbook(srcChart.getWorkbook()); | |||
chartCopy.setWorkbook(srcChart.getWorkbook()); | |||
c.setAttributeText(idQualifiedName, slide.getRelationId(chartCopy)); | |||
} catch (InvalidFormatException e) { | |||
throw new POIXMLException(e); |