<!-- Don't forget to update status.xml too! -->
<release version="3.0.3-beta1" date="2008-04-??">
+ <action dev="POI-DEVELOPERS" type="add">Initial support for getting and changing chart and series titles</action>
+ <action dev="POI-DEVELOPERS" type="add">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</action>
<action dev="POI-DEVELOPERS" type="fix">44792 - fixed encode/decode problems in ExternalNameRecord and CRNRecord.</action>
<action dev="POI-DEVELOPERS" type="fix">43670, 44501 - Fix how HDGF deals with trailing data in the list of chunk headers</action>
<action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action>
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.0.3-beta1" date="2008-04-??">
+ <action dev="POI-DEVELOPERS" type="add">Initial support for getting and changing chart and series titles</action>
+ <action dev="POI-DEVELOPERS" type="add">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</action>
<action dev="POI-DEVELOPERS" type="fix">44792 - fixed encode/decode problems in ExternalNameRecord and CRNRecord.</action>
<action dev="POI-DEVELOPERS" type="fix">43670, 44501 - Fix how HDGF deals with trailing data in the list of chunk headers</action>
<action dev="POI-DEVELOPERS" type="add">30311 - More work on Conditional Formatting</action>
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.
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";
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,
}
/**
- * 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
// 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);
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.
*
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;
}
/**
* 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();
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,
}
}
-
private EOFRecord createEOFRecord()
{
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");
+ }
+ }
+ }
}
import java.io.File;
import java.io.FileInputStream;
+import org.apache.poi.hssf.record.SeriesRecord;
+
import junit.framework.TestCase;
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"))
);
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());
}
}