From b1d13db902da75c16b52e98ace2a49b9b5367c2f Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Sun, 21 Feb 2016 21:17:23 +0000 Subject: [PATCH] Patch from Jim King from bug #57989 - XSSFChart support for setting chart titles, plus get+set unit tests git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1731566 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/xssf/usermodel/XSSFChart.java | 54 +++++++ .../usermodel/charts/TestXSSFChartTitle.java | 132 ++++++++++++++++++ test-data/spreadsheet/chartTitle_noTitle.xlsx | Bin 0 -> 12623 bytes .../spreadsheet/chartTitle_withTitle.xlsx | Bin 0 -> 12743 bytes 4 files changed, 186 insertions(+) create mode 100644 src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java create mode 100644 test-data/spreadsheet/chartTitle_noTitle.xlsx create mode 100644 test-data/spreadsheet/chartTitle_withTitle.xlsx diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java index 27f1e996ec..f6d6834615 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFChart.java @@ -51,8 +51,13 @@ import org.openxmlformats.schemas.drawingml.x2006.chart.CTPageMargins; import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea; import org.openxmlformats.schemas.drawingml.x2006.chart.CTPrintSettings; import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle; +import org.openxmlformats.schemas.drawingml.x2006.chart.CTTx; import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; import org.openxmlformats.schemas.drawingml.x2006.chart.ChartSpaceDocument; +import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; import org.w3c.dom.NodeList; import org.w3c.dom.Text; @@ -282,6 +287,55 @@ public final class XSSFChart extends POIXMLDocumentPart implements Chart, ChartA return new XSSFRichTextString(text.toString()); } + /** + * Sets the title text. + */ + public void setTitle(String newTitle) { + CTTitle ctTitle; + if (chart.isSetTitle()) { + ctTitle = chart.getTitle(); + } else { + ctTitle = chart.addNewTitle(); + } + + CTTx tx; + if (ctTitle.isSetTx()) { + tx = ctTitle.getTx(); + } else { + tx = ctTitle.addNewTx(); + } + + if (tx.isSetStrRef()) { + tx.unsetStrRef(); + } + + CTTextBody rich; + if (tx.isSetRich()) { + rich = tx.getRich(); + } else { + rich = tx.addNewRich(); + rich.addNewBodyPr(); // body properties must exist (but can be empty) + } + + CTTextParagraph para; + if (rich.sizeOfPArray() > 0) { + para = rich.getPArray(0); + } else { + para = rich.addNewP(); + } + + if (para.sizeOfRArray() > 0) { + CTRegularTextRun run = para.getRArray(0); + run.setT(newTitle); + } else if (para.sizeOfFldArray() > 0) { + CTTextField fld = para.getFldArray(0); + fld.setT(newTitle); + } else { + CTRegularTextRun run = para.addNewR(); + run.setT(newTitle); + } + } + public XSSFChartLegend getOrCreateLegend() { return new XSSFChartLegend(this); } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java new file mode 100644 index 0000000000..de05749300 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java @@ -0,0 +1,132 @@ +/* ==================================================================== + 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.xssf.usermodel.charts; + +import junit.framework.TestCase; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.usermodel.charts.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.XSSFTestDataSamples; +import org.apache.poi.xssf.usermodel.*; + +import java.util.List; + +/** + * Test get/set chart title. + */ +public class TestXSSFChartTitle extends TestCase { + private Workbook createWorkbookWithChart() { + Workbook wb = new XSSFWorkbook(); + Sheet sheet = wb.createSheet("linechart"); + final int NUM_OF_ROWS = 3; + final int NUM_OF_COLUMNS = 10; + + // Create a row and put some cells in it. Rows are 0 based. + Row row; + Cell cell; + for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) { + row = sheet.createRow((short) rowIndex); + for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) { + cell = row.createCell((short) colIndex); + cell.setCellValue(colIndex * (rowIndex + 1)); + } + } + + Drawing drawing = sheet.createDrawingPatriarch(); + ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15); + + Chart chart = drawing.createChart(anchor); + ChartLegend legend = chart.getOrCreateLegend(); + legend.setPosition(LegendPosition.TOP_RIGHT); + + LineChartData data = chart.getChartDataFactory().createLineChartData(); + + // Use a category axis for the bottom axis. + ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); + ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); + leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); + + ChartDataSource xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1)); + ChartDataSource ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1)); + ChartDataSource ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1)); + + data.addSeries(xs, ys1); + data.addSeries(xs, ys2); + + chart.plot(data, bottomAxis, leftAxis); + + return wb; + } + + /** + * Gets the first chart from the named sheet in the workbook. + */ + private XSSFChart getChartFromWorkbook(Workbook wb, String sheetName) { + Sheet sheet = wb.getSheet(sheetName); + if (sheet instanceof XSSFSheet) { + XSSFSheet xsheet = (XSSFSheet) sheet; + XSSFDrawing drawing = xsheet.getDrawingPatriarch(); + if (drawing != null) { + List charts = drawing.getCharts(); + if (charts != null && charts.size() > 0) { + return charts.get(0); + } + } + } + return null; + } + + public void testNewChart() { + Workbook wb = createWorkbookWithChart(); + XSSFChart chart = getChartFromWorkbook(wb, "linechart"); + assertNotNull(chart); + assertNull(chart.getTitle()); + final String myTitle = "My chart title"; + chart.setTitle(myTitle); + XSSFRichTextString queryTitle = chart.getTitle(); + assertNotNull(queryTitle); + assertEquals(myTitle, queryTitle.toString()); + } + + public void testExistingChartWithTitle() { + Workbook wb = XSSFTestDataSamples.openSampleWorkbook("chartTitle_withTitle.xlsx"); + XSSFChart chart = getChartFromWorkbook(wb, "Sheet1"); + assertNotNull(chart); + XSSFRichTextString originalTitle = chart.getTitle(); + assertNotNull(originalTitle); + final String myTitle = "My chart title"; + assertFalse(myTitle.equals(originalTitle.toString())); + chart.setTitle(myTitle); + XSSFRichTextString queryTitle = chart.getTitle(); + assertNotNull(queryTitle); + assertEquals(myTitle, queryTitle.toString()); + } + + public void testExistingChartNoTitle() { + Workbook wb = XSSFTestDataSamples.openSampleWorkbook("chartTitle_noTitle.xlsx"); + XSSFChart chart = getChartFromWorkbook(wb, "Sheet1"); + assertNotNull(chart); + assertNull(chart.getTitle()); + final String myTitle = "My chart title"; + chart.setTitle(myTitle); + XSSFRichTextString queryTitle = chart.getTitle(); + assertNotNull(queryTitle); + assertEquals(myTitle, queryTitle.toString()); + } + +} diff --git a/test-data/spreadsheet/chartTitle_noTitle.xlsx b/test-data/spreadsheet/chartTitle_noTitle.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..48daae4ee56846f47a98ecca79f11c223f99d6a5 GIT binary patch literal 12623 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 literal 0 HcmV?d00001 diff --git a/test-data/spreadsheet/chartTitle_withTitle.xlsx b/test-data/spreadsheet/chartTitle_withTitle.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..680579d00af2c71f63887cb5a13f34b778edd03b GIT binary patch literal 12743 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 u0Du4m0PwdQ=%x5yqt~Cs>!|)D{zoj6mj(mc<