From 24fee0c0d58d29584a26827113ae80bc692a60b8 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 27 Feb 2018 23:12:53 +0000 Subject: [PATCH] [github-98] write data in respective Column in case of XDDFChart. Thanks to Sandeep Tiwari. This closes #98 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1825521 13f79535-47bb-0310-9956-ffa450edef68 --- .../examples/BarChartExampleDOCX.java | 129 ++++++++++++++++++ .../usermodel/examples/bar-chart-data.txt | 4 + .../examples/bar-chart-template.docx | Bin 0 -> 37656 bytes .../poi/xddf/usermodel/chart/XDDFChart.java | 120 ++++++++++++---- .../xddf/usermodel/chart/XDDFDataSource.java | 2 + .../chart/XDDFDataSourcesFactory.java | 47 ++++++- .../poi/xslf/usermodel/XSLFGraphicFrame.java | 2 +- 7 files changed, 278 insertions(+), 26 deletions(-) create mode 100644 src/examples/src/org/apache/poi/xwpf/usermodel/examples/BarChartExampleDOCX.java create mode 100644 src/examples/src/org/apache/poi/xwpf/usermodel/examples/bar-chart-data.txt create mode 100644 src/examples/src/org/apache/poi/xwpf/usermodel/examples/bar-chart-template.docx diff --git a/src/examples/src/org/apache/poi/xwpf/usermodel/examples/BarChartExampleDOCX.java b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/BarChartExampleDOCX.java new file mode 100644 index 0000000000..efc5a70f87 --- /dev/null +++ b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/BarChartExampleDOCX.java @@ -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 "); + 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 listCategories = new ArrayList(3); + + // Values + List listValues = new ArrayList(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 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 valuesData = XDDFDataSourcesFactory.fromArray(values, valuesDataRange, 1); + values[2] = 10.0; + final XDDFNumericalDataSource 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 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); + } +} + diff --git a/src/examples/src/org/apache/poi/xwpf/usermodel/examples/bar-chart-data.txt b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/bar-chart-data.txt new file mode 100644 index 0000000000..7f9c271036 --- /dev/null +++ b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/bar-chart-data.txt @@ -0,0 +1,4 @@ +My Bar or Column Chart +First 1.0 +Second 3.0 +Third 4.0 \ No newline at end of file diff --git a/src/examples/src/org/apache/poi/xwpf/usermodel/examples/bar-chart-template.docx b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/bar-chart-template.docx new file mode 100644 index 0000000000000000000000000000000000000000..ddd57ef0a30048bf0911a8f6310d036400639706 GIT binary patch literal 37656 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 literal 0 HcmV?d00001 diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java index 99a117fd36..b39419bfca 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java @@ -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 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) { + + } } /** diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java index f6c19a1d1d..1ab617571d 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSource.java @@ -31,5 +31,7 @@ public interface XDDFDataSource { boolean isNumeric(); + int getColIndex(); + String getDataRangeReference(); } diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java index a740d92f4f..be8a34ad23 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java @@ -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 XDDFNumericalDataSource fromArray(T[] elements, String dataRange, int col) { + return new NumericalArrayDataSource(elements, dataRange, col); + } + + public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange, int col) { + return new StringArrayDataSource(elements, dataRange, col); + } + public static XDDFNumericalDataSource fromNumericCellRange(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { return new NumericalCellRangeDataSource(sheet, cellRangeAddress); @@ -133,12 +151,19 @@ public class XDDFDataSourcesFactory { private abstract static class AbstractArrayDataSource implements XDDFDataSource { 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 extends AbstractArrayDataSource @@ -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 implements XDDFDataSource { @@ -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; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java index b2cf29e43b..35da6bf558 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java @@ -189,7 +189,7 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame