From f8c6a52a2cb6ad5ee3a93dbb2dc31e948688dde3 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Thu, 10 Apr 2008 15:54:02 +0000 Subject: [PATCH] Initial support for getting and changing chart titles and series titles git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@646854 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 2 + src/documentation/content/xdocs/status.xml | 2 + .../record/DrawingRecordForBiffViewer.java | 17 +++ .../apache/poi/hssf/record/RecordFactory.java | 2 + .../apache/poi/hssf/usermodel/HSSFSheet.java | 31 +++- .../apache/poi/hssf/usermodel/HSSFChart.java | 138 ++++++++++++++++-- .../poi/hssf/usermodel/TestHSSFChart.java | 80 +++++++++- 7 files changed, 245 insertions(+), 27 deletions(-) diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 35e5a8f01..c3b1f72e8 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,8 @@ + Initial support for getting and changing chart and series titles + Implement a proxy HSSFListener which tracks the format records, and lets you lookup the format string for a given cell. Convert the xls to csv example to use it 44792 - fixed encode/decode problems in ExternalNameRecord and CRNRecord. 43670, 44501 - Fix how HDGF deals with trailing data in the list of chunk headers 30311 - More work on Conditional Formatting diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index c102f37e8..2ceb4ea9e 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,8 @@ + Initial support for getting and changing chart and series titles + Implement a proxy HSSFListener which tracks the format records, and lets you lookup the format string for a given cell. Convert the xls to csv example to use it 44792 - fixed encode/decode problems in ExternalNameRecord and CRNRecord. 43670, 44501 - Fix how HDGF deals with trailing data in the list of chunk headers 30311 - More work on Conditional Formatting diff --git a/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java b/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java index b0a1bbf2e..4e5536f30 100644 --- a/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java +++ b/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java @@ -17,6 +17,8 @@ package org.apache.poi.hssf.record; +import java.io.ByteArrayInputStream; + /** * This is purely for the biff viewer. During normal operations we don't want * to be seeing this. @@ -35,6 +37,21 @@ public class DrawingRecordForBiffViewer super(in); } + public DrawingRecordForBiffViewer(DrawingRecord r) + { + super(convertToInputStream(r)); + convertRawBytesToEscherRecords(); + } + private static RecordInputStream convertToInputStream(DrawingRecord r) + { + byte[] data = r.serialize(); + RecordInputStream rinp = new RecordInputStream( + new ByteArrayInputStream(data) + ); + rinp.nextRecord(); + return rinp; + } + protected String getRecordName() { return "MSODRAWING"; diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java index 1dada4450..5a627988f 100644 --- a/src/java/org/apache/poi/hssf/record/RecordFactory.java +++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java @@ -77,6 +77,8 @@ public class RecordFactory NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class, FileSharingRecord.class, ChartTitleFormatRecord.class, DVRecord.class, DVALRecord.class, UncalcedRecord.class, + ChartRecord.class, LegendRecord.class, ChartTitleFormatRecord.class, + SeriesRecord.class, SeriesTextRecord.class, HyperlinkRecord.class, ExternalNameRecord.class, // TODO - same changes in non-@deprecated version of this class SupBookRecord.class, diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 856ada170..197139ccc 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -1554,18 +1554,14 @@ public final class HSSFSheet { } /** - * Returns the top-level drawing patriach, if there is - * one. - * This will hold any graphics or charts for the sheet. + * Returns the agregate escher records for this sheet, + * it there is one. * WARNING - calling this will trigger a parsing of the * associated escher records. Any that aren't supported * (such as charts and complex drawing types) will almost - * certainly be lost or corrupted when written out. Only - * use this with simple drawings, otherwise call - * {@link HSSFSheet#createDrawingPatriarch()} and - * start from scratch! + * certainly be lost or corrupted when written out. */ - public HSSFPatriarch getDrawingPatriarch() { + public EscherAggregate getDrawingEscherAggregate() { book.findDrawingGroup(); // If there's now no drawing manager, then there's @@ -1584,6 +1580,25 @@ public final class HSSFSheet { // Grab our aggregate record, and wire it up EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid); + return agg; + } + + /** + * Returns the top-level drawing patriach, if there is + * one. + * This will hold any graphics or charts for the sheet. + * WARNING - calling this will trigger a parsing of the + * associated escher records. Any that aren't supported + * (such as charts and complex drawing types) will almost + * certainly be lost or corrupted when written out. Only + * use this with simple drawings, otherwise call + * {@link HSSFSheet#createDrawingPatriarch()} and + * start from scratch! + */ + public HSSFPatriarch getDrawingPatriarch() { + EscherAggregate agg = getDrawingEscherAggregate(); + if(agg == null) return null; + HSSFPatriarch patriarch = new HSSFPatriarch(this, agg); agg.setPatriarch(patriarch); diff --git a/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFChart.java b/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFChart.java index d708a5c1d..5b4850193 100644 --- a/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFChart.java +++ b/src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFChart.java @@ -19,14 +19,58 @@ package org.apache.poi.hssf.usermodel; -import org.apache.poi.hssf.record.*; -import org.apache.poi.hssf.record.formula.Area3DPtg; - import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Stack; +import org.apache.poi.hssf.record.AreaFormatRecord; +import org.apache.poi.hssf.record.AxisLineFormatRecord; +import org.apache.poi.hssf.record.AxisOptionsRecord; +import org.apache.poi.hssf.record.AxisParentRecord; +import org.apache.poi.hssf.record.AxisRecord; +import org.apache.poi.hssf.record.AxisUsedRecord; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BarRecord; +import org.apache.poi.hssf.record.BeginRecord; +import org.apache.poi.hssf.record.CategorySeriesAxisRecord; +import org.apache.poi.hssf.record.ChartFormatRecord; +import org.apache.poi.hssf.record.ChartRecord; +import org.apache.poi.hssf.record.ChartTitleFormatRecord; +import org.apache.poi.hssf.record.DataFormatRecord; +import org.apache.poi.hssf.record.DefaultDataLabelTextPropertiesRecord; +import org.apache.poi.hssf.record.DimensionsRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.EndRecord; +import org.apache.poi.hssf.record.FontBasisRecord; +import org.apache.poi.hssf.record.FontIndexRecord; +import org.apache.poi.hssf.record.FooterRecord; +import org.apache.poi.hssf.record.FrameRecord; +import org.apache.poi.hssf.record.HCenterRecord; +import org.apache.poi.hssf.record.HeaderRecord; +import org.apache.poi.hssf.record.LegendRecord; +import org.apache.poi.hssf.record.LineFormatRecord; +import org.apache.poi.hssf.record.LinkedDataFormulaField; +import org.apache.poi.hssf.record.LinkedDataRecord; +import org.apache.poi.hssf.record.PlotAreaRecord; +import org.apache.poi.hssf.record.PlotGrowthRecord; +import org.apache.poi.hssf.record.PrintSetupRecord; +import org.apache.poi.hssf.record.ProtectRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.SCLRecord; +import org.apache.poi.hssf.record.SeriesIndexRecord; +import org.apache.poi.hssf.record.SeriesRecord; +import org.apache.poi.hssf.record.SeriesTextRecord; +import org.apache.poi.hssf.record.SeriesToChartGroupRecord; +import org.apache.poi.hssf.record.SheetPropertiesRecord; +import org.apache.poi.hssf.record.TextRecord; +import org.apache.poi.hssf.record.TickRecord; +import org.apache.poi.hssf.record.UnitsRecord; +import org.apache.poi.hssf.record.UnknownRecord; +import org.apache.poi.hssf.record.VCenterRecord; +import org.apache.poi.hssf.record.ValueRangeRecord; +import org.apache.poi.hssf.record.formula.Area3DPtg; + /** * Has methods for construction of a chart object. * @@ -35,11 +79,13 @@ import java.util.Stack; public class HSSFChart { private ChartRecord chartRecord; - private SeriesRecord seriesRecord; + private LegendRecord legendRecord; private ChartTitleFormatRecord chartTitleFormat; private SeriesTextRecord chartTitleText; + private List series = new ArrayList(); + private HSSFChart(ChartRecord chartRecord) { this.chartRecord = chartRecord; } @@ -121,8 +167,8 @@ public class HSSFChart /** * Returns all the charts for the given sheet. * - * NOTE: Does not yet work... checking it in just so others - * can take a look. + * NOTE: You won't be able to do very much with + * these charts yet, as this is very limited support */ public static HSSFChart[] getSheetCharts(HSSFSheet sheet) { List charts = new ArrayList(); @@ -132,33 +178,49 @@ public class HSSFChart List records = sheet.getSheet().getRecords(); for(Iterator it = records.iterator(); it.hasNext();) { Record r = (Record)it.next(); - System.err.println(r); - - if(r instanceof DrawingRecord) { - DrawingRecord dr = (DrawingRecord)r; - } if(r instanceof ChartRecord) { lastChart = new HSSFChart((ChartRecord)r); charts.add(lastChart); } + if(r instanceof LegendRecord) { + lastChart.legendRecord = (LegendRecord)r; + } if(r instanceof SeriesRecord) { - lastChart.seriesRecord = (SeriesRecord)r; + HSSFSeries series = lastChart.new HSSFSeries( (SeriesRecord)r ); + lastChart.series.add(series); } if(r instanceof ChartTitleFormatRecord) { lastChart.chartTitleFormat = (ChartTitleFormatRecord)r; } if(r instanceof SeriesTextRecord) { - lastChart.chartTitleText = - (SeriesTextRecord)r; + // Applies to a series, unless we've seen + // a legend already + SeriesTextRecord str = (SeriesTextRecord)r; + if(lastChart.legendRecord == null && + lastChart.series.size() > 0) { + HSSFSeries series = (HSSFSeries) + lastChart.series.get(lastChart.series.size()-1); + series.seriesTitleText = str; + } else { + lastChart.chartTitleText = str; + } } } return (HSSFChart[]) charts.toArray( new HSSFChart[charts.size()] ); } + + /** + * Returns the series of the chart + */ + public HSSFSeries[] getSeries() { + return (HSSFSeries[]) + series.toArray(new HSSFSeries[series.size()]); + } /** * Returns the chart's title, if there is one, @@ -184,7 +246,6 @@ public class HSSFChart } } - private EOFRecord createEOFRecord() { @@ -858,4 +919,51 @@ public class HSSFChart r.setUnits( (short) 0 ); return r; } + + + /** + * A series in a chart + */ + public class HSSFSeries { + private SeriesRecord series; + private SeriesTextRecord seriesTitleText; + + private HSSFSeries(SeriesRecord series) { + this.series = series; + } + + public short getNumValues() { + return series.getNumValues(); + } + /** + * See {@link SeriesRecord} + */ + public short getValueType() { + return series.getValuesDataType(); + } + + /** + * Returns the series' title, if there is one, + * or null if not + */ + public String getSeriesTitle() { + if(seriesTitleText != null) { + return seriesTitleText.getText(); + } + return null; + } + + /** + * Changes the series' title, but only if there + * was one already. + * TODO - add in the records if not + */ + public void setSeriesTitle(String title) { + if(seriesTitleText != null) { + seriesTitleText.setText(title); + } else { + throw new IllegalStateException("No series title found to change"); + } + } + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestHSSFChart.java b/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestHSSFChart.java index 2a069c3ff..184d46d2f 100644 --- a/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestHSSFChart.java +++ b/src/scratchpad/testcases/org/apache/poi/hssf/usermodel/TestHSSFChart.java @@ -19,6 +19,8 @@ package org.apache.poi.hssf.usermodel; import java.io.File; import java.io.FileInputStream; +import org.apache.poi.hssf.record.SeriesRecord; + import junit.framework.TestCase; public class TestHSSFChart extends TestCase { @@ -29,14 +31,65 @@ public class TestHSSFChart extends TestCase { } public void testSingleChart() throws Exception { + HSSFWorkbook wb = new HSSFWorkbook( + new FileInputStream(new File(dirName, "WithChart.xls")) + ); + HSSFSheet s1 = wb.getSheetAt(0); + HSSFSheet s2 = wb.getSheetAt(1); + HSSFSheet s3 = wb.getSheetAt(2); + + assertEquals(0, HSSFChart.getSheetCharts(s1).length); + assertEquals(1, HSSFChart.getSheetCharts(s2).length); + assertEquals(0, HSSFChart.getSheetCharts(s3).length); + + HSSFChart[] charts; + + // Check the chart on the 2nd sheet + charts = HSSFChart.getSheetCharts(s2); + assertEquals(1, charts.length); + + assertEquals(2, charts[0].getSeries().length); + assertEquals("1st Column", charts[0].getSeries()[0].getSeriesTitle()); + assertEquals("2nd Column", charts[0].getSeries()[1].getSeriesTitle()); + assertEquals(null, charts[0].getChartTitle()); } public void testTwoCharts() throws Exception { + HSSFWorkbook wb = new HSSFWorkbook( + new FileInputStream(new File(dirName, "WithTwoCharts.xls")) + ); + HSSFSheet s1 = wb.getSheetAt(0); + HSSFSheet s2 = wb.getSheetAt(1); + HSSFSheet s3 = wb.getSheetAt(2); + + assertEquals(0, HSSFChart.getSheetCharts(s1).length); + assertEquals(1, HSSFChart.getSheetCharts(s2).length); + assertEquals(1, HSSFChart.getSheetCharts(s3).length); + + HSSFChart[] charts; + + // Check the chart on the 2nd sheet + charts = HSSFChart.getSheetCharts(s2); + assertEquals(1, charts.length); + + assertEquals(2, charts[0].getSeries().length); + assertEquals("1st Column", charts[0].getSeries()[0].getSeriesTitle()); + assertEquals("2nd Column", charts[0].getSeries()[1].getSeriesTitle()); + assertEquals(null, charts[0].getChartTitle()); + + // And the third sheet + charts = HSSFChart.getSheetCharts(s3); + assertEquals(1, charts.length); + + assertEquals(2, charts[0].getSeries().length); + assertEquals("Squares", charts[0].getSeries()[0].getSeriesTitle()); + assertEquals("Base Numbers", charts[0].getSeries()[1].getSeriesTitle()); + assertEquals(null, charts[0].getChartTitle()); } - - public void BROKENtestThreeCharts() throws Exception { + + public void testThreeCharts() throws Exception { HSSFWorkbook wb = new HSSFWorkbook( new FileInputStream(new File(dirName, "WithThreeCharts.xls")) ); @@ -51,11 +104,30 @@ public class TestHSSFChart extends TestCase { HSSFChart[] charts; + // Check the charts on the 2nd sheet charts = HSSFChart.getSheetCharts(s2); - assertNull(charts[0].getChartTitle()); + assertEquals(2, charts.length); + + assertEquals(2, charts[0].getSeries().length); + assertEquals("1st Column", charts[0].getSeries()[0].getSeriesTitle()); + assertEquals("2nd Column", charts[0].getSeries()[1].getSeriesTitle()); + assertEquals(6, charts[0].getSeries()[0].getNumValues()); + assertEquals(6, charts[0].getSeries()[1].getNumValues()); + assertEquals(SeriesRecord.CATEGORY_DATA_TYPE_NUMERIC, charts[0].getSeries()[0].getValueType()); + assertEquals(SeriesRecord.CATEGORY_DATA_TYPE_NUMERIC, charts[0].getSeries()[1].getValueType()); + assertEquals(null, charts[0].getChartTitle()); + + assertEquals(1, charts[1].getSeries().length); + assertEquals(null, charts[1].getSeries()[0].getSeriesTitle()); assertEquals("Pie Chart Title Thingy", charts[1].getChartTitle()); + // And the third sheet charts = HSSFChart.getSheetCharts(s3); - assertEquals("Sheet 3 Chart with Title", charts[1].getChartTitle()); + assertEquals(1, charts.length); + + assertEquals(2, charts[0].getSeries().length); + assertEquals("Squares", charts[0].getSeries()[0].getSeriesTitle()); + assertEquals("Base Numbers", charts[0].getSeries()[1].getSeriesTitle()); + assertEquals("Sheet 3 Chart with Title", charts[0].getChartTitle()); } }