aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/poi/hssf
diff options
context:
space:
mode:
authorGlen Stampoultzis <glens@apache.org>2004-04-09 11:45:38 +0000
committerGlen Stampoultzis <glens@apache.org>2004-04-09 11:45:38 +0000
commitb6ea214cc1e42332b7198954c63a783935b4bdf4 (patch)
tree9ffdd5aea352c9c258ed2df5fa1a4deb4d63792e /src/java/org/apache/poi/hssf
parent0873a633af2dc09fd9eab9ee1b2d5835e0d7fc2d (diff)
downloadpoi-b6ea214cc1e42332b7198954c63a783935b4bdf4.tar.gz
poi-b6ea214cc1e42332b7198954c63a783935b4bdf4.zip
Ported the drawing stuff from the rel_2_branch. Given the effort this took I'm really really wanting to move to subversion.
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@353542 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/poi/hssf')
-rw-r--r--src/java/org/apache/poi/hssf/dev/BiffViewer.java163
-rw-r--r--src/java/org/apache/poi/hssf/model/AbstractShape.java126
-rw-r--r--src/java/org/apache/poi/hssf/model/ConvertAnchor.java50
-rw-r--r--src/java/org/apache/poi/hssf/model/DrawingManager.java136
-rw-r--r--src/java/org/apache/poi/hssf/model/LineShape.java105
-rw-r--r--src/java/org/apache/poi/hssf/model/PolygonShape.java143
-rw-r--r--src/java/org/apache/poi/hssf/model/Sheet.java232
-rw-r--r--src/java/org/apache/poi/hssf/model/SimpleFilledShape.java111
-rw-r--r--src/java/org/apache/poi/hssf/model/TestDrawingManager.java81
-rw-r--r--src/java/org/apache/poi/hssf/model/TextboxShape.java151
-rw-r--r--src/java/org/apache/poi/hssf/model/Workbook.java52
-rw-r--r--src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java266
-rw-r--r--src/java/org/apache/poi/hssf/record/CommonObjectDataSubRecord.java498
-rw-r--r--src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java30
-rw-r--r--src/java/org/apache/poi/hssf/record/DrawingRecord.java97
-rw-r--r--src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java35
-rw-r--r--src/java/org/apache/poi/hssf/record/DrawingSelectionRecord.java30
-rw-r--r--src/java/org/apache/poi/hssf/record/EndSubRecord.java176
-rw-r--r--src/java/org/apache/poi/hssf/record/EscherAggregate.java696
-rw-r--r--src/java/org/apache/poi/hssf/record/GroupMarkerSubRecord.java176
-rw-r--r--src/java/org/apache/poi/hssf/record/HorizontalPageBreakRecord.java107
-rw-r--r--src/java/org/apache/poi/hssf/record/ObjRecord.java227
-rw-r--r--src/java/org/apache/poi/hssf/record/PageBreakRecord.java304
-rw-r--r--src/java/org/apache/poi/hssf/record/RecordFactory.java10
-rw-r--r--src/java/org/apache/poi/hssf/record/SubRecord.java98
-rw-r--r--src/java/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java127
-rw-r--r--src/java/org/apache/poi/hssf/record/TestDrawingGroupRecord.java36
-rw-r--r--src/java/org/apache/poi/hssf/record/TestEndSubRecord.java102
-rw-r--r--src/java/org/apache/poi/hssf/record/TestEscherAggregate.java124
-rw-r--r--src/java/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java137
-rw-r--r--src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java519
-rw-r--r--src/java/org/apache/poi/hssf/record/TextObjectRecord.java216
-rw-r--r--src/java/org/apache/poi/hssf/record/UnknownRecord.java7
-rw-r--r--src/java/org/apache/poi/hssf/record/VerticalPageBreakRecord.java107
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java773
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java450
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java567
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/FontDetails.java143
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java40
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java37
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java207
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFFont.java8
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java55
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java159
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java66
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java179
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFShape.java195
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java17
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java148
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java161
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java64
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java107
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java34
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java81
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics.java71
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics2d.java74
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestFontDetails.java45
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestHSSFClientAnchor.java44
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestHSSFRichTextString.java50
59 files changed, 9164 insertions, 86 deletions
diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java
index da65d5920c..06e2a24658 100644
--- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java
+++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java
@@ -2,7 +2,7 @@
* ====================================================================
* The Apache Software License, Version 1.1
*
- * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -107,7 +107,7 @@ public class BiffViewer {
new POIFSFileSystem(new FileInputStream(filename));
InputStream stream =
fs.createDocumentInputStream("Workbook");
- Record[] records = createRecords(stream, dump);
+ createRecords(stream, dump);
} catch (Exception e) {
e.printStackTrace();
}
@@ -128,17 +128,20 @@ public class BiffViewer {
public static Record[] createRecords(InputStream in, boolean dump)
throws RecordFormatException {
ArrayList records = new ArrayList();
- Record last_record = null;
+ //Record last_record = null;
int loc = 0;
+ RecordDetails activeRecord = null;
+
try {
// long offset = 0;
short rectype = 0;
do {
rectype = LittleEndian.readShort(in);
- System.out.println("============================================");
- System.out.println("Offset 0x" + Integer.toHexString(loc) + " (" + loc + ")");
+ int startloc = loc;
+// System.out.println("============================================");
+// System.out.println("Offset 0x" + Integer.toHexString(loc) + " (" + loc + ")");
loc += 2;
if (rectype != 0) {
short recsize = LittleEndian.readShort(in);
@@ -147,36 +150,27 @@ public class BiffViewer {
byte[] data = new byte[(int) recsize];
in.read(data);
- if ((rectype == WSBoolRecord.sid) && (recsize == 0)) {
- System.out.println(loc);
- }
loc += recsize;
-// offset += 4 + recsize;
- if (dump) {
- dump(rectype, recsize, data);
+ Record record = createRecord(rectype, recsize, data );
+ if (record.getSid() != ContinueRecord.sid)
+ {
+ records.add(record);
+ if (activeRecord != null)
+ activeRecord.dump();
+ activeRecord = new RecordDetails(rectype, recsize, startloc, data, record);
}
- Record[] recs = createRecord(rectype, recsize,
- data);
- // handle MulRK records
-
- Record record = recs[0];
-
- if ((record instanceof UnknownRecord)
- && !dump) {
- // if we didn't already dump
- // just cause dump was on and we're hit an unknow
- dumpUnknownRecord(data);
+ else
+ {
+ activeRecord.getRecord().processContinueRecord(data);
}
- if (record != null) {
- if (rectype == ContinueRecord.sid) {
- dumpContinueRecord(last_record, dump, data);
- } else {
- last_record = record;
- records.add(record);
- }
+ if (dump) {
+ dumpRaw(rectype, recsize, data);
}
}
} while (rectype != 0);
+
+ activeRecord.dump();
+
} catch (IOException e) {
throw new RecordFormatException("Error reading bytes");
}
@@ -186,15 +180,14 @@ public class BiffViewer {
return retval;
}
+ private static void dumpNormal(Record record, int startloc, short rectype, short recsize)
+ {
+ System.out.println("Offset 0x" + Integer.toHexString(startloc) + " (" + startloc + ")");
+ System.out.println( "recordid = 0x" + Integer.toHexString( rectype ) + ", size = " + recsize );
+ System.out.println( record.toString() );
+ }
+
- /**
- * Description of the Method
- *
- *@param last_record Description of the Parameter
- *@param dump Description of the Parameter
- *@param data Description of the Parameter
- *@exception IOException Description of the Exception
- */
private static void dumpContinueRecord(Record last_record, boolean dump, byte[] data) throws IOException {
if (last_record == null) {
throw new RecordFormatException(
@@ -226,12 +219,6 @@ public class BiffViewer {
}
- /**
- * Description of the Method
- *
- *@param data Description of the Parameter
- *@exception IOException Description of the Exception
- */
private static void dumpUnknownRecord(byte[] data) throws IOException {
// record hex dump it!
System.out.println(
@@ -247,10 +234,11 @@ public class BiffViewer {
}
- private static void dump( short rectype, short recsize, byte[] data ) throws IOException
+ private static void dumpRaw( short rectype, short recsize, byte[] data ) throws IOException
{
// System.out
// .println("fixing to recordize the following");
+ System.out.println("============================================");
System.out.print( "rectype = 0x"
+ Integer.toHexString( rectype ) );
System.out.println( ", recsize = 0x"
@@ -275,13 +263,9 @@ public class BiffViewer {
* Essentially a duplicate of RecordFactory. Kept seperate as not to screw
* up non-debug operations.
*
- *@param rectype Description of the Parameter
- *@param size Description of the Parameter
- *@param data Description of the Parameter
- *@return Description of the Return Value
*/
- private static Record[] createRecord( short rectype, short size,
+ private static Record createRecord( short rectype, short size,
byte[] data )
{
Record retval = null;
@@ -429,6 +413,15 @@ public class BiffViewer {
case GridsetRecord.sid:
retval = new GridsetRecord( rectype, size, data );
break;
+ case DrawingGroupRecord.sid:
+ retval = new DrawingGroupRecord( rectype, size, data );
+ break;
+ case DrawingRecordForBiffViewer.sid:
+ retval = new DrawingRecordForBiffViewer( rectype, size, data );
+ break;
+ case DrawingSelectionRecord.sid:
+ retval = new DrawingSelectionRecord( rectype, size, data );
+ break;
case GutsRecord.sid:
retval = new GutsRecord( rectype, size, data );
break;
@@ -631,19 +624,24 @@ public class BiffViewer {
retval = new PaneRecord( rectype, size, data );
break;
case SharedFormulaRecord.sid:
- retval = new SharedFormulaRecord( rectype, size, data);
- break;
+ retval = new SharedFormulaRecord( rectype, size, data);
+ break;
+ case ObjRecord.sid:
+ retval = new ObjRecord( rectype, size, data);
+ break;
+ case TextObjectRecord.sid:
+ retval = new TextObjectRecord( rectype, size, data);
+ break;
+ case HorizontalPageBreakRecord.sid:
+ retval = new HorizontalPageBreakRecord( rectype, size, data);
+ break;
+ case VerticalPageBreakRecord.sid:
+ retval = new VerticalPageBreakRecord( rectype, size, data);
+ break;
default:
retval = new UnknownRecord( rectype, size, data );
}
- if ( realretval == null )
- {
- realretval = new Record[1];
- realretval[0] = retval;
- System.out.println( "recordid = 0x" + Integer.toHexString( rectype ) + ", size =" + size );
- System.out.println( realretval[0].toString() );
- }
- return realretval;
+ return retval;
}
@@ -674,6 +672,7 @@ public class BiffViewer {
public static void main(String[] args) {
try {
+ System.setProperty("poi.deserialize.escher", "true");
BiffViewer viewer = new BiffViewer(args);
if ((args.length > 1) && args[1].equals("on")) {
@@ -696,4 +695,50 @@ public class BiffViewer {
e.printStackTrace();
}
}
+
+ static class RecordDetails
+ {
+ short rectype, recsize;
+ int startloc;
+ byte[] data;
+ Record record;
+
+ public RecordDetails( short rectype, short recsize, int startloc, byte[] data, Record record )
+ {
+ this.rectype = rectype;
+ this.recsize = recsize;
+ this.startloc = startloc;
+ this.data = data;
+ this.record = record;
+ }
+
+ public short getRectype()
+ {
+ return rectype;
+ }
+
+ public short getRecsize()
+ {
+ return recsize;
+ }
+
+ public byte[] getData()
+ {
+ return data;
+ }
+
+ public Record getRecord()
+ {
+ return record;
+ }
+
+ public void dump() throws IOException
+ {
+ if (record instanceof UnknownRecord)
+ dumpUnknownRecord(data);
+ else
+ dumpNormal(record, startloc, rectype, recsize);
+ }
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/model/AbstractShape.java b/src/java/org/apache/poi/hssf/model/AbstractShape.java
new file mode 100644
index 0000000000..522220184f
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/AbstractShape.java
@@ -0,0 +1,126 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.usermodel.*;
+
+/**
+ * An abstract shape is the lowlevel model for a shape.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public abstract class AbstractShape
+{
+ /**
+ * Create a new shape object used to create the escher records.
+ *
+ * @param hssfShape The simple shape this is based on.
+ */
+ public static AbstractShape createShape( HSSFShape hssfShape, int shapeId )
+ {
+ AbstractShape shape = null;
+ if (hssfShape instanceof HSSFTextbox)
+ {
+ shape = new TextboxShape( (HSSFTextbox)hssfShape, shapeId );
+ }
+ else if (hssfShape instanceof HSSFPolygon)
+ {
+ shape = new PolygonShape( (HSSFPolygon) hssfShape, shapeId );
+ }
+ else if (hssfShape instanceof HSSFSimpleShape)
+ {
+ HSSFSimpleShape simpleShape = (HSSFSimpleShape) hssfShape;
+ switch ( simpleShape.getShapeType() )
+ {
+ case HSSFSimpleShape.OBJECT_TYPE_LINE:
+ shape = new LineShape( simpleShape, shapeId );
+ break;
+ case HSSFSimpleShape.OBJECT_TYPE_OVAL:
+ case HSSFSimpleShape.OBJECT_TYPE_RECTANGLE:
+ shape = new SimpleFilledShape( simpleShape, shapeId );
+ break;
+ default:
+ throw new IllegalArgumentException("Do not know how to handle this type of shape");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown shape type");
+ }
+ EscherSpRecord sp = shape.getSpContainer().getChildById(EscherSpRecord.RECORD_ID);
+ if (hssfShape.getParent() != null)
+ sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_CHILD);
+ return shape;
+ }
+
+ protected AbstractShape()
+ {
+ }
+
+ /**
+ * @return The shape container and it's children that can represent this
+ * shape.
+ */
+ public abstract EscherContainerRecord getSpContainer();
+
+ /**
+ * @return The object record that is associated with this shape.
+ */
+ public abstract ObjRecord getObjRecord();
+
+ /**
+ * Creates an escher anchor record from a HSSFAnchor.
+ *
+ * @param userAnchor The high level anchor to convert.
+ * @return An escher anchor record.
+ */
+ protected EscherRecord createAnchor( HSSFAnchor userAnchor )
+ {
+ return ConvertAnchor.createAnchor(userAnchor);
+ }
+
+ /**
+ * Add standard properties to the opt record. These properties effect
+ * all records.
+ *
+ * @param shape The user model shape.
+ * @param opt The opt record to add the properties to.
+ * @return The number of options added.
+ */
+ protected int addStandardOptions( HSSFShape shape, EscherOptRecord opt )
+ {
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x080000 ) );
+// opt.addEscherProperty( new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x080008 ) );
+ if ( shape.isNoFill() )
+ {
+ // Wonderful... none of the spec's give any clue as to what these constants mean.
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.FILL__NOFILLHITTEST, 0x00110000 ) );
+ }
+ else
+ {
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.FILL__NOFILLHITTEST, 0x00010000 ) );
+ }
+ opt.addEscherProperty( new EscherRGBProperty( EscherProperties.FILL__FILLCOLOR, shape.getFillColor() ) );
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.GROUPSHAPE__PRINT, 0x080000 ) );
+ opt.addEscherProperty( new EscherRGBProperty( EscherProperties.LINESTYLE__COLOR, shape.getLineStyleColor() ) );
+ int options = 5;
+ if (shape.getLineWidth() != HSSFShape.LINEWIDTH_DEFAULT)
+ {
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.LINESTYLE__LINEWIDTH, shape.getLineWidth()));
+ options++;
+ }
+ if (shape.getLineStyle() != HSSFShape.LINESTYLE_SOLID)
+ {
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.LINESTYLE__LINEDASHING, shape.getLineStyle()));
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.LINESTYLE__LINEENDCAPSTYLE, 0));
+ if (shape.getLineStyle() == HSSFShape.LINESTYLE_NONE)
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080000));
+ else
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080008));
+ options += 3;
+ }
+ opt.sortProperties();
+ return options; // # options added
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/ConvertAnchor.java b/src/java/org/apache/poi/hssf/model/ConvertAnchor.java
new file mode 100644
index 0000000000..bb07a121a8
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/ConvertAnchor.java
@@ -0,0 +1,50 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherClientAnchorRecord;
+import org.apache.poi.ddf.EscherChildAnchorRecord;
+import org.apache.poi.hssf.usermodel.HSSFAnchor;
+import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
+import org.apache.poi.hssf.usermodel.HSSFChildAnchor;
+
+/**
+ * $Id$
+ */
+public class ConvertAnchor
+{
+ public static EscherRecord createAnchor( HSSFAnchor userAnchor )
+ {
+ if (userAnchor instanceof HSSFClientAnchor)
+ {
+ HSSFClientAnchor a = (HSSFClientAnchor) userAnchor;
+
+ EscherClientAnchorRecord anchor = new EscherClientAnchorRecord();
+ anchor.setRecordId( EscherClientAnchorRecord.RECORD_ID );
+ anchor.setOptions( (short) 0x0000 );
+ anchor.setFlag( (short) 0 );
+ anchor.setCol1( (short) Math.min(a.getCol1(), a.getCol2()) );
+ anchor.setDx1( (short) Math.min(a.getDx1(), a.getDx2()) );
+ anchor.setRow1( (short) Math.min(a.getRow1(), a.getRow2()) );
+ anchor.setDy1( (short) Math.min(a.getDy1(), a.getDy2()) );
+
+ anchor.setCol2( (short) Math.max(a.getCol1(), a.getCol2()) );
+ anchor.setDx2( (short) Math.max(a.getDx1(), a.getDx2()) );
+ anchor.setRow2( (short) Math.max(a.getRow1(), a.getRow2()) );
+ anchor.setDy2( (short) Math.max(a.getDy1(), a.getDy2() ) );
+ return anchor;
+ }
+ else
+ {
+ HSSFChildAnchor a = (HSSFChildAnchor) userAnchor;
+ EscherChildAnchorRecord anchor = new EscherChildAnchorRecord();
+ anchor.setRecordId( EscherChildAnchorRecord.RECORD_ID );
+ anchor.setOptions( (short) 0x0000 );
+ anchor.setDx1( (short) Math.min(a.getDx1(), a.getDx2()) );
+ anchor.setDy1( (short) Math.min(a.getDy1(), a.getDy2()) );
+ anchor.setDx2( (short) Math.max(a.getDx2(), a.getDx1()) );
+ anchor.setDy2( (short) Math.max(a.getDy2(), a.getDy1()) );
+ return anchor;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/DrawingManager.java b/src/java/org/apache/poi/hssf/model/DrawingManager.java
new file mode 100644
index 0000000000..585f6b90b3
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/DrawingManager.java
@@ -0,0 +1,136 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherDgRecord;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Provides utilities to manage drawing groups.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class DrawingManager
+{
+ EscherDggRecord dgg;
+ Map dgMap = new HashMap(); // key = Short(drawingId), value=EscherDgRecord
+
+ public DrawingManager( EscherDggRecord dgg )
+ {
+ this.dgg = dgg;
+ }
+
+ public EscherDgRecord createDgRecord()
+ {
+ EscherDgRecord dg = new EscherDgRecord();
+ dg.setRecordId( EscherDgRecord.RECORD_ID );
+ short dgId = findNewDrawingGroupId();
+ dg.setOptions( (short) ( dgId << 4 ) );
+ dg.setNumShapes( 0 );
+ dg.setLastMSOSPID( -1 );
+ dgg.addCluster( dgId, 0 );
+ dgg.setDrawingsSaved( dgg.getDrawingsSaved() + 1 );
+ dgMap.put( new Short( dgId ), dg );
+ return dg;
+ }
+
+ /**
+ * Allocates new shape id for the new drawing group id.
+ *
+ * @return a new shape id.
+ */
+ public int allocateShapeId(short drawingGroupId)
+ {
+ // Get the last shape id for this drawing group.
+ EscherDgRecord dg = (EscherDgRecord) dgMap.get(new Short(drawingGroupId));
+ int lastShapeId = dg.getLastMSOSPID();
+
+
+ // Have we run out of shapes for this cluster?
+ int newShapeId = 0;
+ if (lastShapeId % 1024 == 1023)
+ {
+ // Yes:
+ // Find the starting shape id of the next free cluster
+ newShapeId = findFreeSPIDBlock();
+ // Create a new cluster in the dgg record.
+ dgg.addCluster(drawingGroupId, 1);
+ }
+ else
+ {
+ // No:
+ // Find the cluster for this drawing group with free space.
+ for (int i = 0; i < dgg.getFileIdClusters().length; i++)
+ {
+ EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
+ if (c.getDrawingGroupId() == drawingGroupId)
+ {
+ if (c.getNumShapeIdsUsed() != 1024)
+ {
+ // Increment the number of shapes used for this cluster.
+ c.incrementShapeId();
+ }
+ }
+ // If the last shape id = -1 then we know to find a free block;
+ if (dg.getLastMSOSPID() == -1)
+ {
+ newShapeId = findFreeSPIDBlock();
+ }
+ else
+ {
+ // The new shape id to be the last shapeid of this cluster + 1
+ newShapeId = dg.getLastMSOSPID() + 1;
+ }
+ }
+ }
+ // Increment the total number of shapes used in the dgg.
+ dgg.setNumShapesSaved(dgg.getNumShapesSaved() + 1);
+ // Is the new shape id >= max shape id for dgg?
+ if (newShapeId >= dgg.getShapeIdMax())
+ {
+ // Yes:
+ // Set the max shape id = new shape id + 1
+ dgg.setShapeIdMax(newShapeId + 1);
+ }
+ // Set last shape id for this drawing group.
+ dg.setLastMSOSPID(newShapeId);
+ // Increased the number of shapes used for this drawing group.
+ dg.incrementShapeCount();
+
+
+ return newShapeId;
+ }
+
+ //////////// Non-public methods /////////////
+ short findNewDrawingGroupId()
+ {
+ short dgId = 1;
+ while ( drawingGroupExists( dgId ) )
+ dgId++;
+ return dgId;
+ }
+
+ boolean drawingGroupExists( short dgId )
+ {
+ for ( int i = 0; i < dgg.getFileIdClusters().length; i++ )
+ {
+ if ( dgg.getFileIdClusters()[i].getDrawingGroupId() == dgId )
+ return true;
+ }
+ return false;
+ }
+
+ int findFreeSPIDBlock()
+ {
+ int max = dgg.getShapeIdMax();
+ int next = ( ( max / 1024 ) + 1 ) * 1024;
+ return next;
+ }
+
+ public EscherDggRecord getDgg()
+ {
+ return dgg;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/LineShape.java b/src/java/org/apache/poi/hssf/model/LineShape.java
new file mode 100644
index 0000000000..0b4e4d1e51
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/LineShape.java
@@ -0,0 +1,105 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.usermodel.*;
+
+/**
+ * Represents a line shape and creates all the line specific low level records.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class LineShape
+ extends AbstractShape
+{
+ private EscherContainerRecord spContainer;
+ private ObjRecord objRecord;
+
+ /**
+ * Creates the line shape from the highlevel user shape. All low level
+ * records are created at this point.
+ *
+ * @param hssfShape The user model shape.
+ * @param shapeId The identifier to use for this shape.
+ */
+ LineShape( HSSFSimpleShape hssfShape, int shapeId )
+ {
+ spContainer = createSpContainer(hssfShape, shapeId);
+ objRecord = createObjRecord(hssfShape, shapeId);
+ }
+
+ /**
+ * Creates the lowerlevel escher records for this shape.
+ */
+ private EscherContainerRecord createSpContainer(HSSFSimpleShape hssfShape, int shapeId)
+ {
+ HSSFShape shape = hssfShape;
+
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherRecord anchor = new EscherClientAnchorRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ sp.setOptions( (short) ( (EscherAggregate.ST_LINE << 4) | 0x2 ) );
+
+ sp.setShapeId( shapeId );
+ sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ opt.addEscherProperty( new EscherShapePathProperty( EscherProperties.GEOMETRY__SHAPEPATH, EscherShapePathProperty.COMPLEX ) );
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 1048592 ) );
+ addStandardOptions(shape, opt);
+ HSSFAnchor userAnchor = shape.getAnchor();
+ if (userAnchor.isHorizontallyFlipped())
+ sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPHORIZ);
+ if (userAnchor.isVerticallyFlipped())
+ sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPVERT);
+ anchor = createAnchor(userAnchor);
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+
+ spContainer.addChildRecord(sp);
+ spContainer.addChildRecord(opt);
+ spContainer.addChildRecord(anchor);
+ spContainer.addChildRecord(clientData);
+
+ return spContainer;
+ }
+
+ /**
+ * Creates the low level OBJ record for this shape.
+ */
+ private ObjRecord createObjRecord(HSSFShape hssfShape, int shapeId)
+ {
+ HSSFShape shape = hssfShape;
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType((short) ((HSSFSimpleShape)shape).getShapeType());
+ c.setObjectId((short) ( shapeId ));
+ c.setLocked(true);
+ c.setPrintable(true);
+ c.setAutofill(true);
+ c.setAutoline(true);
+ EndSubRecord e = new EndSubRecord();
+
+ obj.addSubRecord(c);
+ obj.addSubRecord(e);
+
+ return obj;
+ }
+
+ public EscherContainerRecord getSpContainer()
+ {
+ return spContainer;
+ }
+
+ public ObjRecord getObjRecord()
+ {
+ return objRecord;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/PolygonShape.java b/src/java/org/apache/poi/hssf/model/PolygonShape.java
new file mode 100644
index 0000000000..fccc92e6ac
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/PolygonShape.java
@@ -0,0 +1,143 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.record.EscherAggregate;
+import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
+import org.apache.poi.hssf.record.EndSubRecord;
+import org.apache.poi.hssf.usermodel.HSSFSimpleShape;
+import org.apache.poi.hssf.usermodel.HSSFShape;
+import org.apache.poi.hssf.usermodel.HSSFPolygon;
+import org.apache.poi.util.LittleEndian;
+
+public class PolygonShape
+ extends AbstractShape
+{
+ public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30;
+
+ private EscherContainerRecord spContainer;
+ private ObjRecord objRecord;
+
+ /**
+ * Creates the low evel records for an polygon.
+ *
+ * @param hssfShape The highlevel shape.
+ * @param shapeId The shape id to use for this shape.
+ */
+ PolygonShape( HSSFPolygon hssfShape, int shapeId )
+ {
+ spContainer = createSpContainer( hssfShape, shapeId );
+ objRecord = createObjRecord( hssfShape, shapeId );
+ }
+
+ /**
+ * Generates the shape records for this shape.
+ *
+ */
+ private EscherContainerRecord createSpContainer( HSSFPolygon hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ sp.setOptions( (short) ( ( EscherAggregate.ST_DONUT << 4 ) | 0x2 ) );
+ sp.setShapeId( shapeId );
+ if (hssfShape.getParent() == null)
+ sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ else
+ sp.setFlags( EscherSpRecord.FLAG_CHILD | EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.TRANSFORM__ROTATION, false, false, 0));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__RIGHT, false, false, hssfShape.getDrawAreaWidth()));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__BOTTOM, false, false, hssfShape.getDrawAreaHeight()));
+ opt.addEscherProperty(new EscherShapePathProperty(EscherProperties.GEOMETRY__SHAPEPATH, EscherShapePathProperty.COMPLEX));
+ EscherArrayProperty verticesProp = new EscherArrayProperty(EscherProperties.GEOMETRY__VERTICES, false, new byte[0] );
+ verticesProp.setNumberOfElementsInArray(hssfShape.getXPoints().length+1);
+ verticesProp.setNumberOfElementsInMemory(hssfShape.getXPoints().length+1);
+ verticesProp.setSizeOfElements(0xFFF0);
+ for (int i = 0; i < hssfShape.getXPoints().length; i++)
+ {
+ byte[] data = new byte[4];
+ LittleEndian.putShort(data, 0, (short)hssfShape.getXPoints()[i]);
+ LittleEndian.putShort(data, 2, (short)hssfShape.getYPoints()[i]);
+ verticesProp.setElement(i, data);
+ }
+ int point = hssfShape.getXPoints().length;
+ byte[] data = new byte[4];
+ LittleEndian.putShort(data, 0, (short)hssfShape.getXPoints()[0]);
+ LittleEndian.putShort(data, 2, (short)hssfShape.getYPoints()[0]);
+ verticesProp.setElement(point, data);
+ opt.addEscherProperty(verticesProp);
+ EscherArrayProperty segmentsProp = new EscherArrayProperty(EscherProperties.GEOMETRY__SEGMENTINFO, false, null );
+ segmentsProp.setSizeOfElements(0x0002);
+ segmentsProp.setNumberOfElementsInArray(hssfShape.getXPoints().length * 2 + 4);
+ segmentsProp.setNumberOfElementsInMemory(hssfShape.getXPoints().length * 2 + 4);
+ segmentsProp.setElement(0, new byte[] { (byte)0x00, (byte)0x40 } );
+ segmentsProp.setElement(1, new byte[] { (byte)0x00, (byte)0xAC } );
+ for (int i = 0; i < hssfShape.getXPoints().length; i++)
+ {
+ segmentsProp.setElement(2 + i * 2, new byte[] { (byte)0x01, (byte)0x00 } );
+ segmentsProp.setElement(3 + i * 2, new byte[] { (byte)0x00, (byte)0xAC } );
+ }
+ segmentsProp.setElement(segmentsProp.getNumberOfElementsInArray() - 2, new byte[] { (byte)0x01, (byte)0x60 } );
+ segmentsProp.setElement(segmentsProp.getNumberOfElementsInArray() - 1, new byte[] { (byte)0x00, (byte)0x80 } );
+ opt.addEscherProperty(segmentsProp);
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__FILLOK, false, false, 0x00010001));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINESTARTARROWHEAD, false, false, 0x0));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEENDARROWHEAD, false, false, 0x0));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEENDCAPSTYLE, false, false, 0x0));
+
+ addStandardOptions(shape, opt);
+
+ EscherRecord anchor = createAnchor( shape.getAnchor() );
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+
+ spContainer.addChildRecord( sp );
+ spContainer.addChildRecord( opt );
+ spContainer.addChildRecord( anchor );
+ spContainer.addChildRecord( clientData );
+
+ return spContainer;
+ }
+
+ /**
+ * Creates the low level OBJ record for this shape.
+ */
+ private ObjRecord createObjRecord( HSSFShape hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType( OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING );
+ c.setObjectId( (short) ( shapeId ) );
+ c.setLocked( true );
+ c.setPrintable( true );
+ c.setAutofill( true );
+ c.setAutoline( true );
+ EndSubRecord e = new EndSubRecord();
+
+ obj.addSubRecord( c );
+ obj.addSubRecord( e );
+
+ return obj;
+ }
+
+ public EscherContainerRecord getSpContainer()
+ {
+ return spContainer;
+ }
+
+ public ObjRecord getObjRecord()
+ {
+ return objRecord;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java
index 456606bc2d..0adcd531aa 100644
--- a/src/java/org/apache/poi/hssf/model/Sheet.java
+++ b/src/java/org/apache/poi/hssf/model/Sheet.java
@@ -122,6 +122,8 @@ public class Sheet implements Model
private Iterator rowRecIterator = null;
protected int eofLoc = 0;
protected ProtectRecord protect = null;
+ protected PageBreakRecord rowBreaks = null;
+ protected PageBreakRecord colBreaks = null;
public static final byte PANE_LOWER_RIGHT = (byte)0;
public static final byte PANE_UPPER_RIGHT = (byte)1;
@@ -155,7 +157,7 @@ public class Sheet implements Model
*/
public static Sheet createSheet(List recs, int sheetnum, int offset)
{
- log.logFormatted(log.DEBUG,
+ log.logFormatted(POILogger.DEBUG,
"Sheet createSheet (existing file) with %",
new Integer(recs.size()));
Sheet retval = new Sheet();
@@ -170,18 +172,18 @@ public class Sheet implements Model
if (rec.getSid() == LabelRecord.sid)
{
- log.log(log.DEBUG, "Hit label record.");
+ log.log(POILogger.DEBUG, "Hit label record.");
retval.containsLabels = true;
}
else if (rec.getSid() == BOFRecord.sid)
{
bofEofNestingLevel++;
- log.log(log.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel);
+ log.log(POILogger.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel);
}
else if (rec.getSid() == EOFRecord.sid)
{
--bofEofNestingLevel;
- log.log(log.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel);
+ log.log(POILogger.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel);
if (bofEofNestingLevel == 0) {
records.add(rec);
retval.eofLoc = k;
@@ -296,6 +298,14 @@ public class Sheet implements Model
{
rec = null;
}
+ else if (rec.getSid() == PageBreakRecord.HORIZONTAL_SID)
+ {
+ retval.rowBreaks = (PageBreakRecord)rec;
+ }
+ else if (rec.getSid() == PageBreakRecord.VERTICAL_SID)
+ {
+ retval.colBreaks = (PageBreakRecord)rec;
+ }
if (rec != null)
@@ -312,7 +322,7 @@ public class Sheet implements Model
{
retval.cells = new ValueRecordsAggregate();
}
- log.log(log.DEBUG, "sheet createSheet (existing file) exited");
+ log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
return retval;
}
@@ -371,7 +381,7 @@ public class Sheet implements Model
public static Sheet createSheet(List records, int sheetnum)
{
- log.log(log.DEBUG,
+ log.log(POILogger.DEBUG,
"Sheet createSheet (exisiting file) assumed offset 0");
return createSheet(records, sheetnum, 0);
}
@@ -386,7 +396,7 @@ public class Sheet implements Model
public static Sheet createSheet()
{
- log.log(log.DEBUG, "Sheet createsheet from scratch called");
+ log.log(POILogger.DEBUG, "Sheet createsheet from scratch called");
Sheet retval = new Sheet();
ArrayList records = new ArrayList(30);
@@ -409,6 +419,10 @@ public class Sheet implements Model
(DefaultRowHeightRecord) retval.createDefaultRowHeight();
records.add( retval.defaultrowheight );
records.add( retval.createWSBool() );
+ retval.rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID);
+ records.add(retval.rowBreaks);
+ retval.colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID);
+ records.add(retval.colBreaks);
retval.header = (HeaderRecord) retval.createHeader();
records.add( retval.header );
retval.footer = (FooterRecord) retval.createFooter();
@@ -421,7 +435,7 @@ public class Sheet implements Model
(DefaultColWidthRecord) retval.createDefaultColWidth();
records.add( retval.defaultcolwidth);
retval.dims = ( DimensionsRecord ) retval.createDimensions();
- retval.dimsloc = 19;
+ retval.dimsloc = records.size()-1;
records.add(retval.dims);
records.add(retval.windowTwo = retval.createWindowTwo());
retval.setLoc(records.size() - 1);
@@ -432,7 +446,7 @@ public class Sheet implements Model
records.add(retval.protect);
records.add(retval.createEOF());
retval.records = records;
- log.log(log.DEBUG, "Sheet createsheet from scratch exit");
+ log.log(POILogger.DEBUG, "Sheet createsheet from scratch exit");
return retval;
}
@@ -576,7 +590,7 @@ public class Sheet implements Model
public void convertLabelRecords(Workbook wb)
{
- log.log(log.DEBUG, "convertLabelRecords called");
+ log.log(POILogger.DEBUG, "convertLabelRecords called");
if (containsLabels)
{
for (int k = 0; k < records.size(); k++)
@@ -600,7 +614,7 @@ public class Sheet implements Model
}
}
}
- log.log(log.DEBUG, "convertLabelRecords exit");
+ log.log(POILogger.DEBUG, "convertLabelRecords exit");
}
/**
@@ -614,8 +628,8 @@ public class Sheet implements Model
{
checkCells();
checkRows();
- log.log(log.DEBUG, "Sheet.getNumRecords");
- log.logFormatted(log.DEBUG, "returning % + % + % - 2 = %", new int[]
+ log.log(POILogger.DEBUG, "Sheet.getNumRecords");
+ log.logFormatted(POILogger.DEBUG, "returning % + % + % - 2 = %", new int[]
{
records.size(), cells.getPhysicalNumberOfCells(),
rows.getPhysicalNumberOfRows(),
@@ -638,8 +652,8 @@ public class Sheet implements Model
public void setDimensions(int firstrow, short firstcol, int lastrow,
short lastcol)
{
- log.log(log.DEBUG, "Sheet.setDimensions");
- log.log(log.DEBUG,
+ log.log(POILogger.DEBUG, "Sheet.setDimensions");
+ log.log(POILogger.DEBUG,
(new StringBuffer("firstrow")).append(firstrow)
.append("firstcol").append(firstcol).append("lastrow")
.append(lastrow).append("lastcol").append(lastcol)
@@ -2560,4 +2574,192 @@ public class Sheet implements Model
{
return protect;
}
+
+ public int aggregateDrawingRecords(DrawingManager drawingManager)
+ {
+ int loc = findFirstRecordLocBySid(DrawingRecord.sid);
+ boolean noDrawingRecordsFound = loc == -1;
+ if (noDrawingRecordsFound)
+ {
+ EscherAggregate aggregate = new EscherAggregate( drawingManager );
+ loc = findFirstRecordLocBySid(EscherAggregate.sid);
+ if (loc == -1)
+ {
+ loc = findFirstRecordLocBySid( WindowTwoRecord.sid );
+ }
+ else
+ {
+ getRecords().remove(loc);
+ }
+ getRecords().add( loc, aggregate );
+ return loc;
+ }
+ else
+ {
+ List records = getRecords();
+ EscherAggregate r = EscherAggregate.createAggregate( records, loc, drawingManager );
+ int startloc = loc;
+ while ( loc + 1 < records.size()
+ && records.get( loc ) instanceof DrawingRecord
+ && records.get( loc + 1 ) instanceof ObjRecord )
+ {
+ loc += 2;
+ }
+ int endloc = loc-1;
+ for(int i = 0; i < (endloc - startloc + 1); i++)
+ records.remove(startloc);
+ records.add(startloc, r);
+
+ return startloc;
+ }
+ }
+
+ /**
+ * Perform any work necessary before the sheet is about to be serialized.
+ * For instance the escher aggregates size needs to be calculated before
+ * serialization so that the dgg record (which occurs first) can be written.
+ */
+ public void preSerialize()
+ {
+ for ( Iterator iterator = getRecords().iterator(); iterator.hasNext(); )
+ {
+ Record r = (Record) iterator.next();
+ if (r instanceof EscherAggregate)
+ r.getRecordSize(); // Trigger flatterning of user model and corresponding update of dgg record.
+ }
+ }
+
+ /**
+ * Shifts all the page breaks in the range "count" number of rows/columns
+ * @param breaks The page record to be shifted
+ * @param start Starting "main" value to shift breaks
+ * @param stop Ending "main" value to shift breaks
+ * @param count number of units (rows/columns) to shift by
+ */
+ public void shiftBreaks(PageBreakRecord breaks, short start, short stop, int count) {
+
+ if(rowBreaks == null)
+ return;
+ Iterator iterator = breaks.getBreaksIterator();
+ List shiftedBreak = new ArrayList();
+ while(iterator.hasNext())
+ {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ short breakLocation = breakItem.main;
+ boolean inStart = (breakLocation >= start);
+ boolean inEnd = (breakLocation <= stop);
+ if(inStart && inEnd)
+ shiftedBreak.add(breakItem);
+ }
+
+ iterator = shiftedBreak.iterator();
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ breaks.removeBreak(breakItem.main);
+ breaks.addBreak((short)(breakItem.main+count), breakItem.subFrom, breakItem.subTo);
+ }
+ }
+
+ /**
+ * Sets a page break at the indicated row
+ * @param row
+ */
+ public void setRowBreak(int row, short fromCol, short toCol) {
+ rowBreaks.addBreak((short)row, fromCol, toCol);
+ }
+
+ /**
+ * Removes a page break at the indicated row
+ * @param row
+ */
+ public void removeRowBreak(int row) {
+ rowBreaks.removeBreak((short)row);
+ }
+
+ /**
+ * Queries if the specified row has a page break
+ * @param row
+ * @return true if the specified row has a page break
+ */
+ public boolean isRowBroken(int row) {
+ return rowBreaks.getBreak((short)row) != null;
+ }
+
+ /**
+ * Sets a page break at the indicated column
+ * @param row
+ */
+ public void setColumnBreak(short column, short fromRow, short toRow) {
+ colBreaks.addBreak(column, fromRow, toRow);
+ }
+
+ /**
+ * Removes a page break at the indicated column
+ * @param row
+ */
+ public void removeColumnBreak(short column) {
+ colBreaks.removeBreak(column);
+ }
+
+ /**
+ * Queries if the specified column has a page break
+ * @param row
+ * @return true if the specified column has a page break
+ */
+ public boolean isColumnBroken(short column) {
+ return colBreaks.getBreak(column) != null;
+ }
+
+ /**
+ * Shifts the horizontal page breaks for the indicated count
+ * @param startingRow
+ * @param endingRow
+ * @param count
+ */
+ public void shiftRowBreaks(int startingRow, int endingRow, int count) {
+ shiftBreaks(rowBreaks, (short)startingRow, (short)endingRow, (short)count);
+ }
+
+ /**
+ * Shifts the vertical page breaks for the indicated count
+ * @param startingCol
+ * @param endingCol
+ * @param count
+ */
+ public void shiftColumnBreaks(short startingCol, short endingCol, short count) {
+ shiftBreaks(colBreaks, startingCol, endingCol, count);
+ }
+
+ /**
+ * Returns all the row page breaks
+ * @return
+ */
+ public Iterator getRowBreaks() {
+ return rowBreaks.getBreaksIterator();
+ }
+
+ /**
+ * Returns the number of row page breaks
+ * @return
+ */
+ public int getNumRowBreaks(){
+ return (int)rowBreaks.getNumBreaks();
+ }
+
+ /**
+ * Returns all the column page breaks
+ * @return
+ */
+ public Iterator getColumnBreaks(){
+ return colBreaks.getBreaksIterator();
+ }
+
+ /**
+ * Returns the number of column page breaks
+ * @return
+ */
+ public int getNumColumnBreaks(){
+ return (int)colBreaks.getNumBreaks();
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/model/SimpleFilledShape.java b/src/java/org/apache/poi/hssf/model/SimpleFilledShape.java
new file mode 100644
index 0000000000..dadb02d79d
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/SimpleFilledShape.java
@@ -0,0 +1,111 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.record.EscherAggregate;
+import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
+import org.apache.poi.hssf.record.EndSubRecord;
+import org.apache.poi.hssf.usermodel.HSSFSimpleShape;
+import org.apache.poi.hssf.usermodel.HSSFShape;
+
+public class SimpleFilledShape
+ extends AbstractShape
+{
+ private EscherContainerRecord spContainer;
+ private ObjRecord objRecord;
+
+ /**
+ * Creates the low evel records for an oval.
+ *
+ * @param hssfShape The highlevel shape.
+ * @param shapeId The shape id to use for this shape.
+ */
+ SimpleFilledShape( HSSFSimpleShape hssfShape, int shapeId )
+ {
+ spContainer = createSpContainer( hssfShape, shapeId );
+ objRecord = createObjRecord( hssfShape, shapeId );
+ }
+
+ /**
+ * Generates the shape records for this shape.
+ *
+ * @param hssfShape
+ * @param shapeId
+ * @return
+ */
+ private EscherContainerRecord createSpContainer( HSSFSimpleShape hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ short shapeType = objTypeToShapeType( hssfShape.getShapeType() );
+ sp.setOptions( (short) ( ( shapeType << 4 ) | 0x2 ) );
+ sp.setShapeId( shapeId );
+ sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ addStandardOptions(shape, opt);
+ EscherRecord anchor = createAnchor( shape.getAnchor() );
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+
+ spContainer.addChildRecord( sp );
+ spContainer.addChildRecord( opt );
+ spContainer.addChildRecord( anchor );
+ spContainer.addChildRecord( clientData );
+
+ return spContainer;
+ }
+
+ private short objTypeToShapeType( int objType )
+ {
+ short shapeType;
+ if (objType == HSSFSimpleShape.OBJECT_TYPE_OVAL)
+ shapeType = EscherAggregate.ST_ELLIPSE;
+ else if (objType == HSSFSimpleShape.OBJECT_TYPE_RECTANGLE)
+ shapeType = EscherAggregate.ST_RECTANGLE;
+ else
+ throw new IllegalArgumentException("Unable to handle an object of this type");
+ return shapeType;
+ }
+
+ /**
+ * Creates the low level OBJ record for this shape.
+ */
+ private ObjRecord createObjRecord( HSSFShape hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType( (short) ( (HSSFSimpleShape) shape ).getShapeType() );
+ c.setObjectId( (short) ( shapeId ) );
+ c.setLocked( true );
+ c.setPrintable( true );
+ c.setAutofill( true );
+ c.setAutoline( true );
+ EndSubRecord e = new EndSubRecord();
+
+ obj.addSubRecord( c );
+ obj.addSubRecord( e );
+
+ return obj;
+ }
+
+ public EscherContainerRecord getSpContainer()
+ {
+ return spContainer;
+ }
+
+ public ObjRecord getObjRecord()
+ {
+ return objRecord;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/TestDrawingManager.java b/src/java/org/apache/poi/hssf/model/TestDrawingManager.java
new file mode 100644
index 0000000000..606ffccc34
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/TestDrawingManager.java
@@ -0,0 +1,81 @@
+package org.apache.poi.hssf.model;
+
+import junit.framework.TestCase;
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherDgRecord;
+
+public class TestDrawingManager extends TestCase
+{
+ public void testFindFreeSPIDBlock() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ DrawingManager dm = new DrawingManager( dgg );
+ dgg.setShapeIdMax( 1024 );
+ assertEquals( 2048, dm.findFreeSPIDBlock() );
+ dgg.setShapeIdMax( 1025 );
+ assertEquals( 2048, dm.findFreeSPIDBlock() );
+ dgg.setShapeIdMax( 2047 );
+ assertEquals( 2048, dm.findFreeSPIDBlock() );
+ }
+
+ public void testFindNewDrawingGroupId() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ dgg.setDrawingsSaved( 1 );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{
+ new EscherDggRecord.FileIdCluster( 2, 10 )} );
+ DrawingManager dm = new DrawingManager( dgg );
+ assertEquals( 1, dm.findNewDrawingGroupId() );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{
+ new EscherDggRecord.FileIdCluster( 1, 10 ),
+ new EscherDggRecord.FileIdCluster( 2, 10 )} );
+ assertEquals( 3, dm.findNewDrawingGroupId() );
+ }
+
+ public void testDrawingGroupExists() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ dgg.setDrawingsSaved( 1 );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{
+ new EscherDggRecord.FileIdCluster( 2, 10 )} );
+ DrawingManager dm = new DrawingManager( dgg );
+ assertFalse( dm.drawingGroupExists( (short) 1 ) );
+ assertTrue( dm.drawingGroupExists( (short) 2 ) );
+ assertFalse( dm.drawingGroupExists( (short) 3 ) );
+ }
+
+ public void testCreateDgRecord() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ dgg.setDrawingsSaved( 0 );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{} );
+ DrawingManager dm = new DrawingManager( dgg );
+
+ EscherDgRecord dgRecord = dm.createDgRecord();
+ assertEquals( -1, dgRecord.getLastMSOSPID() );
+ assertEquals( 0, dgRecord.getNumShapes() );
+ assertEquals( 1, dm.getDgg().getDrawingsSaved() );
+ assertEquals( 1, dm.getDgg().getFileIdClusters().length );
+ assertEquals( 1, dm.getDgg().getFileIdClusters()[0].getDrawingGroupId() );
+ assertEquals( 0, dm.getDgg().getFileIdClusters()[0].getNumShapeIdsUsed() );
+ }
+
+ public void testAllocateShapeId() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ dgg.setDrawingsSaved( 0 );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{} );
+ DrawingManager dm = new DrawingManager( dgg );
+
+ EscherDgRecord dg = dm.createDgRecord();
+ int shapeId = dm.allocateShapeId( dg.getDrawingGroupId() );
+ assertEquals( 1024, shapeId );
+ assertEquals( 1025, dgg.getShapeIdMax() );
+ assertEquals( 1, dgg.getDrawingsSaved() );
+ assertEquals( 1, dgg.getFileIdClusters()[0].getDrawingGroupId() );
+ assertEquals( 1, dgg.getFileIdClusters()[0].getNumShapeIdsUsed() );
+ assertEquals( 1024, dg.getLastMSOSPID() );
+ assertEquals( 1, dg.getNumShapes() );
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/TextboxShape.java b/src/java/org/apache/poi/hssf/model/TextboxShape.java
new file mode 100644
index 0000000000..02143974db
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/TextboxShape.java
@@ -0,0 +1,151 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.usermodel.*;
+
+/**
+ * Represents an textbox shape and converts between the highlevel records
+ * and lowlevel records for an oval.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TextboxShape
+ extends AbstractShape
+{
+ private EscherContainerRecord spContainer;
+ private TextObjectRecord textObjectRecord;
+ private ObjRecord objRecord;
+ private EscherTextboxRecord escherTextbox;
+
+ /**
+ * Creates the low evel records for an textbox.
+ *
+ * @param hssfShape The highlevel shape.
+ * @param shapeId The shape id to use for this shape.
+ */
+ TextboxShape( HSSFTextbox hssfShape, int shapeId )
+ {
+ spContainer = createSpContainer( hssfShape, shapeId );
+ objRecord = createObjRecord( hssfShape, shapeId );
+ textObjectRecord = createTextObjectRecord( hssfShape, shapeId );
+ }
+
+ /**
+ * Creates the low level OBJ record for this shape.
+ */
+ private ObjRecord createObjRecord( HSSFTextbox hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType( (short) ( (HSSFSimpleShape) shape ).getShapeType() );
+ c.setObjectId( (short) ( shapeId ) );
+ c.setLocked( true );
+ c.setPrintable( true );
+ c.setAutofill( true );
+ c.setAutoline( true );
+ EndSubRecord e = new EndSubRecord();
+
+ obj.addSubRecord( c );
+ obj.addSubRecord( e );
+
+ return obj;
+ }
+
+ /**
+ * Generates the escher shape records for this shape.
+ *
+ * @param hssfShape
+ * @param shapeId
+ * @return
+ */
+ private EscherContainerRecord createSpContainer( HSSFTextbox hssfShape, int shapeId )
+ {
+ HSSFTextbox shape = hssfShape;
+
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherRecord anchor = new EscherClientAnchorRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+ escherTextbox = new EscherTextboxRecord();
+
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ sp.setOptions( (short) ( ( EscherAggregate.ST_TEXTBOX << 4 ) | 0x2 ) );
+
+ sp.setShapeId( shapeId );
+ sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ // opt.addEscherProperty( new EscherBoolProperty( EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 262144 ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTID, 0 ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTLEFT, shape.getMarginLeft() ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTRIGHT, shape.getMarginRight() ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTBOTTOM, shape.getMarginBottom() ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTTOP, shape.getMarginTop() ) );
+ addStandardOptions( shape, opt );
+ HSSFAnchor userAnchor = shape.getAnchor();
+ // if (userAnchor.isHorizontallyFlipped())
+ // sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPHORIZ);
+ // if (userAnchor.isVerticallyFlipped())
+ // sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPVERT);
+ anchor = createAnchor( userAnchor );
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+ escherTextbox.setRecordId( EscherTextboxRecord.RECORD_ID );
+ escherTextbox.setOptions( (short) 0x0000 );
+
+ spContainer.addChildRecord( sp );
+ spContainer.addChildRecord( opt );
+ spContainer.addChildRecord( anchor );
+ spContainer.addChildRecord( clientData );
+ spContainer.addChildRecord( escherTextbox );
+
+ return spContainer;
+ }
+
+ /**
+ * Textboxes also have an extra TXO record associated with them that most
+ * other shapes dont have.
+ */
+ private TextObjectRecord createTextObjectRecord( HSSFTextbox hssfShape, int shapeId )
+ {
+ HSSFTextbox shape = hssfShape;
+
+ TextObjectRecord obj = new TextObjectRecord();
+ obj.setHorizontalTextAlignment( TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED );
+ obj.setVerticalTextAlignment( TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP );
+ obj.setTextLocked( true );
+ obj.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );
+ int frLength = ( shape.getString().numFormattingRuns() + 1 ) * 8;
+ obj.setFormattingRunLength( (short) frLength );
+ obj.setTextLength( (short) shape.getString().length() );
+ obj.setStr( shape.getString() );
+ obj.setReserved7( 0 );
+
+ return obj;
+ }
+
+ public EscherContainerRecord getSpContainer()
+ {
+ return spContainer;
+ }
+
+ public ObjRecord getObjRecord()
+ {
+ return objRecord;
+ }
+
+ public TextObjectRecord getTextObjectRecord()
+ {
+ return textObjectRecord;
+ }
+
+ public EscherRecord getEscherTextbox()
+ {
+ return escherTextbox;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java
index e3593414c6..7a0a3e33a7 100644
--- a/src/java/org/apache/poi/hssf/model/Workbook.java
+++ b/src/java/org/apache/poi/hssf/model/Workbook.java
@@ -60,6 +60,7 @@ import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.hssf.util.SheetReferences;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
+import org.apache.poi.ddf.*;
import java.util.ArrayList;
import java.util.Iterator;
@@ -133,6 +134,7 @@ public class Workbook implements Model {
protected int numfonts = 0; // hold the number of font records
private short maxformatid = -1; // holds the max format id
private boolean uses1904datewindowing = false; // whether 1904 date windowing is being used
+ private DrawingManager drawingManager;
private static POILogger log = POILogFactory.getLogger(Workbook.class);
@@ -2090,6 +2092,56 @@ public class Workbook implements Model {
return palette;
}
+ /**
+ * Creates a drawing group record. If it already exists then it's left
+ * alone.
+ */
+ public void createDrawingGroup()
+ {
+ int dggLoc = findFirstRecordLocBySid(EscherContainerRecord.DGG_CONTAINER);
+ if (dggLoc == -1)
+ {
+ EscherContainerRecord dggContainer = new EscherContainerRecord();
+ EscherDggRecord dgg = new EscherDggRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherSplitMenuColorsRecord splitMenuColors = new EscherSplitMenuColorsRecord();
+
+ dggContainer.setRecordId((short) 0xF000);
+ dggContainer.setOptions((short) 0x000F);
+ dgg.setRecordId(EscherDggRecord.RECORD_ID);
+ dgg.setOptions((short)0x0000);
+ dgg.setShapeIdMax(1024);
+ dgg.setNumShapesSaved(0);
+ dgg.setDrawingsSaved(0);
+ dgg.setFileIdClusters(new EscherDggRecord.FileIdCluster[] {} );
+ drawingManager = new DrawingManager(dgg);
+ opt.setRecordId((short) 0xF00B);
+ opt.setOptions((short) 0x0033);
+ opt.addEscherProperty( new EscherBoolProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 524296) );
+ opt.addEscherProperty( new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, 134217737) );
+ opt.addEscherProperty( new EscherRGBProperty(EscherProperties.LINESTYLE__COLOR, 134217792) );
+ splitMenuColors.setRecordId((short) 0xF11E);
+ splitMenuColors.setOptions((short) 0x0040);
+ splitMenuColors.setColor1(0x0800000D);
+ splitMenuColors.setColor2(0x0800000C);
+ splitMenuColors.setColor3(0x08000017);
+ splitMenuColors.setColor4(0x100000F7);
+
+ dggContainer.addChildRecord(dgg);
+ dggContainer.addChildRecord(opt);
+ dggContainer.addChildRecord(splitMenuColors);
+
+ DrawingGroupRecord drawingGroup = new DrawingGroupRecord();
+ drawingGroup.addEscherRecord(dggContainer);
+ int loc = findFirstRecordLocBySid(CountryRecord.sid);
+ getRecords().add(loc+1, drawingGroup);
+ }
+ }
+
+ public DrawingManager getDrawingManager()
+ {
+ return drawingManager;
+ }
}
diff --git a/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java b/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java
new file mode 100644
index 0000000000..59d85acff6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java
@@ -0,0 +1,266 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordFactory;
+import org.apache.poi.ddf.NullEscherSerializationListener;
+import org.apache.poi.util.LittleEndian;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The escher container record is used to hold escher records. It is abstract and
+ * must be subclassed for maximum benefit.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public abstract class AbstractEscherHolderRecord
+ extends Record
+{
+ private static final boolean DESERIALISE = System.getProperty("poi.deserialize.escher") != null;
+
+ private List escherRecords;
+ private byte[] rawData;
+
+
+ public AbstractEscherHolderRecord()
+ {
+ escherRecords = new ArrayList();
+ }
+
+ /**
+ * Constructs a Bar record and sets its fields appropriately.
+ *
+ * @param id id must be 0x1017 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public AbstractEscherHolderRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a Bar record and sets its fields appropriately.
+ *
+ * @param id id must be 0x1017 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public AbstractEscherHolderRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != getSid())
+ {
+ throw new RecordFormatException("Not a Bar record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+ escherRecords = new ArrayList();
+ if (! DESERIALISE )
+ {
+ rawData = new byte[size];
+ System.arraycopy(data, offset, rawData, 0, size);
+ }
+ else
+ {
+ EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
+ int pos = offset;
+ while ( pos < offset + size )
+ {
+ EscherRecord r = recordFactory.createRecord(data, pos);
+ int bytesRead = r.fillFields(data, pos, recordFactory );
+ escherRecords.add(r);
+ pos += bytesRead;
+ }
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ final String nl = System.getProperty("line.separator");
+ buffer.append("[" + getRecordName() + "]" + nl);
+ for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ buffer.append(r.toString());
+ }
+ buffer.append("[/" + getRecordName() + "]" + nl);
+
+ return buffer.toString();
+ }
+
+ protected abstract String getRecordName();
+
+ public int serialize(int offset, byte[] data)
+ {
+ if (escherRecords.size() == 0 && rawData != null)
+ {
+ System.arraycopy( rawData, 0, data, offset, rawData.length);
+ return rawData.length;
+ }
+ else
+ {
+ collapseShapeInformation();
+
+ LittleEndian.putShort(data, 0 + offset, getSid());
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+ int pos = offset + 4;
+ for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ pos += r.serialize(pos, data, new NullEscherSerializationListener() );
+ }
+
+ return getRecordSize();
+ }
+ }
+
+ /**
+ * Size of record (including 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ if (escherRecords.size() == 0 && rawData != null)
+ {
+ return rawData.length;
+ }
+ else
+ {
+ collapseShapeInformation();
+
+ int size = 4;
+ for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ size += r.getRecordSize();
+ }
+ return size;
+ }
+ }
+
+ private void collapseShapeInformation()
+ {
+
+ }
+
+ public abstract short getSid();
+
+ public Object clone()
+ {
+ throw new IllegalStateException("Not implemented yet.");
+ }
+
+ public void addEscherRecord(int index, EscherRecord element)
+ {
+ escherRecords.add( index, element );
+ }
+
+ public boolean addEscherRecord(EscherRecord element)
+ {
+ return escherRecords.add( element );
+ }
+
+ public List getEscherRecords()
+ {
+ return escherRecords;
+ }
+
+ public void clearEscherRecords()
+ {
+ escherRecords.clear();
+ }
+
+
+ public EscherRecord getEscherRecord(int index)
+ {
+ return (EscherRecord) escherRecords.get(index);
+ }
+
+
+} // END OF CLASS
+
+
+
+
diff --git a/src/java/org/apache/poi/hssf/record/CommonObjectDataSubRecord.java b/src/java/org/apache/poi/hssf/record/CommonObjectDataSubRecord.java
new file mode 100644
index 0000000000..65c76d6041
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/CommonObjectDataSubRecord.java
@@ -0,0 +1,498 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * The common object data record is used to store all common preferences for an excel object.
+ * NOTE: This source is automatically generated please do not modify this file. Either subclass or
+ * remove the record in src/records/definitions.
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class CommonObjectDataSubRecord
+ extends SubRecord
+{
+ public final static short sid = 0x15;
+ private short field_1_objectType;
+ public final static short OBJECT_TYPE_GROUP = 0;
+ public final static short OBJECT_TYPE_LINE = 1;
+ public final static short OBJECT_TYPE_RECTANGLE = 2;
+ public final static short OBJECT_TYPE_OVAL = 3;
+ public final static short OBJECT_TYPE_ARC = 4;
+ public final static short OBJECT_TYPE_CHART = 5;
+ public final static short OBJECT_TYPE_TEXT = 6;
+ public final static short OBJECT_TYPE_BUTTON = 7;
+ public final static short OBJECT_TYPE_PICTURE = 8;
+ public final static short OBJECT_TYPE_POLYGON = 9;
+ public final static short OBJECT_TYPE_RESERVED1 = 10;
+ public final static short OBJECT_TYPE_CHECKBOX = 11;
+ public final static short OBJECT_TYPE_OPTION_BUTTON = 12;
+ public final static short OBJECT_TYPE_EDIT_BOX = 13;
+ public final static short OBJECT_TYPE_LABEL = 14;
+ public final static short OBJECT_TYPE_DIALOG_BOX = 15;
+ public final static short OBJECT_TYPE_SPINNER = 16;
+ public final static short OBJECT_TYPE_SCROLL_BAR = 17;
+ public final static short OBJECT_TYPE_LIST_BOX = 18;
+ public final static short OBJECT_TYPE_GROUP_BOX = 19;
+ public final static short OBJECT_TYPE_COMBO_BOX = 20;
+ public final static short OBJECT_TYPE_RESERVED2 = 21;
+ public final static short OBJECT_TYPE_RESERVED3 = 22;
+ public final static short OBJECT_TYPE_RESERVED4 = 23;
+ public final static short OBJECT_TYPE_RESERVED5 = 24;
+ public final static short OBJECT_TYPE_COMMENT = 25;
+ public final static short OBJECT_TYPE_RESERVED6 = 26;
+ public final static short OBJECT_TYPE_RESERVED7 = 27;
+ public final static short OBJECT_TYPE_RESERVED8 = 28;
+ public final static short OBJECT_TYPE_RESERVED9 = 29;
+ public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30;
+ private short field_2_objectId;
+ private short field_3_option;
+ private BitField locked = new BitField(0x1);
+ private BitField printable = new BitField(0x10);
+ private BitField autofill = new BitField(0x2000);
+ private BitField autoline = new BitField(0x4000);
+ private int field_4_reserved1;
+ private int field_5_reserved2;
+ private int field_6_reserved3;
+
+
+ public CommonObjectDataSubRecord()
+ {
+
+ }
+
+ /**
+ * Constructs a CommonObjectData record and sets its fields appropriately.
+ *
+ * @param id id must be 0x15 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public CommonObjectDataSubRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a CommonObjectData record and sets its fields appropriately.
+ *
+ * @param id id must be 0x15 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public CommonObjectDataSubRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a CommonObjectData record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+
+ int pos = 0;
+ field_1_objectType = LittleEndian.getShort(data, pos + 0x0 + offset);
+ field_2_objectId = LittleEndian.getShort(data, pos + 0x2 + offset);
+ field_3_option = LittleEndian.getShort(data, pos + 0x4 + offset);
+ field_4_reserved1 = LittleEndian.getInt(data, pos + 0x6 + offset);
+ field_5_reserved2 = LittleEndian.getInt(data, pos + 0xa + offset);
+ field_6_reserved3 = LittleEndian.getInt(data, pos + 0xe + offset);
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[ftCmo]\n");
+ buffer.append(" .objectType = ")
+ .append("0x").append(HexDump.toHex( getObjectType ()))
+ .append(" (").append( getObjectType() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .objectId = ")
+ .append("0x").append(HexDump.toHex( getObjectId ()))
+ .append(" (").append( getObjectId() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .option = ")
+ .append("0x").append(HexDump.toHex( getOption ()))
+ .append(" (").append( getOption() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .locked = ").append(isLocked()).append('\n');
+ buffer.append(" .printable = ").append(isPrintable()).append('\n');
+ buffer.append(" .autofill = ").append(isAutofill()).append('\n');
+ buffer.append(" .autoline = ").append(isAutoline()).append('\n');
+ buffer.append(" .reserved1 = ")
+ .append("0x").append(HexDump.toHex( getReserved1 ()))
+ .append(" (").append( getReserved1() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved2 = ")
+ .append("0x").append(HexDump.toHex( getReserved2 ()))
+ .append(" (").append( getReserved2() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved3 = ")
+ .append("0x").append(HexDump.toHex( getReserved3 ()))
+ .append(" (").append( getReserved3() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+
+ buffer.append("[/ftCmo]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+ LittleEndian.putShort(data, 4 + offset + pos, field_1_objectType);
+ LittleEndian.putShort(data, 6 + offset + pos, field_2_objectId);
+ LittleEndian.putShort(data, 8 + offset + pos, field_3_option);
+ LittleEndian.putInt(data, 10 + offset + pos, field_4_reserved1);
+ LittleEndian.putInt(data, 14 + offset + pos, field_5_reserved2);
+ LittleEndian.putInt(data, 18 + offset + pos, field_6_reserved3);
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ return 4 + 2 + 2 + 2 + 4 + 4 + 4;
+ }
+
+ public short getSid()
+ {
+ return this.sid;
+ }
+
+ public Object clone() {
+ CommonObjectDataSubRecord rec = new CommonObjectDataSubRecord();
+
+ rec.field_1_objectType = field_1_objectType;
+ rec.field_2_objectId = field_2_objectId;
+ rec.field_3_option = field_3_option;
+ rec.field_4_reserved1 = field_4_reserved1;
+ rec.field_5_reserved2 = field_5_reserved2;
+ rec.field_6_reserved3 = field_6_reserved3;
+ return rec;
+ }
+
+
+ /**
+ * Get the object type field for the CommonObjectData record.
+ *
+ * @return One of
+ * OBJECT_TYPE_GROUP
+ * OBJECT_TYPE_LINE
+ * OBJECT_TYPE_RECTANGLE
+ * OBJECT_TYPE_OVAL
+ * OBJECT_TYPE_ARC
+ * OBJECT_TYPE_CHART
+ * OBJECT_TYPE_TEXT
+ * OBJECT_TYPE_BUTTON
+ * OBJECT_TYPE_PICTURE
+ * OBJECT_TYPE_POLYGON
+ * OBJECT_TYPE_RESERVED1
+ * OBJECT_TYPE_CHECKBOX
+ * OBJECT_TYPE_OPTION_BUTTON
+ * OBJECT_TYPE_EDIT_BOX
+ * OBJECT_TYPE_LABEL
+ * OBJECT_TYPE_DIALOG_BOX
+ * OBJECT_TYPE_SPINNER
+ * OBJECT_TYPE_SCROLL_BAR
+ * OBJECT_TYPE_LIST_BOX
+ * OBJECT_TYPE_GROUP_BOX
+ * OBJECT_TYPE_COMBO_BOX
+ * OBJECT_TYPE_RESERVED2
+ * OBJECT_TYPE_RESERVED3
+ * OBJECT_TYPE_RESERVED4
+ * OBJECT_TYPE_RESERVED5
+ * OBJECT_TYPE_COMMENT
+ * OBJECT_TYPE_RESERVED6
+ * OBJECT_TYPE_RESERVED7
+ * OBJECT_TYPE_RESERVED8
+ * OBJECT_TYPE_RESERVED9
+ * OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING
+ */
+ public short getObjectType()
+ {
+ return field_1_objectType;
+ }
+
+ /**
+ * Set the object type field for the CommonObjectData record.
+ *
+ * @param field_1_objectType
+ * One of
+ * OBJECT_TYPE_GROUP
+ * OBJECT_TYPE_LINE
+ * OBJECT_TYPE_RECTANGLE
+ * OBJECT_TYPE_OVAL
+ * OBJECT_TYPE_ARC
+ * OBJECT_TYPE_CHART
+ * OBJECT_TYPE_TEXT
+ * OBJECT_TYPE_BUTTON
+ * OBJECT_TYPE_PICTURE
+ * OBJECT_TYPE_POLYGON
+ * OBJECT_TYPE_RESERVED1
+ * OBJECT_TYPE_CHECKBOX
+ * OBJECT_TYPE_OPTION_BUTTON
+ * OBJECT_TYPE_EDIT_BOX
+ * OBJECT_TYPE_LABEL
+ * OBJECT_TYPE_DIALOG_BOX
+ * OBJECT_TYPE_SPINNER
+ * OBJECT_TYPE_SCROLL_BAR
+ * OBJECT_TYPE_LIST_BOX
+ * OBJECT_TYPE_GROUP_BOX
+ * OBJECT_TYPE_COMBO_BOX
+ * OBJECT_TYPE_RESERVED2
+ * OBJECT_TYPE_RESERVED3
+ * OBJECT_TYPE_RESERVED4
+ * OBJECT_TYPE_RESERVED5
+ * OBJECT_TYPE_COMMENT
+ * OBJECT_TYPE_RESERVED6
+ * OBJECT_TYPE_RESERVED7
+ * OBJECT_TYPE_RESERVED8
+ * OBJECT_TYPE_RESERVED9
+ * OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING
+ */
+ public void setObjectType(short field_1_objectType)
+ {
+ this.field_1_objectType = field_1_objectType;
+ }
+
+ /**
+ * Get the object id field for the CommonObjectData record.
+ */
+ public short getObjectId()
+ {
+ return field_2_objectId;
+ }
+
+ /**
+ * Set the object id field for the CommonObjectData record.
+ */
+ public void setObjectId(short field_2_objectId)
+ {
+ this.field_2_objectId = field_2_objectId;
+ }
+
+ /**
+ * Get the option field for the CommonObjectData record.
+ */
+ public short getOption()
+ {
+ return field_3_option;
+ }
+
+ /**
+ * Set the option field for the CommonObjectData record.
+ */
+ public void setOption(short field_3_option)
+ {
+ this.field_3_option = field_3_option;
+ }
+
+ /**
+ * Get the reserved1 field for the CommonObjectData record.
+ */
+ public int getReserved1()
+ {
+ return field_4_reserved1;
+ }
+
+ /**
+ * Set the reserved1 field for the CommonObjectData record.
+ */
+ public void setReserved1(int field_4_reserved1)
+ {
+ this.field_4_reserved1 = field_4_reserved1;
+ }
+
+ /**
+ * Get the reserved2 field for the CommonObjectData record.
+ */
+ public int getReserved2()
+ {
+ return field_5_reserved2;
+ }
+
+ /**
+ * Set the reserved2 field for the CommonObjectData record.
+ */
+ public void setReserved2(int field_5_reserved2)
+ {
+ this.field_5_reserved2 = field_5_reserved2;
+ }
+
+ /**
+ * Get the reserved3 field for the CommonObjectData record.
+ */
+ public int getReserved3()
+ {
+ return field_6_reserved3;
+ }
+
+ /**
+ * Set the reserved3 field for the CommonObjectData record.
+ */
+ public void setReserved3(int field_6_reserved3)
+ {
+ this.field_6_reserved3 = field_6_reserved3;
+ }
+
+ /**
+ * Sets the locked field value.
+ * true if object is locked when sheet has been protected
+ */
+ public void setLocked(boolean value)
+ {
+ field_3_option = locked.setShortBoolean(field_3_option, value);
+ }
+
+ /**
+ * true if object is locked when sheet has been protected
+ * @return the locked field value.
+ */
+ public boolean isLocked()
+ {
+ return locked.isSet(field_3_option);
+ }
+
+ /**
+ * Sets the printable field value.
+ * object appears when printed
+ */
+ public void setPrintable(boolean value)
+ {
+ field_3_option = printable.setShortBoolean(field_3_option, value);
+ }
+
+ /**
+ * object appears when printed
+ * @return the printable field value.
+ */
+ public boolean isPrintable()
+ {
+ return printable.isSet(field_3_option);
+ }
+
+ /**
+ * Sets the autofill field value.
+ * whether object uses an automatic fill style
+ */
+ public void setAutofill(boolean value)
+ {
+ field_3_option = autofill.setShortBoolean(field_3_option, value);
+ }
+
+ /**
+ * whether object uses an automatic fill style
+ * @return the autofill field value.
+ */
+ public boolean isAutofill()
+ {
+ return autofill.isSet(field_3_option);
+ }
+
+ /**
+ * Sets the autoline field value.
+ * whether object uses an automatic line style
+ */
+ public void setAutoline(boolean value)
+ {
+ field_3_option = autoline.setShortBoolean(field_3_option, value);
+ }
+
+ /**
+ * whether object uses an automatic line style
+ * @return the autoline field value.
+ */
+ public boolean isAutoline()
+ {
+ return autoline.isSet(field_3_option);
+ }
+
+
+} // END OF CLASS
+
+
diff --git a/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java b/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java
new file mode 100644
index 0000000000..9d3ab19ce1
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java
@@ -0,0 +1,30 @@
+package org.apache.poi.hssf.record;
+
+public class DrawingGroupRecord extends AbstractEscherHolderRecord
+{
+ public static final short sid = 0xEB;
+
+ public DrawingGroupRecord()
+ {
+ }
+
+ public DrawingGroupRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public DrawingGroupRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ protected String getRecordName()
+ {
+ return "MSODRAWINGGROUP";
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/DrawingRecord.java b/src/java/org/apache/poi/hssf/record/DrawingRecord.java
new file mode 100644
index 0000000000..cb847afeb6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/DrawingRecord.java
@@ -0,0 +1,97 @@
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.util.LittleEndian;
+
+public class DrawingRecord extends Record
+{
+ public static final short sid = 0xEC;
+
+ private byte[] recordData;
+
+ public DrawingRecord()
+ {
+ }
+
+ public DrawingRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public DrawingRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a MSODRAWING record");
+ }
+ }
+
+ protected void fillFields( byte[] data, short size, int offset )
+ {
+ if (offset == 0 && size == data.length)
+ {
+ recordData = data;
+ }
+ else
+ {
+ recordData = new byte[size];
+ System.arraycopy(data, offset, recordData, 0, size);
+ }
+ }
+
+ protected void fillFields( byte[] data, short size )
+ {
+ recordData = data;
+ }
+
+ public int serialize( int offset, byte[] data )
+ {
+ if (recordData == null)
+ {
+ recordData = new byte[ 0 ];
+ }
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, ( short ) (recordData.length));
+ if (recordData.length > 0)
+ {
+ System.arraycopy(recordData, 0, data, 4 + offset, recordData.length);
+ }
+ return getRecordSize();
+ }
+
+ public int getRecordSize()
+ {
+ int retval = 4;
+
+ if (recordData != null)
+ {
+ retval += recordData.length;
+ }
+ return retval;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public byte[] getData()
+ {
+ return recordData;
+ }
+
+ public void setData( byte[] thedata )
+ {
+ this.recordData = thedata;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java b/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java
new file mode 100644
index 0000000000..2dbd71418f
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java
@@ -0,0 +1,35 @@
+package org.apache.poi.hssf.record;
+
+/**
+ * This is purely for the biff viewer. During normal operations we don't want
+ * to be seeing this.
+ */
+public class DrawingRecordForBiffViewer
+ extends AbstractEscherHolderRecord
+{
+ public static final short sid = 0xEC;
+
+ public DrawingRecordForBiffViewer()
+ {
+ }
+
+ public DrawingRecordForBiffViewer( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public DrawingRecordForBiffViewer( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ protected String getRecordName()
+ {
+ return "MSODRAWING";
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/DrawingSelectionRecord.java b/src/java/org/apache/poi/hssf/record/DrawingSelectionRecord.java
new file mode 100644
index 0000000000..2ae8660fc1
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/DrawingSelectionRecord.java
@@ -0,0 +1,30 @@
+package org.apache.poi.hssf.record;
+
+public class DrawingSelectionRecord extends AbstractEscherHolderRecord
+{
+ public static final short sid = 0xED;
+
+ public DrawingSelectionRecord()
+ {
+ }
+
+ public DrawingSelectionRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public DrawingSelectionRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ protected String getRecordName()
+ {
+ return "MSODRAWINGSELECTION";
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/EndSubRecord.java b/src/java/org/apache/poi/hssf/record/EndSubRecord.java
new file mode 100644
index 0000000000..22ac23462d
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/EndSubRecord.java
@@ -0,0 +1,176 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * The end data record is used to denote the end of the subrecords.
+ * NOTE: This source is automatically generated please do not modify this file. Either subclass or
+ * remove the record in src/records/definitions.
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EndSubRecord
+ extends SubRecord
+{
+ public final static short sid = 0x00;
+
+
+ public EndSubRecord()
+ {
+
+ }
+
+ /**
+ * Constructs a End record and sets its fields appropriately.
+ *
+ * @param id id must be 0x00 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public EndSubRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a End record and sets its fields appropriately.
+ *
+ * @param id id must be 0x00 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public EndSubRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a End record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+
+ int pos = 0;
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[ftEnd]\n");
+
+ buffer.append("[/ftEnd]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ return 4 ;
+ }
+
+ public short getSid()
+ {
+ return this.sid;
+ }
+
+ public Object clone() {
+ EndSubRecord rec = new EndSubRecord();
+
+ return rec;
+ }
+
+
+
+} // END OF CLASS
+
+
diff --git a/src/java/org/apache/poi/hssf/record/EscherAggregate.java b/src/java/org/apache/poi/hssf/record/EscherAggregate.java
new file mode 100644
index 0000000000..a45621927b
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/EscherAggregate.java
@@ -0,0 +1,696 @@
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.hssf.model.AbstractShape;
+import org.apache.poi.hssf.model.TextboxShape;
+import org.apache.poi.hssf.model.DrawingManager;
+import org.apache.poi.hssf.model.ConvertAnchor;
+
+import java.util.*;
+
+/**
+ * This class is used to aggregate the MSODRAWING and OBJ record
+ * combinations. This is necessary due to the bizare way in which
+ * these records are serialized. What happens is that you get a
+ * combination of MSODRAWING -> OBJ -> MSODRAWING -> OBJ records
+ * but the escher records are serialized _across_ the MSODRAWING
+ * records.
+ * <p>
+ * It gets even worse when you start looking at TXO records.
+ * <p>
+ * So what we do with this class is aggregate lazily. That is
+ * we don't aggregate the MSODRAWING -> OBJ records unless we
+ * need to modify them.
+ *
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherAggregate extends AbstractEscherHolderRecord
+{
+ public static final short sid = 9876;
+
+ public static final short ST_MIN = (short) 0;
+ public static final short ST_NOT_PRIMATIVE = ST_MIN;
+ public static final short ST_RECTANGLE = (short) 1;
+ public static final short ST_ROUNDRECTANGLE = (short) 2;
+ public static final short ST_ELLIPSE = (short) 3;
+ public static final short ST_DIAMOND = (short) 4;
+ public static final short ST_ISOCELESTRIANGLE = (short) 5;
+ public static final short ST_RIGHTTRIANGLE = (short) 6;
+ public static final short ST_PARALLELOGRAM = (short) 7;
+ public static final short ST_TRAPEZOID = (short) 8;
+ public static final short ST_HEXAGON = (short) 9;
+ public static final short ST_OCTAGON = (short) 10;
+ public static final short ST_PLUS = (short) 11;
+ public static final short ST_STAR = (short) 12;
+ public static final short ST_ARROW = (short) 13;
+ public static final short ST_THICKARROW = (short) 14;
+ public static final short ST_HOMEPLATE = (short) 15;
+ public static final short ST_CUBE = (short) 16;
+ public static final short ST_BALLOON = (short) 17;
+ public static final short ST_SEAL = (short) 18;
+ public static final short ST_ARC = (short) 19;
+ public static final short ST_LINE = (short) 20;
+ public static final short ST_PLAQUE = (short) 21;
+ public static final short ST_CAN = (short) 22;
+ public static final short ST_DONUT = (short) 23;
+ public static final short ST_TEXTSIMPLE = (short) 24;
+ public static final short ST_TEXTOCTAGON = (short) 25;
+ public static final short ST_TEXTHEXAGON = (short) 26;
+ public static final short ST_TEXTCURVE = (short) 27;
+ public static final short ST_TEXTWAVE = (short) 28;
+ public static final short ST_TEXTRING = (short) 29;
+ public static final short ST_TEXTONCURVE = (short) 30;
+ public static final short ST_TEXTONRING = (short) 31;
+ public static final short ST_STRAIGHTCONNECTOR1 = (short) 32;
+ public static final short ST_BENTCONNECTOR2 = (short) 33;
+ public static final short ST_BENTCONNECTOR3 = (short) 34;
+ public static final short ST_BENTCONNECTOR4 = (short) 35;
+ public static final short ST_BENTCONNECTOR5 = (short) 36;
+ public static final short ST_CURVEDCONNECTOR2 = (short) 37;
+ public static final short ST_CURVEDCONNECTOR3 = (short) 38;
+ public static final short ST_CURVEDCONNECTOR4 = (short) 39;
+ public static final short ST_CURVEDCONNECTOR5 = (short) 40;
+ public static final short ST_CALLOUT1 = (short) 41;
+ public static final short ST_CALLOUT2 = (short) 42;
+ public static final short ST_CALLOUT3 = (short) 43;
+ public static final short ST_ACCENTCALLOUT1 = (short) 44;
+ public static final short ST_ACCENTCALLOUT2 = (short) 45;
+ public static final short ST_ACCENTCALLOUT3 = (short) 46;
+ public static final short ST_BORDERCALLOUT1 = (short) 47;
+ public static final short ST_BORDERCALLOUT2 = (short) 48;
+ public static final short ST_BORDERCALLOUT3 = (short) 49;
+ public static final short ST_ACCENTBORDERCALLOUT1 = (short) 50;
+ public static final short ST_ACCENTBORDERCALLOUT2 = (short) 51;
+ public static final short ST_ACCENTBORDERCALLOUT3 = (short) 52;
+ public static final short ST_RIBBON = (short) 53;
+ public static final short ST_RIBBON2 = (short) 54;
+ public static final short ST_CHEVRON = (short) 55;
+ public static final short ST_PENTAGON = (short) 56;
+ public static final short ST_NOSMOKING = (short) 57;
+ public static final short ST_SEAL8 = (short) 58;
+ public static final short ST_SEAL16 = (short) 59;
+ public static final short ST_SEAL32 = (short) 60;
+ public static final short ST_WEDGERECTCALLOUT = (short) 61;
+ public static final short ST_WEDGERRECTCALLOUT = (short) 62;
+ public static final short ST_WEDGEELLIPSECALLOUT = (short) 63;
+ public static final short ST_WAVE = (short) 64;
+ public static final short ST_FOLDEDCORNER = (short) 65;
+ public static final short ST_LEFTARROW = (short) 66;
+ public static final short ST_DOWNARROW = (short) 67;
+ public static final short ST_UPARROW = (short) 68;
+ public static final short ST_LEFTRIGHTARROW = (short) 69;
+ public static final short ST_UPDOWNARROW = (short) 70;
+ public static final short ST_IRREGULARSEAL1 = (short) 71;
+ public static final short ST_IRREGULARSEAL2 = (short) 72;
+ public static final short ST_LIGHTNINGBOLT = (short) 73;
+ public static final short ST_HEART = (short) 74;
+ public static final short ST_PICTUREFRAME = (short) 75;
+ public static final short ST_QUADARROW = (short) 76;
+ public static final short ST_LEFTARROWCALLOUT = (short) 77;
+ public static final short ST_RIGHTARROWCALLOUT = (short) 78;
+ public static final short ST_UPARROWCALLOUT = (short) 79;
+ public static final short ST_DOWNARROWCALLOUT = (short) 80;
+ public static final short ST_LEFTRIGHTARROWCALLOUT = (short) 81;
+ public static final short ST_UPDOWNARROWCALLOUT = (short) 82;
+ public static final short ST_QUADARROWCALLOUT = (short) 83;
+ public static final short ST_BEVEL = (short) 84;
+ public static final short ST_LEFTBRACKET = (short) 85;
+ public static final short ST_RIGHTBRACKET = (short) 86;
+ public static final short ST_LEFTBRACE = (short) 87;
+ public static final short ST_RIGHTBRACE = (short) 88;
+ public static final short ST_LEFTUPARROW = (short) 89;
+ public static final short ST_BENTUPARROW = (short) 90;
+ public static final short ST_BENTARROW = (short) 91;
+ public static final short ST_SEAL24 = (short) 92;
+ public static final short ST_STRIPEDRIGHTARROW = (short) 93;
+ public static final short ST_NOTCHEDRIGHTARROW = (short) 94;
+ public static final short ST_BLOCKARC = (short) 95;
+ public static final short ST_SMILEYFACE = (short) 96;
+ public static final short ST_VERTICALSCROLL = (short) 97;
+ public static final short ST_HORIZONTALSCROLL = (short) 98;
+ public static final short ST_CIRCULARARROW = (short) 99;
+ public static final short ST_NOTCHEDCIRCULARARROW = (short) 100;
+ public static final short ST_UTURNARROW = (short) 101;
+ public static final short ST_CURVEDRIGHTARROW = (short) 102;
+ public static final short ST_CURVEDLEFTARROW = (short) 103;
+ public static final short ST_CURVEDUPARROW = (short) 104;
+ public static final short ST_CURVEDDOWNARROW = (short) 105;
+ public static final short ST_CLOUDCALLOUT = (short) 106;
+ public static final short ST_ELLIPSERIBBON = (short) 107;
+ public static final short ST_ELLIPSERIBBON2 = (short) 108;
+ public static final short ST_FLOWCHARTPROCESS = (short) 109;
+ public static final short ST_FLOWCHARTDECISION = (short) 110;
+ public static final short ST_FLOWCHARTINPUTOUTPUT = (short) 111;
+ public static final short ST_FLOWCHARTPREDEFINEDPROCESS = (short) 112;
+ public static final short ST_FLOWCHARTINTERNALSTORAGE = (short) 113;
+ public static final short ST_FLOWCHARTDOCUMENT = (short) 114;
+ public static final short ST_FLOWCHARTMULTIDOCUMENT = (short) 115;
+ public static final short ST_FLOWCHARTTERMINATOR = (short) 116;
+ public static final short ST_FLOWCHARTPREPARATION = (short) 117;
+ public static final short ST_FLOWCHARTMANUALINPUT = (short) 118;
+ public static final short ST_FLOWCHARTMANUALOPERATION = (short) 119;
+ public static final short ST_FLOWCHARTCONNECTOR = (short) 120;
+ public static final short ST_FLOWCHARTPUNCHEDCARD = (short) 121;
+ public static final short ST_FLOWCHARTPUNCHEDTAPE = (short) 122;
+ public static final short ST_FLOWCHARTSUMMINGJUNCTION = (short) 123;
+ public static final short ST_FLOWCHARTOR = (short) 124;
+ public static final short ST_FLOWCHARTCOLLATE = (short) 125;
+ public static final short ST_FLOWCHARTSORT = (short) 126;
+ public static final short ST_FLOWCHARTEXTRACT = (short) 127;
+ public static final short ST_FLOWCHARTMERGE = (short) 128;
+ public static final short ST_FLOWCHARTOFFLINESTORAGE = (short) 129;
+ public static final short ST_FLOWCHARTONLINESTORAGE = (short) 130;
+ public static final short ST_FLOWCHARTMAGNETICTAPE = (short) 131;
+ public static final short ST_FLOWCHARTMAGNETICDISK = (short) 132;
+ public static final short ST_FLOWCHARTMAGNETICDRUM = (short) 133;
+ public static final short ST_FLOWCHARTDISPLAY = (short) 134;
+ public static final short ST_FLOWCHARTDELAY = (short) 135;
+ public static final short ST_TEXTPLAINTEXT = (short) 136;
+ public static final short ST_TEXTSTOP = (short) 137;
+ public static final short ST_TEXTTRIANGLE = (short) 138;
+ public static final short ST_TEXTTRIANGLEINVERTED = (short) 139;
+ public static final short ST_TEXTCHEVRON = (short) 140;
+ public static final short ST_TEXTCHEVRONINVERTED = (short) 141;
+ public static final short ST_TEXTRINGINSIDE = (short) 142;
+ public static final short ST_TEXTRINGOUTSIDE = (short) 143;
+ public static final short ST_TEXTARCHUPCURVE = (short) 144;
+ public static final short ST_TEXTARCHDOWNCURVE = (short) 145;
+ public static final short ST_TEXTCIRCLECURVE = (short) 146;
+ public static final short ST_TEXTBUTTONCURVE = (short) 147;
+ public static final short ST_TEXTARCHUPPOUR = (short) 148;
+ public static final short ST_TEXTARCHDOWNPOUR = (short) 149;
+ public static final short ST_TEXTCIRCLEPOUR = (short) 150;
+ public static final short ST_TEXTBUTTONPOUR = (short) 151;
+ public static final short ST_TEXTCURVEUP = (short) 152;
+ public static final short ST_TEXTCURVEDOWN = (short) 153;
+ public static final short ST_TEXTCASCADEUP = (short) 154;
+ public static final short ST_TEXTCASCADEDOWN = (short) 155;
+ public static final short ST_TEXTWAVE1 = (short) 156;
+ public static final short ST_TEXTWAVE2 = (short) 157;
+ public static final short ST_TEXTWAVE3 = (short) 158;
+ public static final short ST_TEXTWAVE4 = (short) 159;
+ public static final short ST_TEXTINFLATE = (short) 160;
+ public static final short ST_TEXTDEFLATE = (short) 161;
+ public static final short ST_TEXTINFLATEBOTTOM = (short) 162;
+ public static final short ST_TEXTDEFLATEBOTTOM = (short) 163;
+ public static final short ST_TEXTINFLATETOP = (short) 164;
+ public static final short ST_TEXTDEFLATETOP = (short) 165;
+ public static final short ST_TEXTDEFLATEINFLATE = (short) 166;
+ public static final short ST_TEXTDEFLATEINFLATEDEFLATE = (short) 167;
+ public static final short ST_TEXTFADERIGHT = (short) 168;
+ public static final short ST_TEXTFADELEFT = (short) 169;
+ public static final short ST_TEXTFADEUP = (short) 170;
+ public static final short ST_TEXTFADEDOWN = (short) 171;
+ public static final short ST_TEXTSLANTUP = (short) 172;
+ public static final short ST_TEXTSLANTDOWN = (short) 173;
+ public static final short ST_TEXTCANUP = (short) 174;
+ public static final short ST_TEXTCANDOWN = (short) 175;
+ public static final short ST_FLOWCHARTALTERNATEPROCESS = (short) 176;
+ public static final short ST_FLOWCHARTOFFPAGECONNECTOR = (short) 177;
+ public static final short ST_CALLOUT90 = (short) 178;
+ public static final short ST_ACCENTCALLOUT90 = (short) 179;
+ public static final short ST_BORDERCALLOUT90 = (short) 180;
+ public static final short ST_ACCENTBORDERCALLOUT90 = (short) 181;
+ public static final short ST_LEFTRIGHTUPARROW = (short) 182;
+ public static final short ST_SUN = (short) 183;
+ public static final short ST_MOON = (short) 184;
+ public static final short ST_BRACKETPAIR = (short) 185;
+ public static final short ST_BRACEPAIR = (short) 186;
+ public static final short ST_SEAL4 = (short) 187;
+ public static final short ST_DOUBLEWAVE = (short) 188;
+ public static final short ST_ACTIONBUTTONBLANK = (short) 189;
+ public static final short ST_ACTIONBUTTONHOME = (short) 190;
+ public static final short ST_ACTIONBUTTONHELP = (short) 191;
+ public static final short ST_ACTIONBUTTONINFORMATION = (short) 192;
+ public static final short ST_ACTIONBUTTONFORWARDNEXT = (short) 193;
+ public static final short ST_ACTIONBUTTONBACKPREVIOUS = (short) 194;
+ public static final short ST_ACTIONBUTTONEND = (short) 195;
+ public static final short ST_ACTIONBUTTONBEGINNING = (short) 196;
+ public static final short ST_ACTIONBUTTONRETURN = (short) 197;
+ public static final short ST_ACTIONBUTTONDOCUMENT = (short) 198;
+ public static final short ST_ACTIONBUTTONSOUND = (short) 199;
+ public static final short ST_ACTIONBUTTONMOVIE = (short) 200;
+ public static final short ST_HOSTCONTROL = (short) 201;
+ public static final short ST_TEXTBOX = (short) 202;
+ public static final short ST_NIL = (short) 0x0FFF;
+
+ protected HSSFPatriarch patriarch;
+
+ /** Maps shape container objects to their OBJ records */
+ private Map shapeToObj = new HashMap();
+ private DrawingManager drawingManager;
+ private short drawingGroupId;
+
+ public EscherAggregate( DrawingManager drawingManager )
+ {
+ this.drawingManager = drawingManager;
+ }
+
+ /**
+ * @return Returns the current sid.
+ */
+ public short getSid()
+ {
+ return sid;
+ }
+
+ /**
+ * Unused since this is an aggregate record. Use createAggregate().
+ *
+ * @see #createAggregate
+ */
+ protected void fillFields( byte[] data, short size, int offset )
+ {
+ throw new IllegalStateException( "Should not reach here" );
+ }
+
+ /**
+ * Calculates the string representation of this record. This is
+ * simply a dump of all the records.
+ */
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separtor" );
+
+ StringBuffer result = new StringBuffer();
+ result.append( '[' ).append( getRecordName() ).append( ']' + nl );
+ for ( Iterator iterator = getEscherRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord escherRecord = (EscherRecord) iterator.next();
+ result.append( escherRecord.toString() );
+ }
+ result.append( "[/" ).append( getRecordName() ).append( ']' + nl );
+
+ return result.toString();
+ }
+
+ /**
+ * Collapses the drawing records into an aggregate.
+ */
+ public static EscherAggregate createAggregate( List records, int locFirstDrawingRecord, DrawingManager drawingManager )
+ {
+ // Keep track of any shape records created so we can match them back to the object id's.
+ // Textbox objects are also treated as shape objects.
+ final List shapeRecords = new ArrayList();
+ EscherRecordFactory recordFactory = new DefaultEscherRecordFactory()
+ {
+ public EscherRecord createRecord( byte[] data, int offset )
+ {
+ EscherRecord r = super.createRecord( data, offset );
+ if ( r.getRecordId() == EscherClientDataRecord.RECORD_ID || r.getRecordId() == EscherTextboxRecord.RECORD_ID )
+ {
+ shapeRecords.add( r );
+ }
+ return r;
+ }
+ };
+
+ // Calculate the size of the buffer
+ EscherAggregate agg = new EscherAggregate(drawingManager);
+ int loc = locFirstDrawingRecord;
+ int dataSize = 0;
+ while ( loc + 1 < records.size()
+ && sid( records, loc ) == DrawingRecord.sid
+ && isObjectRecord( records, loc + 1 ) )
+ {
+ dataSize += ( (DrawingRecord) records.get( loc ) ).getData().length;
+ loc += 2;
+ }
+
+ // Create one big buffer
+ byte buffer[] = new byte[dataSize];
+ int offset = 0;
+ loc = locFirstDrawingRecord;
+ while ( loc + 1 < records.size()
+ && sid( records, loc ) == DrawingRecord.sid
+ && isObjectRecord( records, loc + 1 ) )
+ {
+ DrawingRecord drawingRecord = (DrawingRecord) records.get( loc );
+ System.arraycopy( drawingRecord.getData(), 0, buffer, offset, drawingRecord.getData().length );
+ offset += drawingRecord.getData().length;
+ loc += 2;
+ }
+
+ // Decode the shapes
+ // agg.escherRecords = new ArrayList();
+ int pos = 0;
+ while ( pos < dataSize )
+ {
+ EscherRecord r = recordFactory.createRecord( buffer, pos );
+ int bytesRead = r.fillFields( buffer, pos, recordFactory );
+ agg.addEscherRecord( r );
+ pos += bytesRead;
+ }
+
+ // Associate the object records with the shapes
+ loc = locFirstDrawingRecord;
+ int shapeIndex = 0;
+ agg.shapeToObj = new HashMap();
+ while ( loc + 1 < records.size()
+ && sid( records, loc ) == DrawingRecord.sid
+ && isObjectRecord( records, loc + 1 ) )
+ {
+ Record objRecord = (Record) records.get( loc + 1 );
+ agg.shapeToObj.put( shapeRecords.get( shapeIndex++ ), objRecord );
+ loc += 2;
+ }
+
+ return agg;
+
+ }
+
+ /**
+ * Serializes this aggregate to a byte array. Since this is an aggregate
+ * record it will effectively serialize the aggregated records.
+ *
+ * @param offset The offset into the start of the array.
+ * @param data The byte array to serialize to.
+ * @return The number of bytes serialized.
+ */
+ public int serialize( int offset, byte[] data )
+ {
+ convertUserModelToRecords();
+
+ // Determine buffer size
+ List records = getEscherRecords();
+ int size = getEscherRecordSize( records );
+ byte[] buffer = new byte[size];
+
+
+ // Serialize escher records into one big data structure and keep note of ending offsets.
+ final List spEndingOffsets = new ArrayList();
+ final List shapes = new ArrayList();
+ int pos = 0;
+ for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord e = (EscherRecord) iterator.next();
+ pos += e.serialize( pos, buffer, new EscherSerializationListener()
+ {
+ public void beforeRecordSerialize( int offset, short recordId, EscherRecord record )
+ {
+ }
+
+ public void afterRecordSerialize( int offset, short recordId, int size, EscherRecord record )
+ {
+ if ( recordId == EscherClientDataRecord.RECORD_ID || recordId == EscherTextboxRecord.RECORD_ID )
+ {
+ spEndingOffsets.add( new Integer( offset ) );
+ shapes.add( record );
+ }
+ }
+ } );
+ }
+ // todo: fix this
+ shapes.add( 0, null );
+ spEndingOffsets.add( 0, null );
+
+ // Split escher records into separate MSODRAWING and OBJ, TXO records. (We don't break on
+ // the first one because it's the patriach).
+ pos = offset;
+ for ( int i = 1; i < shapes.size(); i++ )
+ {
+ int endOffset = ( (Integer) spEndingOffsets.get( i ) ).intValue() - 1;
+ int startOffset;
+ if ( i == 1 )
+ startOffset = 0;
+ else
+ startOffset = ( (Integer) spEndingOffsets.get( i - 1 ) ).intValue();
+
+ // Create and write a new MSODRAWING record
+ DrawingRecord drawing = new DrawingRecord();
+ byte[] drawingData = new byte[endOffset - startOffset + 1];
+ System.arraycopy( buffer, startOffset, drawingData, 0, drawingData.length );
+ drawing.setData( drawingData );
+ int temp = drawing.serialize( pos, data );
+ pos += temp;
+
+ // Write the matching OBJ record
+ Record obj = (Record) shapeToObj.get( shapes.get( i ) );
+ temp = obj.serialize( pos, data );
+ pos += temp;
+
+ }
+
+ int bytesWritten = pos - offset;
+ if ( bytesWritten != getRecordSize() )
+ throw new RecordFormatException( bytesWritten + " bytes written but getRecordSize() reports " + getRecordSize() );
+ return bytesWritten;
+ }
+
+ /**
+ * How many bytes do the raw escher records contain.
+ * @param records List of escher records
+ * @return the number of bytes
+ */
+ private int getEscherRecordSize( List records )
+ {
+ int size = 0;
+ for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
+ size += ( (EscherRecord) iterator.next() ).getRecordSize();
+ return size;
+ }
+
+ /**
+ * The number of bytes required to serialize this record.
+ */
+ public int getRecordSize()
+ {
+ convertUserModelToRecords();
+ List records = getEscherRecords();
+ int rawEscherSize = getEscherRecordSize( records );
+ int drawingRecordSize = rawEscherSize + ( shapeToObj.size() ) * 4;
+ int objRecordSize = 0;
+ for ( Iterator iterator = shapeToObj.values().iterator(); iterator.hasNext(); )
+ {
+ Record r = (Record) iterator.next();
+ objRecordSize += r.getRecordSize();
+ }
+ return drawingRecordSize + objRecordSize;
+ }
+
+ /**
+ * Associates an escher record to an OBJ record or a TXO record.
+ */
+ public Object assoicateShapeToObjRecord( EscherRecord r, Record objRecord )
+ {
+ return shapeToObj.put( r, objRecord );
+ }
+
+ public HSSFPatriarch getPatriarch()
+ {
+ return patriarch;
+ }
+
+ public void setPatriarch( HSSFPatriarch patriarch )
+ {
+ this.patriarch = patriarch;
+ }
+
+ public void clear()
+ {
+ clearEscherRecords();
+ shapeToObj.clear();
+// lastShapeId = 1024;
+ }
+
+ protected String getRecordName()
+ {
+ return "ESCHERAGGREGATE";
+ }
+
+ // =============== Private methods ========================
+
+ private static boolean isObjectRecord( List records, int loc )
+ {
+ return sid( records, loc ) == ObjRecord.sid || sid( records, loc ) == TextObjectRecord.sid;
+ }
+
+ private void convertUserModelToRecords()
+ {
+ if ( patriarch != null )
+ {
+ shapeToObj.clear();
+ clearEscherRecords();
+ if ( patriarch.getChildren().size() != 0 )
+ {
+ convertPatriarch( patriarch );
+ EscherContainerRecord dgContainer = (EscherContainerRecord) getEscherRecord( 0 );
+ EscherContainerRecord spgrContainer = null;
+ for ( int i = 0; i < dgContainer.getChildRecords().size(); i++ )
+ if ( dgContainer.getChild( i ).getRecordId() == EscherContainerRecord.SPGR_CONTAINER )
+ spgrContainer = (EscherContainerRecord) dgContainer.getChild( i );
+ convertShapes( patriarch, spgrContainer, shapeToObj );
+
+ patriarch = null;
+ }
+ }
+ }
+
+ private void convertShapes( HSSFShapeContainer parent, EscherContainerRecord escherParent, Map shapeToObj )
+ {
+ if ( escherParent == null ) throw new IllegalArgumentException( "Parent record required" );
+
+ List shapes = parent.getChildren();
+ for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); )
+ {
+ HSSFShape shape = (HSSFShape) iterator.next();
+ if ( shape instanceof HSSFShapeGroup )
+ {
+ convertGroup( (HSSFShapeGroup) shape, escherParent, shapeToObj );
+ }
+ else
+ {
+ AbstractShape shapeModel = AbstractShape.createShape(
+ shape,
+ drawingManager.allocateShapeId(drawingGroupId) );
+ shapeToObj.put( findClientData( shapeModel.getSpContainer() ), shapeModel.getObjRecord() );
+ if ( shapeModel instanceof TextboxShape )
+ {
+ EscherRecord escherTextbox = ( (TextboxShape) shapeModel ).getEscherTextbox();
+ shapeToObj.put( escherTextbox, ( (TextboxShape) shapeModel ).getTextObjectRecord() );
+ // escherParent.addChildRecord(escherTextbox);
+ }
+ escherParent.addChildRecord( shapeModel.getSpContainer() );
+ }
+ }
+ }
+
+ private void convertGroup( HSSFShapeGroup shape, EscherContainerRecord escherParent, Map shapeToObj )
+ {
+ EscherContainerRecord spgrContainer = new EscherContainerRecord();
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpgrRecord spgr = new EscherSpgrRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherRecord anchor;
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spgrContainer.setRecordId( EscherContainerRecord.SPGR_CONTAINER );
+ spgrContainer.setOptions( (short) 0x000F );
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ spgr.setRecordId( EscherSpgrRecord.RECORD_ID );
+ spgr.setOptions( (short) 0x0001 );
+ spgr.setRectX1( shape.getX1() );
+ spgr.setRectY1( shape.getY1() );
+ spgr.setRectX2( shape.getX2() );
+ spgr.setRectY2( shape.getY2() );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ sp.setOptions( (short) 0x0002 );
+ int shapeId = drawingManager.allocateShapeId(drawingGroupId);
+ sp.setShapeId( shapeId );
+ if (shape.getAnchor() instanceof HSSFClientAnchor)
+ sp.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR );
+ else
+ sp.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_CHILD );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ opt.setOptions( (short) 0x0023 );
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x00040004 ) );
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.GROUPSHAPE__PRINT, 0x00080000 ) );
+
+ anchor = ConvertAnchor.createAnchor( shape.getAnchor() );
+// clientAnchor.setCol1( ( (HSSFClientAnchor) shape.getAnchor() ).getCol1() );
+// clientAnchor.setRow1( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow1() );
+// clientAnchor.setDx1( (short) shape.getAnchor().getDx1() );
+// clientAnchor.setDy1( (short) shape.getAnchor().getDy1() );
+// clientAnchor.setCol2( ( (HSSFClientAnchor) shape.getAnchor() ).getCol2() );
+// clientAnchor.setRow2( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow2() );
+// clientAnchor.setDx2( (short) shape.getAnchor().getDx2() );
+// clientAnchor.setDy2( (short) shape.getAnchor().getDy2() );
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+
+ spgrContainer.addChildRecord( spContainer );
+ spContainer.addChildRecord( spgr );
+ spContainer.addChildRecord( sp );
+ spContainer.addChildRecord( opt );
+ spContainer.addChildRecord( anchor );
+ spContainer.addChildRecord( clientData );
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord cmo = new CommonObjectDataSubRecord();
+ cmo.setObjectType( CommonObjectDataSubRecord.OBJECT_TYPE_GROUP );
+ cmo.setObjectId( (short) ( shapeId ) );
+ cmo.setLocked( true );
+ cmo.setPrintable( true );
+ cmo.setAutofill( true );
+ cmo.setAutoline( true );
+ GroupMarkerSubRecord gmo = new GroupMarkerSubRecord();
+ EndSubRecord end = new EndSubRecord();
+ obj.addSubRecord( cmo );
+ obj.addSubRecord( gmo );
+ obj.addSubRecord( end );
+ shapeToObj.put( clientData, obj );
+
+ escherParent.addChildRecord( spgrContainer );
+
+ convertShapes( shape, spgrContainer, shapeToObj );
+
+ }
+
+ private EscherRecord findClientData( EscherContainerRecord spContainer )
+ {
+ for ( Iterator iterator = spContainer.getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ if ( r.getRecordId() == EscherClientDataRecord.RECORD_ID )
+ return r;
+ }
+ throw new IllegalArgumentException( "Can not find client data record" );
+ }
+
+ private void convertPatriarch( HSSFPatriarch patriarch )
+ {
+ EscherContainerRecord dgContainer = new EscherContainerRecord();
+ EscherDgRecord dg;
+ EscherContainerRecord spgrContainer = new EscherContainerRecord();
+ EscherContainerRecord spContainer1 = new EscherContainerRecord();
+ EscherSpgrRecord spgr = new EscherSpgrRecord();
+ EscherSpRecord sp1 = new EscherSpRecord();
+
+ dgContainer.setRecordId( EscherContainerRecord.DG_CONTAINER );
+ dgContainer.setOptions( (short) 0x000F );
+ dg = drawingManager.createDgRecord();
+ drawingGroupId = dg.getDrawingGroupId();
+// dg.setOptions( (short) ( drawingId << 4 ) );
+// dg.setNumShapes( getNumberOfShapes( patriarch ) );
+// dg.setLastMSOSPID( 0 ); // populated after all shape id's are assigned.
+ spgrContainer.setRecordId( EscherContainerRecord.SPGR_CONTAINER );
+ spgrContainer.setOptions( (short) 0x000F );
+ spContainer1.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer1.setOptions( (short) 0x000F );
+ spgr.setRecordId( EscherSpgrRecord.RECORD_ID );
+ spgr.setOptions( (short) 0x0001 ); // don't know what the 1 is for.
+ spgr.setRectX1( patriarch.getX1() );
+ spgr.setRectY1( patriarch.getY1() );
+ spgr.setRectX2( patriarch.getX2() );
+ spgr.setRectY2( patriarch.getY2() );
+ sp1.setRecordId( EscherSpRecord.RECORD_ID );
+ sp1.setOptions( (short) 0x0002 );
+ sp1.setShapeId( drawingManager.allocateShapeId(dg.getDrawingGroupId()) );
+ sp1.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_PATRIARCH );
+
+ dgContainer.addChildRecord( dg );
+ dgContainer.addChildRecord( spgrContainer );
+ spgrContainer.addChildRecord( spContainer1 );
+ spContainer1.addChildRecord( spgr );
+ spContainer1.addChildRecord( sp1 );
+
+ addEscherRecord( dgContainer );
+ }
+
+ /** Retrieve the number of shapes (including the patriarch). */
+// private int getNumberOfShapes( HSSFPatriarch patriarch )
+// {
+// return patriarch.countOfAllChildren();
+// }
+
+ private static short sid( List records, int loc )
+ {
+ return ( (Record) records.get( loc ) ).getSid();
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/GroupMarkerSubRecord.java b/src/java/org/apache/poi/hssf/record/GroupMarkerSubRecord.java
new file mode 100644
index 0000000000..56209aefd2
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/GroupMarkerSubRecord.java
@@ -0,0 +1,176 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * The group marker record is used as a position holder for groups.
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class GroupMarkerSubRecord
+ extends SubRecord
+{
+ public final static short sid = 0x06;
+
+ private byte[] reserved = new byte[0]; // would really love to know what goes in here.
+
+ public GroupMarkerSubRecord()
+ {
+
+ }
+
+ /**
+ * Constructs a group marker record and sets its fields appropriately.
+ *
+ * @param id id must be 0x00 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public GroupMarkerSubRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a group marker record and sets its fields appropriately.
+ *
+ * @param id id must be 0x00 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public GroupMarkerSubRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a Group Marker record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+// int pos = 0;
+ reserved = new byte[size];
+ System.arraycopy(data, offset, reserved, 0, size);
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ String nl = System.getProperty("line.separator");
+ buffer.append("[ftGmo]" + nl);
+ buffer.append(" reserved = ").append(HexDump.toHex(reserved)).append(nl);
+ buffer.append("[/ftGmo]" + nl);
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+ System.arraycopy(reserved, 0, data, offset + 4, getRecordSize() - 4);
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ return 4 + reserved.length;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public Object clone() {
+ GroupMarkerSubRecord rec = new GroupMarkerSubRecord();
+ rec.reserved = new byte[reserved.length];
+ for ( int i = 0; i < reserved.length; i++ )
+ rec.reserved[i] = reserved[i];
+ return rec;
+ }
+
+
+
+} // END OF CLASS
+
+
diff --git a/src/java/org/apache/poi/hssf/record/HorizontalPageBreakRecord.java b/src/java/org/apache/poi/hssf/record/HorizontalPageBreakRecord.java
new file mode 100644
index 0000000000..b7d1eb5121
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/HorizontalPageBreakRecord.java
@@ -0,0 +1,107 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.hssf.record;
+
+/**
+ * HorizontalPageBreak record that stores page breaks at rows
+ * <p>
+ * This class is just used so that SID compares work properly in the RecordFactory
+ * @see PageBreakRecord
+ * @author Danny Mui (dmui at apache dot org)
+ */
+public class HorizontalPageBreakRecord extends PageBreakRecord {
+
+ public static final short sid = PageBreakRecord.HORIZONTAL_SID;
+
+ /**
+ *
+ */
+ public HorizontalPageBreakRecord() {
+ super();
+ }
+
+ /**
+ * @param sid
+ */
+ public HorizontalPageBreakRecord(short sid) {
+ super(sid);
+ }
+
+ /**
+ * @param id
+ * @param size
+ * @param data
+ */
+ public HorizontalPageBreakRecord(short id, short size, byte[] data) {
+ super(id, size, data);
+ }
+
+ /**
+ * @param id
+ * @param size
+ * @param data
+ * @param offset
+ */
+ public HorizontalPageBreakRecord(short id, short size, byte[] data, int offset) {
+ super(id, size, data, offset);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.poi.hssf.record.Record#getSid()
+ */
+ public short getSid() {
+ return sid;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/ObjRecord.java b/src/java/org/apache/poi/hssf/record/ObjRecord.java
new file mode 100644
index 0000000000..5939b487ab
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/ObjRecord.java
@@ -0,0 +1,227 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * The obj record is used to hold various graphic objects and controls.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class ObjRecord
+ extends Record
+{
+ public final static short sid = 0x5D;
+ private List subrecords;
+
+ //00000000 15 00 12 00 01 00 01 00 11 60 00 00 00 00 00 0D .........`......
+ //00000010 26 01 00 00 00 00 00 00 00 00 &.........
+
+
+ public ObjRecord()
+ {
+ subrecords = new ArrayList(2);
+ }
+
+ /**
+ * Constructs a OBJ record and sets its fields appropriately.
+ *
+ * @param id id must be 0x5D or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public ObjRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+ }
+
+ /**
+ * Constructs a obj record and sets its fields appropriately.
+ *
+ * @param id id must be 0x5D or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+ public ObjRecord(short id, short size, byte[] data, int offset)
+ {
+ super(id, size, data, offset);
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not an OBJ record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+ subrecords = new ArrayList();
+ int pos = offset;
+ while (pos - offset < size)
+ {
+ short subRecordSid = LittleEndian.getShort(data, pos);
+ short subRecordSize = LittleEndian.getShort(data, pos + 2);
+ Record subRecord = SubRecord.createSubRecord(subRecordSid, subRecordSize, data, pos + 4);
+ subrecords.add(subRecord);
+ pos += 4 + subRecordSize;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[OBJ]\n");
+ for ( Iterator iterator = subrecords.iterator(); iterator.hasNext(); )
+ {
+ Record record = (Record) iterator.next();
+ buffer.append("SUBRECORD: " + record.toString());
+ }
+ buffer.append("[/OBJ]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+ pos = offset + 4;
+ for ( Iterator iterator = subrecords.iterator(); iterator.hasNext(); )
+ {
+ Record record = (Record) iterator.next();
+ pos += record.serialize(pos, data);
+ }
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (excluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ int size = 0;
+ for ( Iterator iterator = subrecords.iterator(); iterator.hasNext(); )
+ {
+ Record record = (Record) iterator.next();
+ size += record.getRecordSize();
+ }
+ return 4 + size;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public List getSubRecords()
+ {
+ return subrecords;
+ }
+
+ public void clearSubRecords()
+ {
+ subrecords.clear();
+ }
+
+ public void addSubRecord(int index, Object element)
+ {
+ subrecords.add( index, element );
+ }
+
+ public boolean addSubRecord(Object o)
+ {
+ return subrecords.add( o );
+ }
+
+ public Object clone()
+ {
+ ObjRecord rec = new ObjRecord();
+ rec.subrecords = new ArrayList();
+
+ for ( Iterator iterator = subrecords.iterator(); iterator.hasNext(); )
+ subrecords.add(( (Record) iterator.next() ).clone());
+
+ return rec;
+ }
+
+} // END OF CLASS
+
+
+
+
diff --git a/src/java/org/apache/poi/hssf/record/PageBreakRecord.java b/src/java/org/apache/poi/hssf/record/PageBreakRecord.java
new file mode 100644
index 0000000000..6420e5e400
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/PageBreakRecord.java
@@ -0,0 +1,304 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.hssf.record;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Record that contains the functionality page breaks (horizontal and vertical)
+ * <p>
+ * The other two classes just specifically set the SIDS for record creation
+ * @see HorizontalPageBreakRecord
+ * @see VerticalPageBreakREcord
+ *
+ * REFERENCE: Microsoft Excel SDK page 322 and 420
+ * @author Danny Mui (dmui at apache dot org)
+ */
+public class PageBreakRecord extends Record {
+ public static final short HORIZONTAL_SID = (short)0x1B;
+ public static final short VERTICAL_SID = (short)0x1A;
+ public short sid;
+ private short numBreaks;
+ private List breaks;
+ private Map BreakMap;
+
+ /**
+ * Since both records store 2byte integers (short), no point in
+ * differentiating it in the records.
+ * <p>
+ * The subs (rows or columns, don't seem to be able to set but excel sets
+ * them automatically)
+ */
+ public class Break
+ {
+
+ public short main;
+ public short subFrom;
+ public short subTo;
+
+ public Break(short main, short subFrom, short subTo)
+ {
+ this.main = main;
+ this.subFrom = subFrom;
+ this.subTo = subTo;
+ }
+ }
+
+ public PageBreakRecord()
+ {
+
+ }
+
+ /**
+ *
+ * @param sid
+ */
+ public PageBreakRecord(short sid) {
+ super();
+ this.sid = sid;
+ }
+
+ public PageBreakRecord(short id, short size, byte data[])
+ {
+ super(id, size, data);
+ this.sid = id;
+ }
+
+ public PageBreakRecord(short id, short size, byte data[], int offset)
+ {
+ super(id, size, data, offset);
+ this.sid = id;
+ }
+
+ protected void fillFields(byte data[], short size, int offset)
+ {
+ short loadedBreaks = LittleEndian.getShort(data, 0 + offset);
+ setNumBreaks(loadedBreaks);
+ int pos = 2;
+ for(int k = 0; k < loadedBreaks; k++)
+ {
+ addBreak((short)(LittleEndian.getShort(data, pos + offset) - 1), LittleEndian.getShort(data, pos + 2 + offset), LittleEndian.getShort(data, pos + 4 + offset));
+ pos += 6;
+ }
+
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public int serialize(int offset, byte data[])
+ {
+ int recordsize = getRecordSize();
+ int pos = 6;
+ LittleEndian.putShort(data, offset + 0, getSid());
+ LittleEndian.putShort(data, offset + 2, (short)(recordsize - 4));
+ LittleEndian.putShort(data, offset + 4, getNumBreaks());
+ for(Iterator iterator = getBreaksIterator(); iterator.hasNext();)
+ {
+ Break Break = (Break)iterator.next();
+ LittleEndian.putShort(data, offset + pos, (short)(Break.main + 1));
+ pos += 2;
+ LittleEndian.putShort(data, offset + pos, Break.subFrom);
+ pos += 2;
+ LittleEndian.putShort(data, offset + pos, Break.subTo);
+ pos += 2;
+ }
+
+ return recordsize;
+ }
+
+ protected void validateSid(short id)
+ {
+ if(id != HORIZONTAL_SID && id != VERTICAL_SID)
+ throw new RecordFormatException("NOT A HorizontalPageBreak or VerticalPageBreak RECORD!! " + id);
+ else
+ return;
+ }
+
+ public short getNumBreaks()
+ {
+ return breaks != null ? (short)breaks.size() : numBreaks;
+ }
+
+ public void setNumBreaks(short numBreaks)
+ {
+ this.numBreaks = numBreaks;
+ }
+
+ public Iterator getBreaksIterator()
+ {
+ if(breaks == null)
+ return Collections.EMPTY_LIST.iterator();
+ else
+ return breaks.iterator();
+ }
+
+ public String toString()
+ {
+ StringBuffer retval = new StringBuffer();
+
+ if (getSid() != HORIZONTAL_SID && getSid()!= VERTICAL_SID)
+ return "[INVALIDPAGEBREAK]\n .sid ="+getSid()+"[INVALIDPAGEBREAK]";
+
+ String label;
+ String mainLabel;
+ String subLabel;
+
+ if (getSid() == HORIZONTAL_SID) {
+ label = "HORIZONTALPAGEBREAK";
+ mainLabel = "row";
+ subLabel = "col";
+ } else {
+ label = "VERTICALPAGEBREAK";
+ mainLabel = "column";
+ subLabel = "row";
+ }
+
+ retval.append("["+label+"]").append("\n");
+ retval.append(" .sid =").append(getSid()).append("\n");
+ retval.append(" .numbreaks =").append(getNumBreaks()).append("\n");
+ Iterator iterator = getBreaksIterator();
+ for(int k = 0; k < getNumBreaks(); k++)
+ {
+ Break region = (Break)iterator.next();
+
+ retval.append(" .").append(mainLabel).append(" (zero-based) =").append(region.main).append("\n");
+ retval.append(" .").append(subLabel).append("From =").append(region.subFrom).append("\n");
+ retval.append(" .").append(subLabel).append("To =").append(region.subTo).append("\n");
+ }
+
+ retval.append("["+label+"]").append("\n");
+ return retval.toString();
+ }
+
+ /**
+ * Adds the page break at the specified parameters
+ * @param main Depending on sid, will determine row or column to put page break (zero-based)
+ * @param subFrom No user-interface to set (defaults to minumum, 0)
+ * @param subTo No user-interface to set
+ */
+ public void addBreak(short main, short subFrom, short subTo)
+ {
+ if(breaks == null)
+ {
+ breaks = new ArrayList(getNumBreaks() + 10);
+ BreakMap = new HashMap();
+ }
+ Integer key = new Integer(main);
+ Break region = (Break)BreakMap.get(key);
+ if(region != null)
+ {
+ region.main = main;
+ region.subFrom = subFrom;
+ region.subTo = subTo;
+ } else
+ {
+ region = new Break(main, subFrom, subTo);
+ breaks.add(region);
+ }
+ BreakMap.put(key, region);
+ }
+
+ /**
+ * Removes the break indicated by the parameter
+ * @param main (zero-based)
+ */
+ public void removeBreak(short main)
+ {
+ Integer rowKey = new Integer(main);
+ Break region = (Break)BreakMap.get(rowKey);
+ breaks.remove(region);
+ BreakMap.remove(rowKey);
+ }
+
+ public int getRecordSize()
+ {
+ return 6 + getNumBreaks() * 6;
+ }
+
+ /**
+ * Retrieves the region at the row/column indicated
+ * @param main
+ * @return
+ */
+ public Break getBreak(short main)
+ {
+ Integer rowKey = new Integer(main);
+ return (Break)BreakMap.get(rowKey);
+ }
+
+ /* Clones the page break record
+ * @see java.lang.Object#clone()
+ */
+ public Object clone() {
+ PageBreakRecord record = new PageBreakRecord(getSid());
+ Iterator iterator = getBreaksIterator();
+ while (iterator.hasNext()) {
+ Break original = (Break)iterator.next();
+ record.addBreak(original.main, original.subFrom, original.subTo);
+ }
+ return record;
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java
index f12f903b06..f2b5b28f00 100644
--- a/src/java/org/apache/poi/hssf/record/RecordFactory.java
+++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java
@@ -112,7 +112,10 @@ public class RecordFactory
FormulaRecord.class, BoolErrRecord.class, ExternSheetRecord.class,
NameRecord.class, LeftMarginRecord.class, RightMarginRecord.class,
TopMarginRecord.class, BottomMarginRecord.class,
- PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class
+ PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
+ DrawingRecord.class, DrawingGroupRecord.class, DrawingSelectionRecord.class,
+ ObjRecord.class, TextObjectRecord.class,
+ HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class
};
} else {
records = new Class[]
@@ -143,7 +146,10 @@ public class RecordFactory
BoolErrRecord.class, ExternSheetRecord.class, NameRecord.class,
LeftMarginRecord.class, RightMarginRecord.class,
TopMarginRecord.class, BottomMarginRecord.class,
- PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class
+ PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
+ DrawingRecord.class, DrawingGroupRecord.class, DrawingSelectionRecord.class,
+ ObjRecord.class, TextObjectRecord.class,
+ HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class
};
}
diff --git a/src/java/org/apache/poi/hssf/record/SubRecord.java b/src/java/org/apache/poi/hssf/record/SubRecord.java
new file mode 100644
index 0000000000..6b27eb3e03
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/SubRecord.java
@@ -0,0 +1,98 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.hssf.record;
+
+/**
+ * Subrecords are part of the OBJ class.
+ */
+abstract public class SubRecord
+ extends Record
+{
+ public SubRecord()
+ {
+ }
+
+ public SubRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public SubRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ public static Record createSubRecord( short subRecordSid, short size, byte[] data, int offset )
+ {
+ Record r = null;
+
+ switch ( subRecordSid )
+ {
+ case CommonObjectDataSubRecord.sid:
+ r = new CommonObjectDataSubRecord( subRecordSid, size, data, offset );
+ break;
+ case GroupMarkerSubRecord.sid:
+ r = new GroupMarkerSubRecord( subRecordSid, size, data, offset );
+ break;
+ case EndSubRecord.sid:
+ r = new EndSubRecord( subRecordSid, size, data, offset );
+ break;
+ default:
+ r = new UnknownRecord( subRecordSid, size, data, offset );
+ }
+
+ return r;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java b/src/java/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java
new file mode 100644
index 0000000000..2ffe2c2ce2
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java
@@ -0,0 +1,127 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the serialization and deserialization of the CommonObjectDataSubRecord
+ * class works correctly. Test data taken directly from a real
+ * Excel file.
+ *
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestCommonObjectDataSubRecord
+ extends TestCase
+{
+ byte[] data = new byte[] {
+ (byte)0x12,(byte)0x00,(byte)0x01,(byte)0x00,
+ (byte)0x01,(byte)0x00,(byte)0x11,(byte)0x60,
+ (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
+ (byte)0x00,(byte)0x0D,(byte)0x26,(byte)0x01,
+ (byte)0x00,(byte)0x00,
+ };
+
+ public TestCommonObjectDataSubRecord(String name)
+ {
+ super(name);
+ }
+
+ public void testLoad()
+ throws Exception
+ {
+ CommonObjectDataSubRecord record = new CommonObjectDataSubRecord((short)0x15, (short)data.length, data);
+
+
+ assertEquals( CommonObjectDataSubRecord.OBJECT_TYPE_LIST_BOX, record.getObjectType());
+ assertEquals( (short)1, record.getObjectId());
+ assertEquals( (short)1, record.getOption());
+ assertEquals( true , record.isLocked() );
+ assertEquals( false, record.isPrintable() );
+ assertEquals( false, record.isAutofill() );
+ assertEquals( false, record.isAutoline() );
+ assertEquals( (int)24593, record.getReserved1());
+ assertEquals( (int)218103808, record.getReserved2());
+ assertEquals( (int)294, record.getReserved3());
+ assertEquals( 22 , record.getRecordSize() );
+
+ record.validateSid((short)0x15);
+ }
+
+ public void testStore()
+ {
+ CommonObjectDataSubRecord record = new CommonObjectDataSubRecord();
+
+ record.setObjectType( CommonObjectDataSubRecord.OBJECT_TYPE_LIST_BOX );
+ record.setObjectId( (short) 1);
+ record.setOption( (short) 1);
+ record.setLocked( true );
+ record.setPrintable( false );
+ record.setAutofill( false );
+ record.setAutoline( false );
+ record.setReserved1( (int) 24593);
+ record.setReserved2( (int) 218103808);
+ record.setReserved3( (int) 294);
+
+ byte [] recordBytes = record.serialize();
+ assertEquals(recordBytes.length - 4, data.length);
+ for (int i = 0; i < data.length; i++)
+ assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestDrawingGroupRecord.java b/src/java/org/apache/poi/hssf/record/TestDrawingGroupRecord.java
new file mode 100644
index 0000000000..4c42eca886
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestDrawingGroupRecord.java
@@ -0,0 +1,36 @@
+package org.apache.poi.hssf.record;
+
+import junit.framework.TestCase;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherSpRecord;
+import org.apache.poi.util.HexDump;
+
+public class TestDrawingGroupRecord extends TestCase
+{
+ public void testGetRecordSize()
+ throws Exception
+ {
+ DrawingGroupRecord r = new DrawingGroupRecord();
+ assertEquals(4, r.getRecordSize());
+
+ EscherSpRecord sp = new EscherSpRecord();
+ sp.setRecordId(EscherSpRecord.RECORD_ID);
+ sp.setOptions((short) 0x1111);
+ sp.setFlags(-1);
+ sp.setShapeId(-1);
+ EscherContainerRecord dggContainer = new EscherContainerRecord();
+ dggContainer.setOptions((short) 0x000F);
+ dggContainer.setRecordId((short) 0xF000);
+ dggContainer.addChildRecord(sp);
+
+ r.addEscherRecord(dggContainer);
+ assertEquals(28, r.getRecordSize());
+
+ byte[] data = new byte[28];
+ int size = r.serialize(0, data);
+ assertEquals("[EB, 00, 18, 00, 0F, 00, 00, F0, 10, 00, 00, 00, 11, 11, 0A, F0, 08, 00, 00, 00, FF, FF, FF, FF, FF, FF, FF, FF, ]", HexDump.toHex(data));
+ assertEquals(28, size);
+
+ assertEquals(24, dggContainer.getRecordSize());
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestEndSubRecord.java b/src/java/org/apache/poi/hssf/record/TestEndSubRecord.java
new file mode 100644
index 0000000000..d338601948
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestEndSubRecord.java
@@ -0,0 +1,102 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the serialization and deserialization of the EndSubRecord
+ * class works correctly. Test data taken directly from a real
+ * Excel file.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestEndSubRecord
+ extends TestCase
+{
+ byte[] data = new byte[] {
+
+ };
+
+ public TestEndSubRecord(String name)
+ {
+ super(name);
+ }
+
+ public void testLoad()
+ throws Exception
+ {
+ EndSubRecord record = new EndSubRecord((short)0x00, (short)data.length, data);
+
+
+
+ assertEquals( 4, record.getRecordSize() );
+
+ record.validateSid((short)0x00);
+ }
+
+ public void testStore()
+ {
+ EndSubRecord record = new EndSubRecord();
+
+ byte [] recordBytes = record.serialize();
+ assertEquals(recordBytes.length - 4, data.length);
+ for (int i = 0; i < data.length; i++)
+ assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestEscherAggregate.java b/src/java/org/apache/poi/hssf/record/TestEscherAggregate.java
new file mode 100644
index 0000000000..f7acf2f0d5
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestEscherAggregate.java
@@ -0,0 +1,124 @@
+package org.apache.poi.hssf.record;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexRead;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherSpRecord;
+import org.apache.poi.ddf.EscherClientDataRecord;
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.hssf.model.DrawingManager;
+import org.apache.poi.hssf.model.Sheet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests the EscherAggregate class.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestEscherAggregate extends TestCase
+{
+ /**
+ * Tests that the create aggregate method correctly rejoins escher records together.
+ *
+ * @throws Exception
+ */
+ public void testCreateAggregate() throws Exception
+ {
+ String msoDrawingRecord1 =
+ "0F 00 02 F0 20 01 00 00 10 00 08 F0 08 00 00 00 \n" +
+ "03 00 00 00 02 04 00 00 0F 00 03 F0 08 01 00 00 \n" +
+ "0F 00 04 F0 28 00 00 00 01 00 09 F0 10 00 00 00 \n" +
+ "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" +
+ "02 00 0A F0 08 00 00 00 00 04 00 00 05 00 00 00 \n" +
+ "0F 00 04 F0 64 00 00 00 42 01 0A F0 08 00 00 00 \n" +
+ "01 04 00 00 00 0A 00 00 73 00 0B F0 2A 00 00 00 \n" +
+ "BF 00 08 00 08 00 44 01 04 00 00 00 7F 01 00 00 \n" +
+ "01 00 BF 01 00 00 11 00 C0 01 40 00 00 08 FF 01 \n" +
+ "10 00 10 00 BF 03 00 00 08 00 00 00 10 F0 12 00 \n" +
+ "00 00 00 00 01 00 54 00 05 00 45 00 01 00 88 03 \n" +
+ "05 00 94 00 00 00 11 F0 00 00 00 00";
+
+ String msoDrawingRecord2 =
+ "0F 00 04 F0 64 00 00 00 42 01 0A F0 08 00 00 00 " +
+ "02 04 00 00 80 0A 00 00 73 00 0B F0 2A 00 00 00 " +
+ "BF 00 08 00 08 00 44 01 04 00 00 00 7F 01 00 00 " +
+ "01 00 BF 01 00 00 11 00 C0 01 40 00 00 08 FF 01 " +
+ "10 00 10 00 BF 03 00 00 08 00 00 00 10 F0 12 00 " +
+ "00 00 00 00 01 00 8D 03 05 00 E4 00 03 00 4D 03 " +
+ "0B 00 0C 00 00 00 11 F0 00 00 00 00";
+
+ DrawingRecord d1 = new DrawingRecord();
+ d1.setData( HexRead.readFromString( msoDrawingRecord1 ) );
+
+ ObjRecord r1 = new ObjRecord();
+
+ DrawingRecord d2 = new DrawingRecord();
+ d2.setData( HexRead.readFromString( msoDrawingRecord2 ) );
+
+ ObjRecord r2 = new ObjRecord();
+
+ List records = new ArrayList();
+ records.add( d1 );
+ records.add( r1 );
+ records.add( d2 );
+ records.add( r2 );
+
+ DrawingManager drawingManager = new DrawingManager(new EscherDggRecord() );
+ EscherAggregate aggregate = EscherAggregate.createAggregate( records, 0, drawingManager );
+
+ assertEquals( 1, aggregate.getEscherRecords().size() );
+ assertEquals( (short) 0xF002, aggregate.getEscherRecord( 0 ).getRecordId() );
+ assertEquals( 2, aggregate.getEscherRecord( 0 ).getChildRecords().size() );
+
+// System.out.println( "aggregate = " + aggregate );
+ }
+
+ public void testSerialize() throws Exception
+ {
+
+ EscherContainerRecord container1 = new EscherContainerRecord();
+ EscherContainerRecord spContainer1 = new EscherContainerRecord();
+ EscherContainerRecord spContainer2 = new EscherContainerRecord();
+ EscherContainerRecord spContainer3 = new EscherContainerRecord();
+ EscherSpRecord sp1 = new EscherSpRecord();
+ EscherSpRecord sp2 = new EscherSpRecord();
+ EscherSpRecord sp3 = new EscherSpRecord();
+ EscherClientDataRecord d2 = new EscherClientDataRecord();
+ EscherClientDataRecord d3 = new EscherClientDataRecord();
+
+ container1.setOptions( (short) 0x000F );
+ spContainer1.setOptions( (short) 0x000F );
+ spContainer1.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer2.setOptions( (short) 0x000F );
+ spContainer2.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer3.setOptions( (short) 0x000F );
+ spContainer3.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ d2.setRecordId( EscherClientDataRecord.RECORD_ID );
+ d2.setRemainingData( new byte[0] );
+ d3.setRecordId( EscherClientDataRecord.RECORD_ID );
+ d3.setRemainingData( new byte[0] );
+ container1.addChildRecord( spContainer1 );
+ container1.addChildRecord( spContainer2 );
+ container1.addChildRecord( spContainer3 );
+ spContainer1.addChildRecord( sp1 );
+ spContainer2.addChildRecord( sp2 );
+ spContainer3.addChildRecord( sp3 );
+ spContainer2.addChildRecord( d2 );
+ spContainer3.addChildRecord( d3 );
+
+ EscherAggregate aggregate = new EscherAggregate(null);
+ aggregate.addEscherRecord( container1 );
+ aggregate.assoicateShapeToObjRecord( d2, new ObjRecord() );
+ aggregate.assoicateShapeToObjRecord( d3, new ObjRecord() );
+
+ byte[] data = new byte[112];
+ int bytesWritten = aggregate.serialize( 0, data );
+ assertEquals( 112, bytesWritten );
+ assertEquals( "[EC, 00, 40, 00, 0F, 00, 00, 00, 58, 00, 00, 00, 0F, 00, 04, F0, 10, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, EC, 00, 20, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, ]",
+ HexDump.toHex( data ) );
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java b/src/java/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java
new file mode 100644
index 0000000000..1e95653aef
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java
@@ -0,0 +1,137 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the serialization and deserialization of the TextObjectBaseRecord
+ * class works correctly. Test data taken directly from a real
+ * Excel file.
+ *
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestTextObjectBaseRecord
+ extends TestCase
+{
+ byte[] data = new byte[] {
+ 0x44, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
+ public TestTextObjectBaseRecord(String name)
+ {
+ super(name);
+ }
+
+ public void testLoad()
+ throws Exception
+ {
+ TextObjectBaseRecord record = new TextObjectBaseRecord((short)0x1B6, (short)data.length, data);
+
+
+// assertEquals( (short), record.getOptions());
+ assertEquals( false, record.isReserved1() );
+ assertEquals( TextObjectBaseRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment() );
+ assertEquals( TextObjectBaseRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment() );
+ assertEquals( 0, record.getReserved2() );
+ assertEquals( true, record.isTextLocked() );
+ assertEquals( 0, record.getReserved3() );
+ assertEquals( TextObjectBaseRecord.TEXT_ORIENTATION_ROT_RIGHT, record.getTextOrientation());
+ assertEquals( 0, record.getReserved4());
+ assertEquals( 0, record.getReserved5());
+ assertEquals( 0, record.getReserved6());
+ assertEquals( 2, record.getTextLength());
+ assertEquals( 2, record.getFormattingRunLength());
+ assertEquals( 0, record.getReserved7());
+
+
+ assertEquals( 22, record.getRecordSize() );
+
+ record.validateSid((short)0x1B6);
+ }
+
+ public void testStore()
+ {
+ TextObjectBaseRecord record = new TextObjectBaseRecord();
+
+
+
+// record.setOptions( (short) 0x0000);
+ record.setReserved1( false );
+ record.setHorizontalTextAlignment( TextObjectBaseRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED );
+ record.setVerticalTextAlignment( TextObjectBaseRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY );
+ record.setReserved2( (short)0 );
+ record.setTextLocked( true );
+ record.setReserved3( (short)0 );
+ record.setTextOrientation( TextObjectBaseRecord.TEXT_ORIENTATION_ROT_RIGHT );
+ record.setReserved4( (short)0 );
+ record.setReserved5( (short)0 );
+ record.setReserved6( (short)0 );
+ record.setTextLength( (short)2 );
+ record.setFormattingRunLength( (short)2 );
+ record.setReserved7( 0 );
+
+ byte [] recordBytes = record.serialize();
+ assertEquals(recordBytes.length - 4, data.length);
+ for (int i = 0; i < data.length; i++)
+ assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java b/src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java
new file mode 100644
index 0000000000..21d83a0c87
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java
@@ -0,0 +1,519 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * The TXO record is used to define the properties of a text box. It is followed
+ by two continue records unless there is no actual text. The first continue record contains
+ the text data and the next continue record contains the formatting runs.
+ * NOTE: This source is automatically generated please do not modify this file. Either subclass or
+ * remove the record in src/records/definitions.
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TextObjectBaseRecord
+ extends Record
+{
+ public final static short sid = 0x1B6;
+ private short field_1_options;
+ private BitField reserved1 = new BitField(0x1);
+ private BitField HorizontalTextAlignment = new BitField(0x000E);
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
+ private BitField VerticalTextAlignment = new BitField(0x0070);
+ public final static short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
+ public final static short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
+ public final static short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
+ public final static short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
+ private BitField reserved2 = new BitField(0x0180);
+ private BitField textLocked = new BitField(0x200);
+ private BitField reserved3 = new BitField(0xFC00);
+ private short field_2_textOrientation;
+ public final static short TEXT_ORIENTATION_NONE = 0;
+ public final static short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
+ public final static short TEXT_ORIENTATION_ROT_RIGHT = 2;
+ public final static short TEXT_ORIENTATION_ROT_LEFT = 3;
+ private short field_3_reserved4;
+ private short field_4_reserved5;
+ private short field_5_reserved6;
+ private short field_6_textLength;
+ private short field_7_formattingRunLength;
+ private int field_8_reserved7;
+
+
+ public TextObjectBaseRecord()
+ {
+
+ }
+
+ /**
+ * Constructs a TextObjectBase record and sets its fields appropriately.
+ *
+ * @param id id must be 0x1B6 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public TextObjectBaseRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a TextObjectBase record and sets its fields appropriately.
+ *
+ * @param id id must be 0x1B6 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public TextObjectBaseRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a TextObjectBase record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+
+ int pos = 0;
+ field_1_options = LittleEndian.getShort(data, pos + 0x0 + offset);
+ field_2_textOrientation = LittleEndian.getShort(data, pos + 0x2 + offset);
+ field_3_reserved4 = LittleEndian.getShort(data, pos + 0x4 + offset);
+ field_4_reserved5 = LittleEndian.getShort(data, pos + 0x6 + offset);
+ field_5_reserved6 = LittleEndian.getShort(data, pos + 0x8 + offset);
+ field_6_textLength = LittleEndian.getShort(data, pos + 0xa + offset);
+ field_7_formattingRunLength = LittleEndian.getShort(data, pos + 0xc + offset);
+ field_8_reserved7 = LittleEndian.getInt(data, pos + 0xe + offset);
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[TXO]\n");
+ buffer.append(" .options = ")
+ .append("0x").append(HexDump.toHex( getOptions ()))
+ .append(" (").append( getOptions() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved1 = ").append(isReserved1()).append('\n');
+ buffer.append(" .HorizontalTextAlignment = ").append(getHorizontalTextAlignment()).append('\n');
+ buffer.append(" .VerticalTextAlignment = ").append(getVerticalTextAlignment()).append('\n');
+ buffer.append(" .reserved2 = ").append(getReserved2()).append('\n');
+ buffer.append(" .textLocked = ").append(isTextLocked()).append('\n');
+ buffer.append(" .reserved3 = ").append(getReserved3()).append('\n');
+ buffer.append(" .textOrientation = ")
+ .append("0x").append(HexDump.toHex( getTextOrientation ()))
+ .append(" (").append( getTextOrientation() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved4 = ")
+ .append("0x").append(HexDump.toHex( getReserved4 ()))
+ .append(" (").append( getReserved4() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved5 = ")
+ .append("0x").append(HexDump.toHex( getReserved5 ()))
+ .append(" (").append( getReserved5() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved6 = ")
+ .append("0x").append(HexDump.toHex( getReserved6 ()))
+ .append(" (").append( getReserved6() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .textLength = ")
+ .append("0x").append(HexDump.toHex( getTextLength ()))
+ .append(" (").append( getTextLength() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .formattingRunLength = ")
+ .append("0x").append(HexDump.toHex( getFormattingRunLength ()))
+ .append(" (").append( getFormattingRunLength() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved7 = ")
+ .append("0x").append(HexDump.toHex( getReserved7 ()))
+ .append(" (").append( getReserved7() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+
+ buffer.append("[/TXO]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+ LittleEndian.putShort(data, 4 + offset + pos, field_1_options);
+ LittleEndian.putShort(data, 6 + offset + pos, field_2_textOrientation);
+ LittleEndian.putShort(data, 8 + offset + pos, field_3_reserved4);
+ LittleEndian.putShort(data, 10 + offset + pos, field_4_reserved5);
+ LittleEndian.putShort(data, 12 + offset + pos, field_5_reserved6);
+ LittleEndian.putShort(data, 14 + offset + pos, field_6_textLength);
+ LittleEndian.putShort(data, 16 + offset + pos, field_7_formattingRunLength);
+ LittleEndian.putInt(data, 18 + offset + pos, field_8_reserved7);
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ return 4 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 4;
+ }
+
+ public short getSid()
+ {
+ return this.sid;
+ }
+
+ public Object clone() {
+ TextObjectBaseRecord rec = new TextObjectBaseRecord();
+
+ rec.field_1_options = field_1_options;
+ rec.field_2_textOrientation = field_2_textOrientation;
+ rec.field_3_reserved4 = field_3_reserved4;
+ rec.field_4_reserved5 = field_4_reserved5;
+ rec.field_5_reserved6 = field_5_reserved6;
+ rec.field_6_textLength = field_6_textLength;
+ rec.field_7_formattingRunLength = field_7_formattingRunLength;
+ rec.field_8_reserved7 = field_8_reserved7;
+ return rec;
+ }
+
+
+
+
+ /**
+ * Get the options field for the TextObjectBase record.
+ */
+ public short getOptions()
+ {
+ return field_1_options;
+ }
+
+ /**
+ * Set the options field for the TextObjectBase record.
+ */
+ public void setOptions(short field_1_options)
+ {
+ this.field_1_options = field_1_options;
+ }
+
+ /**
+ * Get the text orientation field for the TextObjectBase record.
+ *
+ * @return One of
+ * TEXT_ORIENTATION_NONE
+ * TEXT_ORIENTATION_TOP_TO_BOTTOM
+ * TEXT_ORIENTATION_ROT_RIGHT
+ * TEXT_ORIENTATION_ROT_LEFT
+ */
+ public short getTextOrientation()
+ {
+ return field_2_textOrientation;
+ }
+
+ /**
+ * Set the text orientation field for the TextObjectBase record.
+ *
+ * @param field_2_textOrientation
+ * One of
+ * TEXT_ORIENTATION_NONE
+ * TEXT_ORIENTATION_TOP_TO_BOTTOM
+ * TEXT_ORIENTATION_ROT_RIGHT
+ * TEXT_ORIENTATION_ROT_LEFT
+ */
+ public void setTextOrientation(short field_2_textOrientation)
+ {
+ this.field_2_textOrientation = field_2_textOrientation;
+ }
+
+ /**
+ * Get the reserved4 field for the TextObjectBase record.
+ */
+ public short getReserved4()
+ {
+ return field_3_reserved4;
+ }
+
+ /**
+ * Set the reserved4 field for the TextObjectBase record.
+ */
+ public void setReserved4(short field_3_reserved4)
+ {
+ this.field_3_reserved4 = field_3_reserved4;
+ }
+
+ /**
+ * Get the reserved5 field for the TextObjectBase record.
+ */
+ public short getReserved5()
+ {
+ return field_4_reserved5;
+ }
+
+ /**
+ * Set the reserved5 field for the TextObjectBase record.
+ */
+ public void setReserved5(short field_4_reserved5)
+ {
+ this.field_4_reserved5 = field_4_reserved5;
+ }
+
+ /**
+ * Get the reserved6 field for the TextObjectBase record.
+ */
+ public short getReserved6()
+ {
+ return field_5_reserved6;
+ }
+
+ /**
+ * Set the reserved6 field for the TextObjectBase record.
+ */
+ public void setReserved6(short field_5_reserved6)
+ {
+ this.field_5_reserved6 = field_5_reserved6;
+ }
+
+ /**
+ * Get the text length field for the TextObjectBase record.
+ */
+ public short getTextLength()
+ {
+ return field_6_textLength;
+ }
+
+ /**
+ * Set the text length field for the TextObjectBase record.
+ */
+ public void setTextLength(short field_6_textLength)
+ {
+ this.field_6_textLength = field_6_textLength;
+ }
+
+ /**
+ * Get the formatting run length field for the TextObjectBase record.
+ */
+ public short getFormattingRunLength()
+ {
+ return field_7_formattingRunLength;
+ }
+
+ /**
+ * Set the formatting run length field for the TextObjectBase record.
+ */
+ public void setFormattingRunLength(short field_7_formattingRunLength)
+ {
+ this.field_7_formattingRunLength = field_7_formattingRunLength;
+ }
+
+ /**
+ * Get the reserved7 field for the TextObjectBase record.
+ */
+ public int getReserved7()
+ {
+ return field_8_reserved7;
+ }
+
+ /**
+ * Set the reserved7 field for the TextObjectBase record.
+ */
+ public void setReserved7(int field_8_reserved7)
+ {
+ this.field_8_reserved7 = field_8_reserved7;
+ }
+
+ /**
+ * Sets the reserved1 field value.
+ * reserved field
+ */
+ public void setReserved1(boolean value)
+ {
+ field_1_options = reserved1.setShortBoolean(field_1_options, value);
+ }
+
+ /**
+ * reserved field
+ * @return the reserved1 field value.
+ */
+ public boolean isReserved1()
+ {
+ return reserved1.isSet(field_1_options);
+ }
+
+ /**
+ * Sets the Horizontal text alignment field value.
+ *
+ */
+ public void setHorizontalTextAlignment(short value)
+ {
+ field_1_options = HorizontalTextAlignment.setShortValue(field_1_options, value);
+ }
+
+ /**
+ *
+ * @return the Horizontal text alignment field value.
+ */
+ public short getHorizontalTextAlignment()
+ {
+ return HorizontalTextAlignment.getShortValue(field_1_options);
+ }
+
+ /**
+ * Sets the Vertical text alignment field value.
+ *
+ */
+ public void setVerticalTextAlignment(short value)
+ {
+ field_1_options = VerticalTextAlignment.setShortValue(field_1_options, value);
+ }
+
+ /**
+ *
+ * @return the Vertical text alignment field value.
+ */
+ public short getVerticalTextAlignment()
+ {
+ return VerticalTextAlignment.getShortValue(field_1_options);
+ }
+
+ /**
+ * Sets the reserved2 field value.
+ *
+ */
+ public void setReserved2(short value)
+ {
+ field_1_options = reserved2.setShortValue(field_1_options, value);
+ }
+
+ /**
+ *
+ * @return the reserved2 field value.
+ */
+ public short getReserved2()
+ {
+ return reserved2.getShortValue(field_1_options);
+ }
+
+ /**
+ * Sets the text locked field value.
+ * Text has been locked
+ */
+ public void setTextLocked(boolean value)
+ {
+ field_1_options = textLocked.setShortBoolean(field_1_options, value);
+ }
+
+ /**
+ * Text has been locked
+ * @return the text locked field value.
+ */
+ public boolean isTextLocked()
+ {
+ return textLocked.isSet(field_1_options);
+ }
+
+ /**
+ * Sets the reserved3 field value.
+ *
+ */
+ public void setReserved3(short value)
+ {
+ field_1_options = reserved3.setShortValue(field_1_options, value);
+ }
+
+ /**
+ *
+ * @return the reserved3 field value.
+ */
+ public short getReserved3()
+ {
+ return reserved3.getShortValue(field_1_options);
+ }
+
+
+} // END OF CLASS
+
+
+
+
diff --git a/src/java/org/apache/poi/hssf/record/TextObjectRecord.java b/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
new file mode 100644
index 0000000000..e5652e802c
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
@@ -0,0 +1,216 @@
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.util.StringUtil;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.HexDump;
+import java.io.UnsupportedEncodingException;
+
+public class TextObjectRecord
+ extends TextObjectBaseRecord
+{
+ HSSFRichTextString str = new HSSFRichTextString( "" );
+ int continueRecordCount = 0; // how many times has continue record been called?
+
+ public TextObjectRecord()
+ {
+ }
+
+ public TextObjectRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public TextObjectRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ public int getRecordSize()
+ {
+ int continue1Size = 0;
+ int continue2Size = 0;
+ if (str.length() != 0)
+ {
+ continue1Size = str.length() * 2 + 1 + 4;
+ continue2Size = (str.numFormattingRuns() + 1) * 8 + 4;
+ }
+ return super.getRecordSize() + continue1Size + continue2Size;
+ }
+
+ public int serialize( int offset, byte[] data )
+ {
+ // Temporarily blank out str so that record size is calculated without the continue records.
+ HSSFRichTextString temp = str;
+ str = new HSSFRichTextString("");
+ int bytesWritten1 = super.serialize( offset, data );
+ str = temp;
+
+ int pos = offset + bytesWritten1;
+ if ( str.toString().equals( "" ) == false )
+ {
+ ContinueRecord c1 = createContinue1();
+ ContinueRecord c2 = createContinue2();
+ int bytesWritten2 = c1.serialize( pos, data );
+ pos += bytesWritten2;
+ int bytesWritten3 = c2.serialize( pos, data );
+ pos += bytesWritten3;
+
+ int size = bytesWritten1 + bytesWritten2 + bytesWritten3;
+ if ( size != getRecordSize() )
+ throw new RecordFormatException(size + " bytes written but getRecordSize() reports " + getRecordSize());
+ return size;
+ }
+ if ( bytesWritten1 != getRecordSize() )
+ throw new RecordFormatException(bytesWritten1 + " bytes written but getRecordSize() reports " + getRecordSize());
+ return bytesWritten1;
+ }
+
+ private ContinueRecord createContinue1()
+ {
+ ContinueRecord c1 = new ContinueRecord();
+ byte[] c1Data = new byte[str.length() * 2 + 1];
+ try
+ {
+ c1Data[0] = 1;
+ System.arraycopy( str.toString().getBytes( "UTF-16LE" ), 0, c1Data, 1, str.length() * 2 );
+ }
+ catch ( UnsupportedEncodingException e )
+ {
+ throw new RuntimeException( e.getMessage() );
+ }
+ c1.setData( c1Data );
+ return c1;
+ }
+
+ private ContinueRecord createContinue2()
+ {
+ ContinueRecord c2 = new ContinueRecord();
+ byte[] c2Data = new byte[str.numFormattingRuns() * 8 + 8];
+ int pos = 0;
+ for ( int i = 0; i < str.numFormattingRuns(); i++ )
+ {
+ LittleEndian.putShort( c2Data, pos, (short) str.getIndexOfFormattingRun( i ) );
+ pos += 2;
+ LittleEndian.putShort( c2Data, pos, str.getFontOfFormattingRun( i ) == str.NO_FONT ? 0 : str.getFontOfFormattingRun( i ) );
+ pos += 2;
+ pos += 4; // skip reserved
+ }
+ LittleEndian.putShort( c2Data, pos, (short) str.length() );
+ pos += 2;
+ LittleEndian.putShort( c2Data, pos, (short) 0 );
+ pos += 2;
+ pos += 4; // skip reserved
+
+ c2.setData( c2Data );
+
+ return c2;
+ }
+
+ public void processContinueRecord( byte[] data )
+ {
+ if ( continueRecordCount == 0 )
+ processRawString( data );
+ else
+ processFontRuns( data );
+ continueRecordCount++;
+ }
+
+ private void processFontRuns( byte[] data )
+ {
+ int pos = 0;
+ do
+ {
+ short index = LittleEndian.getShort( data, pos );
+ pos += 2;
+ short iFont = LittleEndian.getShort( data, pos );
+ pos += 2;
+ pos += 4; // skip reserved.
+
+ if ( index >= str.length() )
+ break;
+ str.applyFont( index, str.length(), iFont );
+ }
+ while ( true );
+ }
+
+ private void processRawString( byte[] data )
+ {
+ String s;
+ int pos = 0;
+ byte compressByte = data[pos++];
+ boolean isCompressed = compressByte == 0;
+ try
+ {
+ if ( isCompressed )
+ {
+ s = new String( data, pos, getTextLength(), StringUtil.getPreferredEncoding() );
+ }
+ else
+ {
+ s = new String( data, pos, getTextLength() * 2, "UTF-16LE" );
+ }
+ }
+ catch ( UnsupportedEncodingException e )
+ {
+ throw new RuntimeException( e.getMessage() );
+ }
+ str = new HSSFRichTextString( s );
+ }
+
+ public HSSFRichTextString getStr()
+ {
+ return str;
+ }
+
+ public void setStr( HSSFRichTextString str )
+ {
+ this.str = str;
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append( "[TXO]\n" );
+ buffer.append( " .options = " )
+ .append( "0x" ).append( HexDump.toHex( getOptions() ) )
+ .append( " (" ).append( getOptions() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved1 = " ).append( isReserved1() ).append( '\n' );
+ buffer.append( " .HorizontalTextAlignment = " ).append( getHorizontalTextAlignment() ).append( '\n' );
+ buffer.append( " .VerticalTextAlignment = " ).append( getVerticalTextAlignment() ).append( '\n' );
+ buffer.append( " .reserved2 = " ).append( getReserved2() ).append( '\n' );
+ buffer.append( " .textLocked = " ).append( isTextLocked() ).append( '\n' );
+ buffer.append( " .reserved3 = " ).append( getReserved3() ).append( '\n' );
+ buffer.append( " .textOrientation = " )
+ .append( "0x" ).append( HexDump.toHex( getTextOrientation() ) )
+ .append( " (" ).append( getTextOrientation() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved4 = " )
+ .append( "0x" ).append( HexDump.toHex( getReserved4() ) )
+ .append( " (" ).append( getReserved4() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved5 = " )
+ .append( "0x" ).append( HexDump.toHex( getReserved5() ) )
+ .append( " (" ).append( getReserved5() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved6 = " )
+ .append( "0x" ).append( HexDump.toHex( getReserved6() ) )
+ .append( " (" ).append( getReserved6() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .textLength = " )
+ .append( "0x" ).append( HexDump.toHex( getTextLength() ) )
+ .append( " (" ).append( getTextLength() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved7 = " )
+ .append( "0x" ).append( HexDump.toHex( getReserved7() ) )
+ .append( " (" ).append( getReserved7() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+
+ buffer.append( " .string = " ).append(str).append('\n');
+
+ buffer.append( "[/TXO]\n" );
+ return buffer.toString();
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/UnknownRecord.java b/src/java/org/apache/poi/hssf/record/UnknownRecord.java
index 88ddd4ce05..ae6f6006b2 100644
--- a/src/java/org/apache/poi/hssf/record/UnknownRecord.java
+++ b/src/java/org/apache/poi/hssf/record/UnknownRecord.java
@@ -95,6 +95,13 @@ public class UnknownRecord
this.thedata = data;
}
+ public UnknownRecord( short id, short size, byte[] data, int offset )
+ {
+ sid = id;
+ thedata = new byte[size];
+ System.arraycopy(data, offset, thedata, 0, size);
+ }
+
/**
* spit the record out AS IS. no interperatation or identification
*/
diff --git a/src/java/org/apache/poi/hssf/record/VerticalPageBreakRecord.java b/src/java/org/apache/poi/hssf/record/VerticalPageBreakRecord.java
new file mode 100644
index 0000000000..10a9b7cd4b
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/VerticalPageBreakRecord.java
@@ -0,0 +1,107 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.hssf.record;
+
+/**
+ * VerticalPageBreak record that stores page breaks at columns
+ * <p>
+ * This class is just used so that SID compares work properly in the RecordFactory
+ * @see PageBreakRecord
+ * @author Danny Mui (dmui at apache dot org)
+ */
+public class VerticalPageBreakRecord extends PageBreakRecord {
+
+ public static final short sid = PageBreakRecord.VERTICAL_SID;
+
+ /**
+ *
+ */
+ public VerticalPageBreakRecord() {
+ super();
+ }
+
+ /**
+ * @param sid
+ */
+ public VerticalPageBreakRecord(short sid) {
+ super(sid);
+ }
+
+ /**
+ * @param id
+ * @param size
+ * @param data
+ */
+ public VerticalPageBreakRecord(short id, short size, byte[] data) {
+ super(id, size, data);
+ }
+
+ /**
+ * @param id
+ * @param size
+ * @param data
+ * @param offset
+ */
+ public VerticalPageBreakRecord(short id, short size, byte[] data, int offset) {
+ super(id, size, data, offset);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.poi.hssf.record.Record#getSid()
+ */
+ public short getSid() {
+ return PageBreakRecord.VERTICAL_SID;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java
new file mode 100644
index 0000000000..44b8e4d83b
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java
@@ -0,0 +1,773 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.awt.font.GlyphVector;
+import java.awt.font.FontRenderContext;
+import java.util.Map;
+import java.text.AttributedCharacterIterator;
+
+public class DummyGraphics2d
+ extends Graphics2D
+{
+ BufferedImage img;
+ private Graphics2D g2D;
+
+ public DummyGraphics2d()
+ {
+ img = new BufferedImage(1000, 1000, 2);
+ g2D = (Graphics2D)img.getGraphics();
+ }
+
+ public void addRenderingHints(Map hints)
+ {
+ System.out.println( "addRenderingHinds(Map):" );
+ System.out.println( " hints = " + hints );
+ g2D.addRenderingHints( hints );
+ }
+
+ public void clip(Shape s)
+ {
+ System.out.println( "clip(Shape):" );
+ System.out.println( " s = " + s );
+ g2D.clip( s );
+ }
+
+ public void draw(Shape s)
+ {
+ System.out.println( "draw(Shape):" );
+ System.out.println( "s = " + s );
+ g2D.draw( s );
+ }
+
+ public void drawGlyphVector(GlyphVector g, float x, float y)
+ {
+ System.out.println( "drawGlyphVector(GlyphVector, float, float):" );
+ System.out.println( "g = " + g );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawGlyphVector( g, x, y );
+ }
+
+ public void drawImage(BufferedImage img,
+ BufferedImageOp op,
+ int x,
+ int y)
+ {
+ System.out.println( "drawImage(BufferedImage, BufferedImageOp, x, y):" );
+ System.out.println( "img = " + img );
+ System.out.println( "op = " + op );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawImage( img, op, x, y );
+ }
+
+ public boolean drawImage(Image img,
+ AffineTransform xform,
+ ImageObserver obs)
+ {
+ System.out.println( "drawImage(Image,AfflineTransform,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "xform = " + xform );
+ System.out.println( "obs = " + obs );
+ return g2D.drawImage( img, xform, obs );
+ }
+
+ public void drawRenderableImage(RenderableImage img,
+ AffineTransform xform)
+ {
+ System.out.println( "drawRenderableImage(RenderableImage, AfflineTransform):" );
+ System.out.println( "img = " + img );
+ System.out.println( "xform = " + xform );
+ g2D.drawRenderableImage( img, xform );
+ }
+
+ public void drawRenderedImage(RenderedImage img,
+ AffineTransform xform)
+ {
+ System.out.println( "drawRenderedImage(RenderedImage, AffineTransform):" );
+ System.out.println( "img = " + img );
+ System.out.println( "xform = " + xform );
+ g2D.drawRenderedImage( img, xform );
+ }
+
+ public void drawString(AttributedCharacterIterator iterator,
+ float x, float y)
+ {
+ System.out.println( "drawString(AttributedCharacterIterator):" );
+ System.out.println( "iterator = " + iterator );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawString( iterator, x, y );
+ }
+
+// public void drawString(AttributedCharacterIterator iterator,
+// int x, int y)
+// {
+// g2D.drawString( iterator, x, y );
+// }
+
+ public void drawString(String s, float x, float y)
+ {
+ System.out.println( "drawString(s,x,y):" );
+ System.out.println( "s = " + s );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawString( s, x, y );
+ }
+
+// public void drawString(String str, int x, int y)
+// {
+// g2D.drawString( str, x, y );
+// }
+
+ public void fill(Shape s)
+ {
+ System.out.println( "fill(Shape):" );
+ System.out.println( "s = " + s );
+ g2D.fill( s );
+ }
+
+// public void fill3DRect(int x, int y, int width, int height,
+// boolean raised) {
+// g2D.fill3DRect( x, y, width, height, raised );
+// }
+
+ public Color getBackground()
+ {
+ System.out.println( "getBackground():" );
+ return g2D.getBackground();
+ }
+
+ public Composite getComposite()
+ {
+ System.out.println( "getComposite():" );
+ return g2D.getComposite();
+ }
+
+ public GraphicsConfiguration getDeviceConfiguration()
+ {
+ System.out.println( "getDeviceConfiguration():" );
+ return g2D.getDeviceConfiguration();
+ }
+
+ public FontRenderContext getFontRenderContext()
+ {
+ System.out.println( "getFontRenderContext():" );
+ return g2D.getFontRenderContext();
+ }
+
+ public Paint getPaint()
+ {
+ System.out.println( "getPaint():" );
+ return g2D.getPaint();
+ }
+
+ public Object getRenderingHint(RenderingHints.Key hintKey)
+ {
+ System.out.println( "getRenderingHint(RenderingHints.Key):" );
+ System.out.println( "hintKey = " + hintKey );
+ return g2D.getRenderingHint( hintKey );
+ }
+
+ public RenderingHints getRenderingHints()
+ {
+ System.out.println( "getRenderingHints():" );
+ return g2D.getRenderingHints();
+ }
+
+ public Stroke getStroke()
+ {
+ System.out.println( "getStroke():" );
+ return g2D.getStroke();
+ }
+
+ public AffineTransform getTransform()
+ {
+ System.out.println( "getTransform():" );
+ return g2D.getTransform();
+ }
+
+ public boolean hit(Rectangle rect,
+ Shape s,
+ boolean onStroke)
+ {
+ System.out.println( "hit(Rectangle, Shape, onStroke):" );
+ System.out.println( "rect = " + rect );
+ System.out.println( "s = " + s );
+ System.out.println( "onStroke = " + onStroke );
+ return g2D.hit( rect, s, onStroke );
+ }
+
+ public void rotate(double theta)
+ {
+ System.out.println( "rotate(theta):" );
+ System.out.println( "theta = " + theta );
+ g2D.rotate( theta );
+ }
+
+ public void rotate(double theta, double x, double y)
+ {
+ System.out.println( "rotate(double,double,double):" );
+ System.out.println( "theta = " + theta );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.rotate( theta, x, y );
+ }
+
+ public void scale(double sx, double sy)
+ {
+ System.out.println( "scale(double,double):" );
+ System.out.println( "sx = " + sx );
+ System.out.println( "sy" );
+ g2D.scale( sx, sy );
+ }
+
+ public void setBackground(Color color)
+ {
+ System.out.println( "setBackground(Color):" );
+ System.out.println( "color = " + color );
+ g2D.setBackground( color );
+ }
+
+ public void setComposite(Composite comp)
+ {
+ System.out.println( "setComposite(Composite):" );
+ System.out.println( "comp = " + comp );
+ g2D.setComposite( comp );
+ }
+
+ public void setPaint( Paint paint )
+ {
+ System.out.println( "setPain(Paint):" );
+ System.out.println( "paint = " + paint );
+ g2D.setPaint( paint );
+ }
+
+ public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
+ {
+ System.out.println( "setRenderingHint(RenderingHints.Key, Object):" );
+ System.out.println( "hintKey = " + hintKey );
+ System.out.println( "hintValue = " + hintValue );
+ g2D.setRenderingHint( hintKey, hintValue );
+ }
+
+ public void setRenderingHints(Map hints)
+ {
+ System.out.println( "setRenderingHints(Map):" );
+ System.out.println( "hints = " + hints );
+ g2D.setRenderingHints( hints );
+ }
+
+ public void setStroke(Stroke s)
+ {
+ System.out.println( "setStroke(Stoke):" );
+ System.out.println( "s = " + s );
+ g2D.setStroke( s );
+ }
+
+ public void setTransform(AffineTransform Tx)
+ {
+ System.out.println( "setTransform():" );
+ System.out.println( "Tx = " + Tx );
+ g2D.setTransform( Tx );
+ }
+
+ public void shear(double shx, double shy)
+ {
+ System.out.println( "shear(shx, dhy):" );
+ System.out.println( "shx = " + shx );
+ System.out.println( "shy = " + shy );
+ g2D.shear( shx, shy );
+ }
+
+ public void transform(AffineTransform Tx)
+ {
+ System.out.println( "transform(AffineTransform):" );
+ System.out.println( "Tx = " + Tx );
+ g2D.transform( Tx );
+ }
+
+ public void translate(double tx, double ty)
+ {
+ System.out.println( "translate(double, double):" );
+ System.out.println( "tx = " + tx );
+ System.out.println( "ty = " + ty );
+ g2D.translate( tx, ty );
+ }
+
+// public void translate(int x, int y)
+// {
+// g2D.translate( x, y );
+// }
+
+ public void clearRect(int x, int y, int width, int height)
+ {
+ System.out.println( "clearRect(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.clearRect( x, y, width, height );
+ }
+
+ public void clipRect(int x, int y, int width, int height)
+ {
+ System.out.println( "clipRect(int, int, int, int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.clipRect( x, y, width, height );
+ }
+
+ public void copyArea(int x, int y, int width, int height,
+ int dx, int dy)
+ {
+ System.out.println( "copyArea(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.copyArea( x, y, width, height, dx, dy );
+ }
+
+ public Graphics create()
+ {
+ System.out.println( "create():" );
+ return g2D.create();
+ }
+
+ public Graphics create(int x, int y, int width, int height) {
+ System.out.println( "create(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ return g2D.create( x, y, width, height );
+ }
+
+ public void dispose()
+ {
+ System.out.println( "dispose():" );
+ g2D.dispose();
+ }
+
+ public void draw3DRect(int x, int y, int width, int height,
+ boolean raised) {
+ System.out.println( "draw3DRect(int,int,int,int,boolean):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "raised = " + raised );
+ g2D.draw3DRect( x, y, width, height, raised );
+ }
+
+ public void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ System.out.println( "drawArc(int,int,int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "startAngle = " + startAngle );
+ System.out.println( "arcAngle = " + arcAngle );
+ g2D.drawArc( x, y, width, height, startAngle, arcAngle );
+ }
+
+ public void drawBytes(byte data[], int offset, int length, int x, int y) {
+ System.out.println( "drawBytes(byte[],int,int,int,int):" );
+ System.out.println( "data = " + data );
+ System.out.println( "offset = " + offset );
+ System.out.println( "length = " + length );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawBytes( data, offset, length, x, y );
+ }
+
+ public void drawChars(char data[], int offset, int length, int x, int y) {
+ System.out.println( "drawChars(data,int,int,int,int):" );
+ System.out.println( "data = " + data );
+ System.out.println( "offset = " + offset );
+ System.out.println( "length = " + length );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawChars( data, offset, length, x, y );
+ }
+
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,int,int,int,int,int,int,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "dx1 = " + dx1 );
+ System.out.println( "dy1 = " + dy1 );
+ System.out.println( "dx2 = " + dx2 );
+ System.out.println( "dy2 = " + dy2 );
+ System.out.println( "sx1 = " + sx1 );
+ System.out.println( "sy1 = " + sy1 );
+ System.out.println( "sx2 = " + sx2 );
+ System.out.println( "sy2 = " + sy2 );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer );
+ }
+
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ Color bgcolor,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,int,int,int,int,int,int,Color,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "dx1 = " + dx1 );
+ System.out.println( "dy1 = " + dy1 );
+ System.out.println( "dx2 = " + dx2 );
+ System.out.println( "dy2 = " + dy2 );
+ System.out.println( "sx1 = " + sx1 );
+ System.out.println( "sy1 = " + sy1 );
+ System.out.println( "sx2 = " + sx2 );
+ System.out.println( "sy2 = " + sy2 );
+ System.out.println( "bgcolor = " + bgcolor );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ Color bgcolor,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,Color,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "bgcolor = " + bgcolor );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, x, y, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,observer):" );
+ System.out.println( "img = " + img );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, x, y, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ Color bgcolor,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,int,int,Color,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "bgcolor = " + bgcolor );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, x, y, width, height, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,width,height,observer):" );
+ System.out.println( "img = " + img );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, x, y, width, height, observer );
+ }
+
+ public void drawLine(int x1, int y1, int x2, int y2)
+ {
+ System.out.println( "drawLine(int,int,int,int):" );
+ System.out.println( "x1 = " + x1 );
+ System.out.println( "y1 = " + y1 );
+ System.out.println( "x2 = " + x2 );
+ System.out.println( "y2 = " + y2 );
+ g2D.drawLine( x1, y1, x2, y2 );
+ }
+
+ public void drawOval(int x, int y, int width, int height)
+ {
+ System.out.println( "drawOval(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.drawOval( x, y, width, height );
+ }
+
+ public void drawPolygon(Polygon p) {
+ System.out.println( "drawPolygon(Polygon):" );
+ System.out.println( "p = " + p );
+ g2D.drawPolygon( p );
+ }
+
+ public void drawPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ System.out.println( "drawPolygon(int[],int[],int):" );
+ System.out.println( "xPoints = " + xPoints );
+ System.out.println( "yPoints = " + yPoints );
+ System.out.println( "nPoints = " + nPoints );
+ g2D.drawPolygon( xPoints, yPoints, nPoints );
+ }
+
+ public void drawPolyline(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ System.out.println( "drawPolyline(int[],int[],int):" );
+ System.out.println( "xPoints = " + xPoints );
+ System.out.println( "yPoints = " + yPoints );
+ System.out.println( "nPoints = " + nPoints );
+ g2D.drawPolyline( xPoints, yPoints, nPoints );
+ }
+
+ public void drawRect(int x, int y, int width, int height) {
+ System.out.println( "drawRect(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.drawRect( x, y, width, height );
+ }
+
+ public void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ System.out.println( "drawRoundRect(int,int,int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "arcWidth = " + arcWidth );
+ System.out.println( "arcHeight = " + arcHeight );
+ g2D.drawRoundRect( x, y, width, height, arcWidth, arcHeight );
+ }
+
+ public void drawString(AttributedCharacterIterator iterator,
+ int x, int y)
+ {
+ System.out.println( "drawString(AttributedCharacterIterator,int,int):" );
+ System.out.println( "iterator = " + iterator );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawString( iterator, x, y );
+ }
+
+ public void drawString(String str, int x, int y)
+ {
+ System.out.println( "drawString(str,int,int):" );
+ System.out.println( "str = " + str );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawString( str, x, y );
+ }
+
+ public void fill3DRect(int x, int y, int width, int height,
+ boolean raised) {
+ System.out.println( "fill3DRect(int,int,int,int,boolean):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "raised = " + raised );
+ g2D.fill3DRect( x, y, width, height, raised );
+ }
+
+ public void fillArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ System.out.println( "fillArc(int,int,int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "startAngle = " + startAngle );
+ System.out.println( "arcAngle = " + arcAngle );
+ g2D.fillArc( x, y, width, height, startAngle, arcAngle );
+ }
+
+ public void fillOval(int x, int y, int width, int height)
+ {
+ System.out.println( "fillOval(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.fillOval( x, y, width, height );
+ }
+
+ public void fillPolygon(Polygon p) {
+ System.out.println( "fillPolygon(Polygon):" );
+ System.out.println( "p = " + p );
+ g2D.fillPolygon( p );
+ }
+
+ public void fillPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ System.out.println( "fillPolygon(int[],int[],int):" );
+ System.out.println( "xPoints = " + xPoints );
+ System.out.println( "yPoints = " + yPoints );
+ System.out.println( "nPoints = " + nPoints );
+ g2D.fillPolygon( xPoints, yPoints, nPoints );
+ }
+
+ public void fillRect(int x, int y, int width, int height)
+ {
+ System.out.println( "fillRect(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.fillRect( x, y, width, height );
+ }
+
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ System.out.println( "fillRoundRect(int,int,int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.fillRoundRect( x, y, width, height, arcWidth, arcHeight );
+ }
+
+ public void finalize() {
+ System.out.println( "finalize():" );
+ g2D.finalize();
+ }
+
+ public Shape getClip()
+ {
+ System.out.println( "getClip():" );
+ return g2D.getClip();
+ }
+
+ public Rectangle getClipBounds()
+ {
+ System.out.println( "getClipBounds():" );
+ return g2D.getClipBounds();
+ }
+
+ public Rectangle getClipBounds(Rectangle r) {
+ System.out.println( "getClipBounds(Rectangle):" );
+ System.out.println( "r = " + r );
+ return g2D.getClipBounds( r );
+ }
+
+ public Rectangle getClipRect() {
+ System.out.println( "getClipRect():" );
+ return g2D.getClipRect();
+ }
+
+ public Color getColor()
+ {
+ System.out.println( "getColor():" );
+ return g2D.getColor();
+ }
+
+ public Font getFont()
+ {
+ System.out.println( "getFont():" );
+ return g2D.getFont();
+ }
+
+ public FontMetrics getFontMetrics() {
+ System.out.println( "getFontMetrics():" );
+ return g2D.getFontMetrics();
+ }
+
+ public FontMetrics getFontMetrics(Font f)
+ {
+ System.out.println( "getFontMetrics():" );
+ return g2D.getFontMetrics( f );
+ }
+
+ public boolean hitClip(int x, int y, int width, int height) {
+ System.out.println( "hitClip(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ return g2D.hitClip( x, y, width, height );
+ }
+
+ public void setClip(Shape clip)
+ {
+ System.out.println( "setClip(Shape):" );
+ System.out.println( "clip = " + clip );
+ g2D.setClip( clip );
+ }
+
+ public void setClip(int x, int y, int width, int height)
+ {
+ System.out.println( "setClip(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.setClip( x, y, width, height );
+ }
+
+ public void setColor(Color c)
+ {
+ System.out.println( "setColor():" );
+ System.out.println( "c = " + c );
+ g2D.setColor( c );
+ }
+
+ public void setFont(Font font)
+ {
+ System.out.println( "setFont(Font):" );
+ System.out.println( "font = " + font );
+ g2D.setFont( font );
+ }
+
+ public void setPaintMode()
+ {
+ System.out.println( "setPaintMode():" );
+ g2D.setPaintMode();
+ }
+
+ public void setXORMode(Color c1)
+ {
+ System.out.println( "setXORMode(Color):" );
+ System.out.println( "c1 = " + c1 );
+ g2D.setXORMode( c1 );
+ }
+
+ public String toString() {
+ System.out.println( "toString():" );
+ return g2D.toString();
+ }
+
+ public void translate(int x, int y)
+ {
+ System.out.println( "translate(int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.translate( x, y );
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java
new file mode 100644
index 0000000000..fa8065a44c
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java
@@ -0,0 +1,450 @@
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.hssf.util.HSSFColor;
+
+import java.awt.*;
+import java.awt.image.ImageObserver;
+import java.text.AttributedCharacterIterator;
+
+/**
+ * Translates Graphics calls into escher calls. The translation is lossy so
+ * many features are not supported and some just aren't implemented yet. If
+ * in doubt test the specific calls you wish to make. Graphics calls are
+ * always performed into an EscherGroup so one will need to be created.
+ * <p>
+ * <b>Important:</b>
+ * <blockquote>
+ * One important concept worth considering is that of font size. One of the
+ * difficulties in converting Graphics calls into escher drawing calls is that
+ * Excel does not have the concept of absolute pixel positions. It measures
+ * it's cell widths in 'characters' and the cell heights in points.
+ * Unfortunately it's not defined exactly what a type of character it's
+ * measuring. Presumably this is due to the fact that the Excel will be
+ * using different fonts on different platforms or even within the same
+ * platform.
+ * <p>
+ * Because of this constraint we've had to calculate the
+ * verticalPointsPerPixel. This the amount the font should be scaled by when
+ * you issue commands such as drawString(). A good way to calculate this
+ * is to use the follow formula:
+ * <p>
+ * <pre>
+ * multipler = groupHeightInPoints / heightOfGroup
+ * </pre>
+ * <p>
+ * The height of the group is calculated fairly simply by calculating the
+ * difference between the y coordinates of the bounding box of the shape. The
+ * height of the group can be calculated by using a convenience called
+ * <code>HSSFClientAnchor.getAnchorHeightInPoints()</code>.
+ * </blockquote>
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherGraphics
+ extends Graphics
+{
+ private HSSFShapeGroup escherGroup;
+ private HSSFWorkbook workbook;
+ private float verticalPointsPerPixel = 1.0f;
+ private float verticalPixelsPerPoint;
+ private Color foreground;
+ private Color background = Color.white;
+ private Font font;
+ private static POILogger logger = POILogFactory.getLogger(EscherGraphics.class);
+
+ /**
+ * Construct an escher graphics object.
+ *
+ * @param escherGroup The escher group to write the graphics calls into.
+ * @param workbook The workbook we are using.
+ * @param forecolor The foreground color to use as default.
+ * @param verticalPointsPerPixel The font multiplier. (See class description for information on how this works.).
+ */
+ public EscherGraphics(HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color forecolor, float verticalPointsPerPixel )
+ {
+ this.escherGroup = escherGroup;
+ this.workbook = workbook;
+ this.verticalPointsPerPixel = verticalPointsPerPixel;
+ this.verticalPixelsPerPoint = 1 / verticalPointsPerPixel;
+ this.font = new Font("Arial", 0, 10);
+ this.foreground = forecolor;
+// background = backcolor;
+ }
+
+ /**
+ * Constructs an escher graphics object.
+ *
+ * @param escherGroup The escher group to write the graphics calls into.
+ * @param workbook The workbook we are using.
+ * @param foreground The foreground color to use as default.
+ * @param verticalPointsPerPixel The font multiplier. (See class description for information on how this works.).
+ * @param font The font to use.
+ */
+ EscherGraphics( HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color foreground, Font font, float verticalPointsPerPixel )
+ {
+ this.escherGroup = escherGroup;
+ this.workbook = workbook;
+ this.foreground = foreground;
+// this.background = background;
+ this.font = font;
+ this.verticalPointsPerPixel = verticalPointsPerPixel;
+ this.verticalPixelsPerPoint = 1 / verticalPointsPerPixel;
+ }
+
+ /**
+ * Constructs an escher graphics object.
+ *
+ * @param escherGroup The escher group to write the graphics calls into.
+ * @param workbook The workbook we are using.
+ * @param forecolor The default foreground color.
+ */
+// public EscherGraphics( HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color forecolor)
+// {
+// this(escherGroup, workbook, forecolor, 1.0f);
+// }
+
+
+ public void clearRect(int x, int y, int width, int height)
+ {
+ Color color = foreground;
+ setColor(background);
+ fillRect(x,y,width,height);
+ setColor(color);
+ }
+
+ public void clipRect(int x, int y, int width, int height)
+ {
+ logger.log(POILogger.WARN,"clipRect not supported");
+ }
+
+ public void copyArea(int x, int y, int width, int height, int dx, int dy)
+ {
+ logger.log(POILogger.WARN,"copyArea not supported");
+ }
+
+ public Graphics create()
+ {
+ EscherGraphics g = new EscherGraphics(escherGroup, workbook,
+ foreground, font, verticalPointsPerPixel );
+ return g;
+ }
+
+ public void dispose()
+ {
+ }
+
+ public void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ logger.log(POILogger.WARN,"drawArc not supported");
+ }
+
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ Color bgcolor,
+ ImageObserver observer)
+ {
+ logger.log(POILogger.WARN,"drawImage not supported");
+
+ return true;
+ }
+
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer)
+ {
+ logger.log(POILogger.WARN,"drawImage not supported");
+ return true;
+ }
+
+ public boolean drawImage(Image image, int i, int j, int k, int l, Color color, ImageObserver imageobserver)
+ {
+ return drawImage(image, i, j, i + k, j + l, 0, 0, image.getWidth(imageobserver), image.getHeight(imageobserver), color, imageobserver);
+ }
+
+ public boolean drawImage(Image image, int i, int j, int k, int l, ImageObserver imageobserver)
+ {
+ return drawImage(image, i, j, i + k, j + l, 0, 0, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver);
+ }
+
+ public boolean drawImage(Image image, int i, int j, Color color, ImageObserver imageobserver)
+ {
+ return drawImage(image, i, j, image.getWidth(imageobserver), image.getHeight(imageobserver), color, imageobserver);
+ }
+
+ public boolean drawImage(Image image, int i, int j, ImageObserver imageobserver)
+ {
+ return drawImage(image, i, j, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver);
+ }
+
+ public void drawLine(int x1, int y1, int x2, int y2)
+ {
+ HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor(x1, y1, x2, y2) );
+ shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ shape.setLineWidth(0);
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ }
+
+ public void drawOval(int x, int y, int width, int height)
+ {
+ HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor(x,y,x+width,y+height) );
+ shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
+ shape.setLineWidth(0);
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setNoFill(true);
+ }
+
+ public void drawPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ int right = findBiggest(xPoints);
+ int bottom = findBiggest(yPoints);
+ int left = findSmallest(xPoints);
+ int top = findSmallest(yPoints);
+ HSSFPolygon shape = escherGroup.createPolygon(new HSSFChildAnchor(left,top,right,bottom) );
+ shape.setPolygonDrawArea(right - left, bottom - top);
+ shape.setPoints(addToAll(xPoints, -left), addToAll(yPoints, -top));
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setLineWidth(0);
+ shape.setNoFill(true);
+ }
+
+ private int[] addToAll( int[] values, int amount )
+ {
+ int[] result = new int[values.length];
+ for ( int i = 0; i < values.length; i++ )
+ result[i] = values[i] + amount;
+ return result;
+ }
+
+ public void drawPolyline(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ logger.log(POILogger.WARN,"drawPolyline not supported");
+ }
+
+ public void drawRect(int x, int y, int width, int height)
+ {
+ logger.log(POILogger.WARN,"drawRect not supported");
+ }
+
+ public void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ logger.log(POILogger.WARN,"drawRoundRect not supported");
+ }
+
+ public void drawString(String str, int x, int y)
+ {
+ if (str == null || str.equals(""))
+ return;
+
+ Font excelFont = font;
+ if ( font.getName().equals( "SansSerif" ) )
+ {
+ excelFont = new Font( "Arial", font.getStyle(), (int) ( font.getSize() / verticalPixelsPerPoint ) );
+ }
+ else
+ {
+ excelFont = new Font( font.getName(), font.getStyle(), (int) ( font.getSize() / verticalPixelsPerPoint ));
+ }
+ FontDetails d = StaticFontMetrics.getFontDetails( excelFont );
+ int width = (int) ( (d.getStringWidth( str ) * 2.5) + 12 );
+ int height = (int) ( ( font.getSize() * 2.0 * verticalPixelsPerPoint ) + 6 );
+ y -= ( font.getSize() * verticalPixelsPerPoint ); // we want to draw the shape from the top-left
+ HSSFTextbox textbox = escherGroup.createTextbox( new HSSFChildAnchor( x, y, x + width, y + height ) );
+ textbox.setNoFill( true );
+ textbox.setLineStyle( HSSFShape.LINESTYLE_NONE );
+ HSSFRichTextString s = new HSSFRichTextString( str );
+ HSSFFont hssfFont = matchFont( excelFont );
+ s.applyFont( hssfFont );
+ textbox.setString( s );
+ }
+
+ private HSSFFont matchFont( Font font )
+ {
+ HSSFColor hssfColor = workbook.getCustomPalette()
+ .findColor((byte)foreground.getRed(), (byte)foreground.getGreen(), (byte)foreground.getBlue());
+ if (hssfColor == null)
+ hssfColor = workbook.getCustomPalette().findSimilarColor((byte)foreground.getRed(), (byte)foreground.getGreen(), (byte)foreground.getBlue());
+ boolean bold = (font.getStyle() & Font.BOLD) != 0;
+ boolean italic = (font.getStyle() & Font.ITALIC) != 0;
+ HSSFFont hssfFont = workbook.findFont(bold ? HSSFFont.BOLDWEIGHT_BOLD : 0,
+ hssfColor.getIndex(),
+ (short)(font.getSize() * 20),
+ font.getName(),
+ italic,
+ false,
+ (short)0,
+ (byte)0);
+ if (hssfFont == null)
+ {
+ hssfFont = workbook.createFont();
+ hssfFont.setBoldweight(bold ? HSSFFont.BOLDWEIGHT_BOLD : 0);
+ hssfFont.setColor(hssfColor.getIndex());
+ hssfFont.setFontHeight((short)(font.getSize() * 20));
+ hssfFont.setFontName(font.getName());
+ hssfFont.setItalic(italic);
+ hssfFont.setStrikeout(false);
+ hssfFont.setTypeOffset((short) 0);
+ hssfFont.setUnderline((byte) 0);
+ }
+
+ return hssfFont;
+ }
+
+
+ public void drawString(AttributedCharacterIterator iterator,
+ int x, int y)
+ {
+ logger.log(POILogger.WARN,"drawString not supported");
+ }
+
+ public void fillArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ logger.log(POILogger.WARN,"fillArc not supported");
+ }
+
+ public void fillOval(int x, int y, int width, int height)
+ {
+ HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor( x, y, x + width, y + height ) );
+ shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
+ shape.setLineStyle(HSSFShape.LINESTYLE_NONE);
+ shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ }
+
+ public void fillPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ int right = findBiggest(xPoints);
+ int bottom = findBiggest(yPoints);
+ int left = findSmallest(xPoints);
+ int top = findSmallest(yPoints);
+ HSSFPolygon shape = escherGroup.createPolygon(new HSSFChildAnchor(left,top,right,bottom) );
+ shape.setPolygonDrawArea(right - left, bottom - top);
+ shape.setPoints(addToAll(xPoints, -left), addToAll(yPoints, -top));
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ }
+
+ private int findBiggest( int[] values )
+ {
+ int result = Integer.MIN_VALUE;
+ for ( int i = 0; i < values.length; i++ )
+ {
+ if (values[i] > result)
+ result = values[i];
+ }
+ return result;
+ }
+
+ private int findSmallest( int[] values )
+ {
+ int result = Integer.MAX_VALUE;
+ for ( int i = 0; i < values.length; i++ )
+ {
+ if (values[i] < result)
+ result = values[i];
+ }
+ return result;
+ }
+
+ public void fillRect(int x, int y, int width, int height)
+ {
+ HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor( x, y, x + width, y + height ) );
+ shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE);
+ shape.setLineStyle(HSSFShape.LINESTYLE_NONE);
+ shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ }
+
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ logger.log(POILogger.WARN,"fillRoundRect not supported");
+ }
+
+ public Shape getClip()
+ {
+ return getClipBounds();
+ }
+
+ public Rectangle getClipBounds()
+ {
+ return null;
+ }
+
+ public Rectangle getClipRect()
+ {
+ return getClipBounds();
+ }
+
+ public Color getColor()
+ {
+ return foreground;
+ }
+
+ public Font getFont()
+ {
+ return font;
+ }
+
+ public FontMetrics getFontMetrics(Font f)
+ {
+ return Toolkit.getDefaultToolkit().getFontMetrics(f);
+ }
+
+ public void setClip(int x, int y, int width, int height)
+ {
+ setClip(((Shape) (new Rectangle(x,y,width,height))));
+ }
+
+ public void setClip(Shape shape)
+ {
+ // ignore... not implemented
+ }
+
+ public void setColor(Color color)
+ {
+ foreground = color;
+ }
+
+ public void setFont(Font f)
+ {
+ font = f;
+ }
+
+ public void setPaintMode()
+ {
+ logger.log(POILogger.WARN,"setPaintMode not supported");
+ }
+
+ public void setXORMode(Color color)
+ {
+ logger.log(POILogger.WARN,"setXORMode not supported");
+ }
+
+ public void translate(int x, int y)
+ {
+ logger.log(POILogger.WARN,"translate not supported");
+ }
+
+ public Color getBackground()
+ {
+ return background;
+ }
+
+ public void setBackground( Color background )
+ {
+ this.background = background;
+ }
+
+
+}
+
diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java
new file mode 100644
index 0000000000..f3298d79e6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java
@@ -0,0 +1,567 @@
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+import java.awt.*;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.GeneralPath;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+
+/**
+ * Translates Graphics2d calls into escher calls. The translation is lossy so
+ * many features are not supported and some just aren't implemented yet. If
+ * in doubt test the specific calls you wish to make. Graphics calls are
+ * always drawn into an EscherGroup so one will need to be created.
+ * <p>
+ * <b>Important:</b>
+ * <blockquote>
+ * One important concept worth considering is that of font size. One of the
+ * difficulties in converting Graphics calls into escher drawing calls is that
+ * Excel does not have the concept of absolute pixel positions. It measures
+ * it's cell widths in 'characters' and the cell heights in points.
+ * Unfortunately it's not defined exactly what a type of character it's
+ * measuring. Presumably this is due to the fact that the Excel will be
+ * using different fonts on different platforms or even within the same
+ * platform.
+ * <p>
+ * Because of this constraint you have to calculate the verticalPointsPerPixel.
+ * This the amount the font should be scaled by when
+ * you issue commands such as drawString(). A good way to calculate this
+ * is to use the follow formula:
+ * <p>
+ * <pre>
+ * multipler = groupHeightInPoints / heightOfGroup
+ * </pre>
+ * <p>
+ * The height of the group is calculated fairly simply by calculating the
+ * difference between the y coordinates of the bounding box of the shape. The
+ * height of the group can be calculated by using a convenience called
+ * <code>HSSFClientAnchor.getAnchorHeightInPoints()</code>.
+ * </blockquote>
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherGraphics2d extends Graphics2D
+{
+ private EscherGraphics escherGraphics;
+ private BufferedImage img;
+ private AffineTransform trans;
+ private Stroke stroke;
+ private Paint paint;
+ private Shape deviceclip;
+ private POILogger logger = POILogFactory.getLogger(getClass());
+
+ /**
+ * Constructs one escher graphics object from an escher graphics object.
+ *
+ * @param escherGraphics the original EscherGraphics2d object to copy
+ */
+ public EscherGraphics2d(EscherGraphics escherGraphics)
+ {
+ this.escherGraphics = escherGraphics;
+ setImg( new BufferedImage(1, 1, 2) );
+ setColor(Color.black);
+ }
+
+ public void addRenderingHints(Map map)
+ {
+ getG2D().addRenderingHints(map);
+ }
+
+ public void clearRect(int i, int j, int k, int l)
+ {
+ Paint paint1 = getPaint();
+ setColor(getBackground());
+ fillRect(i, j, k, l);
+ setPaint(paint1);
+ }
+
+ public void clip(Shape shape)
+ {
+ if(getDeviceclip() != null)
+ {
+ Area area = new Area(getClip());
+ if(shape != null)
+ area.intersect(new Area(shape));
+ shape = area;
+ }
+ setClip(shape);
+ }
+
+ public void clipRect(int x, int y, int width, int height)
+ {
+ clip(new Rectangle(x,y,width,height));
+ }
+
+ public void copyArea(int x, int y, int width, int height,
+ int dx, int dy)
+ {
+ getG2D().copyArea(x,y,width,height,dx,dy);
+ }
+
+ public Graphics create()
+ {
+ EscherGraphics2d g2d = new EscherGraphics2d(escherGraphics);
+ return g2d;
+ }
+
+ public void dispose()
+ {
+ getEscherGraphics().dispose();
+ getG2D().dispose();
+ getImg().flush();
+ }
+
+ public void draw(Shape shape)
+ {
+ logger.log(POILogger.WARN,"copyArea not supported");
+ }
+
+ public void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ draw(new java.awt.geom.Arc2D.Float(x, y, width, height, startAngle, arcAngle, 0));
+ }
+
+ public void drawGlyphVector(GlyphVector g, float x, float y)
+ {
+ fill(g.getOutline(x, y));
+ }
+
+ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1,
+ int sx2, int sy2, Color bgColor, ImageObserver imageobserver)
+ {
+ logger.log(POILogger.WARN,"drawImage() not supported");
+ return true;
+ }
+
+ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1,
+ int sx2, int sy2, ImageObserver imageobserver)
+ {
+ logger.log(POILogger.WARN,"drawImage() not supported");
+ return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, imageobserver);
+ }
+ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, Color bgColor, ImageObserver imageobserver)
+ {
+ logger.log(POILogger.WARN,"drawImage() not supported");
+ return true;
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ ImageObserver observer)
+ {
+ return drawImage(img, x,y,width,height, null, observer);
+ }
+
+ public boolean drawImage(Image image, int x, int y, Color bgColor, ImageObserver imageobserver)
+ {
+ return drawImage(image, x, y, image.getWidth(imageobserver), image.getHeight(imageobserver), bgColor, imageobserver);
+ }
+
+ public boolean drawImage(Image image, int x, int y, ImageObserver imageobserver)
+ {
+ return drawImage(image, x, y, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver);
+ }
+
+ public boolean drawImage(Image image, AffineTransform affinetransform, ImageObserver imageobserver)
+ {
+ AffineTransform affinetransform1 = (AffineTransform)getTrans().clone();
+ getTrans().concatenate(affinetransform);
+ drawImage(image, 0, 0, imageobserver);
+ setTrans( affinetransform1 );
+ return true;
+ }
+
+ public void drawImage(BufferedImage bufferedimage, BufferedImageOp op, int x, int y)
+ {
+ BufferedImage img = op.filter(bufferedimage, null);
+ drawImage(((Image) (img)), new AffineTransform(1.0F, 0.0F, 0.0F, 1.0F, x, y), null);
+ }
+
+ public void drawLine(int x1, int y1, int x2, int y2)
+ {
+ getEscherGraphics().drawLine(x1,y1,x2,y2);
+// draw(new GeneralPath(new java.awt.geom.Line2D.Float(x1, y1, x2, y2)));
+ }
+
+ public void drawOval(int x, int y, int width, int height)
+ {
+ getEscherGraphics().drawOval(x,y,width,height);
+// draw(new java.awt.geom.Ellipse2D.Float(x, y, width, height));
+ }
+
+ public void drawPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ getEscherGraphics().drawPolygon(xPoints, yPoints, nPoints);
+ }
+
+ public void drawPolyline(int xPoints[], int yPoints[], int nPoints)
+ {
+ if(nPoints > 0)
+ {
+ GeneralPath generalpath = new GeneralPath();
+ generalpath.moveTo(xPoints[0], yPoints[0]);
+ for(int j = 1; j < nPoints; j++)
+ generalpath.lineTo(xPoints[j], yPoints[j]);
+
+ draw(generalpath);
+ }
+ }
+
+ public void drawRect(int x, int y, int width, int height)
+ {
+ escherGraphics.drawRect(x,y,width,height);
+ }
+
+ public void drawRenderableImage(RenderableImage renderableimage, AffineTransform affinetransform)
+ {
+ drawRenderedImage(renderableimage.createDefaultRendering(), affinetransform);
+ }
+
+ public void drawRenderedImage(RenderedImage renderedimage, AffineTransform affinetransform)
+ {
+ BufferedImage bufferedimage = new BufferedImage(renderedimage.getColorModel(), renderedimage.getData().createCompatibleWritableRaster(), false, null);
+ bufferedimage.setData(renderedimage.getData());
+ drawImage(bufferedimage, affinetransform, null);
+ }
+
+ public void drawRoundRect(int i, int j, int k, int l, int i1, int j1)
+ {
+ draw(new java.awt.geom.RoundRectangle2D.Float(i, j, k, l, i1, j1));
+ }
+
+ public void drawString(String string, float x, float y)
+ {
+ getEscherGraphics().drawString(string, (int)x, (int)y);
+ }
+
+ public void drawString(String string, int x, int y)
+ {
+ getEscherGraphics().drawString(string, x, y);
+ }
+
+ public void drawString(AttributedCharacterIterator attributedcharacteriterator, float x, float y)
+ {
+ TextLayout textlayout = new TextLayout(attributedcharacteriterator, getFontRenderContext());
+ Paint paint1 = getPaint();
+ setColor(getColor());
+ fill(textlayout.getOutline(AffineTransform.getTranslateInstance(x, y)));
+ setPaint(paint1);
+ }
+
+ public void drawString(AttributedCharacterIterator attributedcharacteriterator, int x, int y)
+ {
+ drawString(attributedcharacteriterator, x, y);
+ }
+
+ public void fill(Shape shape)
+ {
+ logger.log(POILogger.WARN,"fill(Shape) not supported");
+ }
+
+ public void fillArc(int i, int j, int k, int l, int i1, int j1)
+ {
+ fill(new java.awt.geom.Arc2D.Float(i, j, k, l, i1, j1, 2));
+ }
+
+ public void fillOval(int x, int y, int width, int height)
+ {
+ escherGraphics.fillOval(x,y,width,height);
+ }
+
+ /**
+ * Fills a closed polygon defined by
+ * arrays of <i>x</i> and <i>y</i> coordinates.
+ * <p>
+ * This method draws the polygon defined by <code>nPoint</code> line
+ * segments, where the first <code>nPoint&nbsp;-&nbsp;1</code>
+ * line segments are line segments from
+ * <code>(xPoints[i&nbsp;-&nbsp;1],&nbsp;yPoints[i&nbsp;-&nbsp;1])</code>
+ * to <code>(xPoints[i],&nbsp;yPoints[i])</code>, for
+ * 1&nbsp;&le;&nbsp;<i>i</i>&nbsp;&le;&nbsp;<code>nPoints</code>.
+ * The figure is automatically closed by drawing a line connecting
+ * the final point to the first point, if those points are different.
+ * <p>
+ * The area inside the polygon is defined using an
+ * even-odd fill rule, also known as the alternating rule.
+ * @param xPoints a an array of <code>x</code> coordinates.
+ * @param yPoints a an array of <code>y</code> coordinates.
+ * @param nPoints a the total number of points.
+ * @see java.awt.Graphics#drawPolygon(int[], int[], int)
+ */
+ public void fillPolygon(int xPoints[], int yPoints[], int nPoints)
+ {
+ escherGraphics.fillPolygon(xPoints, yPoints, nPoints);
+ }
+
+ public void fillRect(int x, int y, int width, int height)
+ {
+ getEscherGraphics().fillRect(x,y,width,height);
+ }
+
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ fill(new java.awt.geom.RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight));
+ }
+
+ public Color getBackground()
+ {
+ return getEscherGraphics().getBackground();
+ }
+
+ public Shape getClip()
+ {
+ try
+ {
+ return getTrans().createInverse().createTransformedShape(getDeviceclip());
+ }
+ catch(Exception _ex)
+ {
+ return null;
+ }
+ }
+
+ public Rectangle getClipBounds()
+ {
+ if(getDeviceclip() != null)
+ return getClip().getBounds();
+ else
+ return null;
+ }
+
+ public Color getColor()
+ {
+ return escherGraphics.getColor();
+ }
+
+ public Composite getComposite()
+ {
+ return getG2D().getComposite();
+ }
+
+ public GraphicsConfiguration getDeviceConfiguration()
+ {
+ return getG2D().getDeviceConfiguration();
+ }
+
+ public Font getFont()
+ {
+ return getEscherGraphics().getFont();
+ }
+
+ public FontMetrics getFontMetrics(Font font)
+ {
+ return getEscherGraphics().getFontMetrics(font);
+ }
+
+ public FontRenderContext getFontRenderContext()
+ {
+ getG2D().setTransform(getTrans());
+ return getG2D().getFontRenderContext();
+ }
+
+ public Paint getPaint()
+ {
+ return paint;
+ }
+
+ public Object getRenderingHint(java.awt.RenderingHints.Key key)
+ {
+ return getG2D().getRenderingHint(key);
+ }
+
+ public RenderingHints getRenderingHints()
+ {
+ return getG2D().getRenderingHints();
+ }
+
+ public Stroke getStroke()
+ {
+ return stroke;
+ }
+
+ public AffineTransform getTransform()
+ {
+ return (AffineTransform)getTrans().clone();
+ }
+
+ public boolean hit(Rectangle rectangle, Shape shape, boolean flag)
+ {
+ getG2D().setTransform(getTrans());
+ getG2D().setStroke(getStroke());
+ getG2D().setClip(getClip());
+ return getG2D().hit(rectangle, shape, flag);
+ }
+
+ public void rotate(double d)
+ {
+ getTrans().rotate(d);
+ }
+
+ public void rotate(double d, double d1, double d2)
+ {
+ getTrans().rotate(d, d1, d2);
+ }
+
+ public void scale(double d, double d1)
+ {
+ getTrans().scale(d, d1);
+ }
+
+ public void setBackground(Color c)
+ {
+ getEscherGraphics().setBackground(c);
+ }
+
+ public void setClip(int i, int j, int k, int l)
+ {
+ setClip(((Shape) (new Rectangle(i, j, k, l))));
+ }
+
+ public void setClip(Shape shape)
+ {
+ setDeviceclip( getTrans().createTransformedShape(shape) );
+ }
+
+ public void setColor(Color c)
+ {
+ escherGraphics.setColor(c);
+ }
+
+ public void setComposite(Composite composite)
+ {
+ getG2D().setComposite(composite);
+ }
+
+ public void setFont(Font font)
+ {
+ getEscherGraphics().setFont(font);
+ }
+
+ public void setPaint(Paint paint1)
+ {
+ if(paint1 != null)
+ {
+ paint = paint1;
+ if(paint1 instanceof Color)
+ setColor( (Color)paint1 );
+ }
+ }
+
+ public void setPaintMode()
+ {
+ getEscherGraphics().setPaintMode();
+ }
+
+ public void setRenderingHint(java.awt.RenderingHints.Key key, Object obj)
+ {
+ getG2D().setRenderingHint(key, obj);
+ }
+
+ public void setRenderingHints(Map map)
+ {
+ getG2D().setRenderingHints(map);
+ }
+
+ public void setStroke(Stroke s)
+ {
+ stroke = s;
+ }
+
+ public void setTransform(AffineTransform affinetransform)
+ {
+ setTrans( (AffineTransform)affinetransform.clone() );
+ }
+
+ public void setXORMode(Color color1)
+ {
+ getEscherGraphics().setXORMode(color1);
+ }
+
+ public void shear(double d, double d1)
+ {
+ getTrans().shear(d, d1);
+ }
+
+ public void transform(AffineTransform affinetransform)
+ {
+ getTrans().concatenate(affinetransform);
+ }
+
+// Image transformImage(Image image, Rectangle rectangle, Rectangle rectangle1, ImageObserver imageobserver, Color color1)
+// {
+// logger.log(POILogger.WARN,"transformImage() not supported");
+// return null;
+// }
+//
+// Image transformImage(Image image, int ai[], Rectangle rectangle, ImageObserver imageobserver, Color color1)
+// {
+// logger.log(POILogger.WARN,"transformImage() not supported");
+// return null;
+// }
+
+ public void translate(double d, double d1)
+ {
+ getTrans().translate(d, d1);
+ }
+
+ public void translate(int i, int j)
+ {
+ getTrans().translate(i, j);
+ }
+
+ private EscherGraphics getEscherGraphics()
+ {
+ return escherGraphics;
+ }
+
+ private BufferedImage getImg()
+ {
+ return img;
+ }
+
+ private void setImg( BufferedImage img )
+ {
+ this.img = img;
+ }
+
+ private Graphics2D getG2D()
+ {
+ return (Graphics2D) img.getGraphics();
+ }
+
+ private AffineTransform getTrans()
+ {
+ return trans;
+ }
+
+ private void setTrans( AffineTransform trans )
+ {
+ this.trans = trans;
+ }
+
+ private Shape getDeviceclip()
+ {
+ return deviceclip;
+ }
+
+ private void setDeviceclip( Shape deviceclip )
+ {
+ this.deviceclip = deviceclip;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/FontDetails.java b/src/java/org/apache/poi/hssf/usermodel/FontDetails.java
new file mode 100644
index 0000000000..96b0d87a86
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/FontDetails.java
@@ -0,0 +1,143 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * Stores width and height details about a font.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class FontDetails
+{
+ private String fontName;
+ private int height;
+ private Map charWidths = new HashMap();
+
+ /**
+ * Construct the font details with the given name and height.
+ *
+ * @param fontName The font name.
+ * @param height The height of the font.
+ */
+ public FontDetails( String fontName, int height )
+ {
+ this.fontName = fontName;
+ this.height = height;
+ }
+
+ public String getFontName()
+ {
+ return fontName;
+ }
+
+ public int getHeight()
+ {
+ return height;
+ }
+
+ public void addChar( char c, int width )
+ {
+ charWidths.put(new Character(c), new Integer(width));
+ }
+
+ /**
+ * Retrieves the width of the specified character. If the metrics for
+ * a particular character are not available it defaults to returning the
+ * width for the 'W' character.
+ */
+ public int getCharWidth( char c )
+ {
+ Integer widthInteger = (Integer)(charWidths.get(new Character(c)));
+ if (widthInteger == null && c != 'W')
+ return getCharWidth('W');
+ else
+ return widthInteger.intValue();
+ }
+
+ public void addChars( char[] characters, int[] widths )
+ {
+ for ( int i = 0; i < characters.length; i++ )
+ {
+ charWidths.put( new Character(characters[i]), new Integer(widths[i]));
+ }
+ }
+
+ /**
+ * Create an instance of <code>FontDetails</code> by loading them from the
+ * provided property object.
+ * @param fontName the font name
+ * @param fontMetricsProps the property object holding the details of this
+ * particular font.
+ * @return a new FontDetails instance.
+ */
+ public static FontDetails create( String fontName, Properties fontMetricsProps )
+ {
+ String heightStr = fontMetricsProps.getProperty( "font." + fontName + ".height");
+ String widthsStr = fontMetricsProps.getProperty( "font." + fontName + ".widths");
+ String charactersStr = fontMetricsProps.getProperty( "font." + fontName + ".characters");
+ int height = Integer.parseInt(heightStr);
+ FontDetails d = new FontDetails(fontName, height);
+ String[] charactersStrArray = split(charactersStr, ",", -1);
+ String[] widthsStrArray = split(widthsStr, ",", -1);
+ if (charactersStrArray.length != widthsStrArray.length)
+ throw new RuntimeException("Number of characters does not number of widths for font " + fontName);
+ for ( int i = 0; i < widthsStrArray.length; i++ )
+ {
+ if (charactersStrArray[i].length() != 0)
+ d.addChar(charactersStrArray[i].charAt(0), Integer.parseInt(widthsStrArray[i]));
+ }
+ return d;
+ }
+
+ /**
+ * Gets the width of all characters in a string.
+ *
+ * @param str The string to measure.
+ * @return The width of the string for a 10 point font.
+ */
+ public int getStringWidth(String str)
+ {
+ int width = 0;
+ for (int i = 0; i < str.length(); i++)
+ {
+ width += getCharWidth(str.charAt(i));
+ }
+ return width;
+ }
+
+ /**
+ * Split the given string into an array of strings using the given
+ * delimiter.
+ */
+ private static String[] split(String text, String separator, int max)
+ {
+ StringTokenizer tok = new StringTokenizer(text, separator);
+ int listSize = tok.countTokens();
+ if(max != -1 && listSize > max)
+ listSize = max;
+ String list[] = new String[listSize];
+ for(int i = 0; tok.hasMoreTokens(); i++)
+ {
+ if(max != -1 && i == listSize - 1)
+ {
+ StringBuffer buf = new StringBuffer((text.length() * (listSize - i)) / listSize);
+ while(tok.hasMoreTokens())
+ {
+ buf.append(tok.nextToken());
+ if(tok.hasMoreTokens())
+ buf.append(separator);
+ }
+ list[i] = buf.toString().trim();
+ break;
+ }
+ list[i] = tok.nextToken().trim();
+ }
+
+ return list;
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java
new file mode 100644
index 0000000000..b9db3c78eb
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java
@@ -0,0 +1,40 @@
+package org.apache.poi.hssf.usermodel;
+
+
+/**
+ * An anchor is what specifics the position of a shape within a client object
+ * or within another containing shape.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public abstract class HSSFAnchor
+{
+ int dx1;
+ int dy1;
+ int dx2;
+ int dy2;
+
+ public HSSFAnchor()
+ {
+ }
+
+ public HSSFAnchor( int dx1, int dy1, int dx2, int dy2 )
+ {
+ this.dx1 = dx1;
+ this.dy1 = dy1;
+ this.dx2 = dx2;
+ this.dy2 = dy2;
+ }
+
+ public int getDx1(){ return dx1; }
+ public void setDx1( int dx1 ){ this.dx1 = dx1; }
+ public int getDy1(){ return dy1; }
+ public void setDy1( int dy1 ){ this.dy1 = dy1; }
+ public int getDy2(){ return dy2; }
+ public void setDy2( int dy2 ){ this.dy2 = dy2; }
+ public int getDx2(){ return dx2; }
+ public void setDx2( int dx2 ){ this.dx2 = dx2; }
+
+ public abstract boolean isHorizontallyFlipped();
+ public abstract boolean isVerticallyFlipped();
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java
new file mode 100644
index 0000000000..fd69f48cde
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java
@@ -0,0 +1,37 @@
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherClientAnchorRecord;
+import org.apache.poi.ddf.EscherChildAnchorRecord;
+
+public class HSSFChildAnchor
+ extends HSSFAnchor
+{
+ public HSSFChildAnchor()
+ {
+ }
+
+ public HSSFChildAnchor( int dx1, int dy1, int dx2, int dy2 )
+ {
+ super( dx1, dy1, dx2, dy2 );
+ }
+
+ public void setAnchor(int dx1, int dy1, int dx2, int dy2)
+ {
+ this.dx1 = dx1;
+ this.dy1 = dy1;
+ this.dx2 = dx2;
+ this.dy2 = dy2;
+ }
+
+ public boolean isHorizontallyFlipped()
+ {
+ return dx1 > dx2;
+ }
+
+ public boolean isVerticallyFlipped()
+ {
+ return dy1 > dy2;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java
new file mode 100644
index 0000000000..cbbaf167f2
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java
@@ -0,0 +1,207 @@
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.ddf.EscherClientAnchorRecord;
+import org.apache.poi.ddf.EscherRecord;
+
+
+/**
+ * A client anchor is attached to an excel worksheet. It anchors against a
+ * top-left and buttom-right cell.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFClientAnchor
+ extends HSSFAnchor
+{
+ short col1;
+ int row1;
+ short col2;
+ int row2;
+
+ /**
+ * Creates a new client anchor and defaults all the anchor positions to 0.
+ */
+ public HSSFClientAnchor()
+ {
+ }
+
+ /**
+ * Creates a new client anchor and sets the top-left and bottom-right
+ * coordinates of the anchor.
+ *
+ * @param dx1 the x coordinate within the first cell.
+ * @param dy1 the y coordinate within the first cell.
+ * @param dx2 the x coordinate within the second cell.
+ * @param dy2 the y coordinate within the second cell.
+ * @param col1 the column (0 based) of the first cell.
+ * @param row1 the row (0 based) of the first cell.
+ * @param col2 the column (0 based) of the second cell.
+ * @param row2 the row (0 based) of the second cell.
+ */
+ public HSSFClientAnchor( int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2 )
+ {
+ super( dx1, dy1, dx2, dy2 );
+
+ checkRange(dx1, 0, 1023, "dx1");
+ checkRange(dx2, 0, 1023, "dx2");
+ checkRange(dy1, 0, 255, "dy1");
+ checkRange(dy2, 0, 255, "dy2");
+ checkRange(col1, 0, 255, "col1");
+ checkRange(col2, 0, 255, "col2");
+ checkRange(row1, 0, 255 * 256, "row1");
+ checkRange(row2, 0, 255 * 256, "row2");
+
+ this.col1 = col1;
+ this.row1 = row1;
+ this.col2 = col2;
+ this.row2 = row2;
+ }
+
+ /**
+ * Calculates the height of a client anchor in points.
+ *
+ * @param sheet the sheet the anchor will be attached to
+ * @return the shape height.
+ */
+ public float getAnchorHeightInPoints(HSSFSheet sheet )
+ {
+ int y1 = Math.min( getDy1(), getDy2() );
+ int y2 = Math.max( getDy1(), getDy2() );
+ int row1 = Math.min( getRow1(), getRow2() );
+ int row2 = Math.max( getRow1(), getRow2() );
+
+ float points = 0;
+ if (row1 == row2)
+ {
+ points = ((y2 - y1) / 256.0f) * getRowHeightInPoints(sheet, row2);
+ }
+ else
+ {
+ points += ((256.0f - y1) / 256.0f) * getRowHeightInPoints(sheet, row1);
+ for (int i = row1 + 1; i < row2; i++)
+ {
+ points += getRowHeightInPoints(sheet, i);
+ }
+ points += (y2 / 256.0f) * getRowHeightInPoints(sheet, row2);
+ }
+
+ return points;
+ }
+
+ private float getRowHeightInPoints(HSSFSheet sheet, int rowNum)
+ {
+ HSSFRow row = sheet.getRow(rowNum);
+ if (row == null)
+ return sheet.getDefaultRowHeightInPoints();
+ else
+ return row.getHeightInPoints();
+ }
+
+ public short getCol1()
+ {
+ return col1;
+ }
+
+ public void setCol1( short col1 )
+ {
+ checkRange(col1, 0, 255, "col1");
+ this.col1 = col1;
+ }
+
+ public short getCol2()
+ {
+ return col2;
+ }
+
+ public void setCol2( short col2 )
+ {
+ checkRange(col2, 0, 255, "col2");
+ this.col2 = col2;
+ }
+
+ public int getRow1()
+ {
+ return row1;
+ }
+
+ public void setRow1( int row1 )
+ {
+ checkRange(row1, 0, 256 * 256, "row1");
+ this.row1 = row1;
+ }
+
+ public int getRow2()
+ {
+ return row2;
+ }
+
+ public void setRow2( int row2 )
+ {
+ checkRange(row2, 0, 256 * 256, "row2");
+ this.row2 = row2;
+ }
+
+ /**
+ * Dets the top-left and bottom-right
+ * coordinates of the anchor.
+ *
+ * @param x1 the x coordinate within the first cell.
+ * @param y1 the y coordinate within the first cell.
+ * @param x2 the x coordinate within the second cell.
+ * @param y2 the y coordinate within the second cell.
+ * @param col1 the column (0 based) of the first cell.
+ * @param row1 the row (0 based) of the first cell.
+ * @param col2 the column (0 based) of the second cell.
+ * @param row2 the row (0 based) of the second cell.
+ */
+ public void setAnchor( short col1, int row1, int x1, int y1, short col2, int row2, int x2, int y2 )
+ {
+ checkRange(dx1, 0, 1023, "dx1");
+ checkRange(dx2, 0, 1023, "dx2");
+ checkRange(dy1, 0, 255, "dy1");
+ checkRange(dy2, 0, 255, "dy2");
+ checkRange(col1, 0, 255, "col1");
+ checkRange(col2, 0, 255, "col2");
+ checkRange(row1, 0, 255 * 256, "row1");
+ checkRange(row2, 0, 255 * 256, "row2");
+
+ this.col1 = col1;
+ this.row1 = row1;
+ this.dx1 = x1;
+ this.dy1 = y1;
+ this.col2 = col2;
+ this.row2 = row2;
+ this.dx2 = x2;
+ this.dy2 = y2;
+ }
+
+ /**
+ * @return true if the anchor goes from right to left.
+ */
+ public boolean isHorizontallyFlipped()
+ {
+ if (col1 == col2)
+ return dx1 > dx2;
+ else
+ return col1 > col2;
+ }
+
+ /**
+ * @return true if the anchor goes from bottom to top.
+ */
+ public boolean isVerticallyFlipped()
+ {
+ if (row1 == row2)
+ return dy1 > dy2;
+ else
+ return row1 > row2;
+ }
+
+ private void checkRange( int value, int minRange, int maxRange, String varName )
+ {
+ if (value < minRange || value > maxRange)
+ throw new IllegalArgumentException(varName + " must be between " + minRange + " and " + maxRange);
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
index 016c0d42f1..1bc777fe8c 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
@@ -386,4 +386,12 @@ public class HSSFFont
{
return font.getUnderline();
}
+
+ public String toString()
+ {
+ return "org.apache.poi.hssf.usermodel.HSSFFont{" +
+ font +
+ "}";
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
index 863533d455..d1eb55a08d 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
@@ -110,7 +110,60 @@ public class HSSFPalette
}
return null;
}
-
+
+ /**
+ * Finds the closest matching color in the custom palette. The
+ * method for finding the distance between the colors is fairly
+ * primative.
+ *
+ * @param red The red component of the color to match.
+ * @param green The green component of the color to match.
+ * @param blue The blue component of the color to match.
+ * @return The closest color or null if there are no custom
+ * colors currently defined.
+ */
+ public HSSFColor findSimilarColor(byte red, byte green, byte blue)
+ {
+ HSSFColor result = null;
+ int minColorDistance = Integer.MAX_VALUE;
+ byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX);
+ for (short i = (short) PaletteRecord.FIRST_COLOR_INDEX; b != null;
+ b = palette.getColor(++i))
+ {
+ int colorDistance = red - b[0] + green - b[1] + blue - b[2];
+ if (colorDistance < minColorDistance)
+ {
+ result = getColor(i);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Adds a new color into an empty color slot.
+ * @param red The red component
+ * @param green The green component
+ * @param blue The blue component
+ *
+ * @return The new custom color.
+ *
+ * @throws RuntimeException if there are more more free color indexes.
+ */
+ public HSSFColor addColor( byte red, byte green, byte blue )
+ {
+ byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX);
+ short i;
+ for (i = (short) PaletteRecord.FIRST_COLOR_INDEX; i < PaletteRecord.STANDARD_PALETTE_SIZE + PaletteRecord.FIRST_COLOR_INDEX; b = palette.getColor(++i))
+ {
+ if (b == null)
+ {
+ setColorAtIndex( i, red, green, blue );
+ return getColor(i);
+ }
+ }
+ throw new RuntimeException("Could not find free color index");
+ }
+
/**
* Sets the color at the given offset
*
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
new file mode 100644
index 0000000000..d0a376c0ec
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
@@ -0,0 +1,159 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The patriarch is the toplevel container for shapes in a sheet. It does
+ * little other than act as a container for other shapes and groups.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFPatriarch
+ implements HSSFShapeContainer
+{
+ List shapes = new ArrayList();
+ HSSFSheet sheet;
+ int x1 = 0;
+ int y1 = 0 ;
+ int x2 = 1023;
+ int y2 = 255;
+
+ /**
+ * Creates the patriarch.
+ *
+ * @param sheet the sheet this patriarch is stored in.
+ */
+ HSSFPatriarch(HSSFSheet sheet)
+ {
+ this.sheet = sheet;
+ }
+
+ /**
+ * Creates a new group record stored under this patriarch.
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created group.
+ */
+ public HSSFShapeGroup createGroup(HSSFClientAnchor anchor)
+ {
+ HSSFShapeGroup group = new HSSFShapeGroup(null, anchor);
+ group.anchor = anchor;
+ shapes.add(group);
+ return group;
+ }
+
+ /**
+ * Creates a simple shape. This includes such shapes as lines, rectangles,
+ * and ovals.
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created shape.
+ */
+ public HSSFSimpleShape createSimpleShape(HSSFClientAnchor anchor)
+ {
+ HSSFSimpleShape shape = new HSSFSimpleShape(null, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Creates a polygon
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created shape.
+ */
+ public HSSFPolygon createPolygon(HSSFClientAnchor anchor)
+ {
+ HSSFPolygon shape = new HSSFPolygon(null, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Constructs a textbox under the patriarch.
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created textbox.
+ */
+ public HSSFTextbox createTextbox(HSSFClientAnchor anchor)
+ {
+ HSSFTextbox shape = new HSSFTextbox(null, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Returns a list of all shapes contained by the patriarch.
+ */
+ public List getChildren()
+ {
+ return shapes;
+ }
+
+ /**
+ * Total count of all children and their children's children.
+ */
+ public int countOfAllChildren()
+ {
+ int count = shapes.size();
+ for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); )
+ {
+ HSSFShape shape = (HSSFShape) iterator.next();
+ count += shape.countOfAllChildren();
+ }
+ return count;
+ }
+ /**
+ * Sets the coordinate space of this group. All children are contrained
+ * to these coordinates.
+ */
+ public void setCoordinates( int x1, int y1, int x2, int y2 )
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+ /**
+ * The top left x coordinate of this group.
+ */
+ public int getX1()
+ {
+ return x1;
+ }
+
+ /**
+ * The top left y coordinate of this group.
+ */
+ public int getY1()
+ {
+ return y1;
+ }
+
+ /**
+ * The bottom right x coordinate of this group.
+ */
+ public int getX2()
+ {
+ return x2;
+ }
+
+ /**
+ * The bottom right y coordinate of this group.
+ */
+ public int getY2()
+ {
+ return y2;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java
new file mode 100644
index 0000000000..67f4532658
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java
@@ -0,0 +1,66 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * @author Glen Stampoultzis (glens at superlinksoftware.com)
+ */
+public class HSSFPolygon
+ extends HSSFShape
+{
+ int[] xPoints;
+ int[] yPoints;
+ int drawAreaWidth = 100;
+ int drawAreaHeight = 100;
+
+ HSSFPolygon( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ }
+
+ public int[] getXPoints()
+ {
+ return xPoints;
+ }
+
+ public int[] getYPoints()
+ {
+ return yPoints;
+ }
+
+ public void setPoints(int[] xPoints, int[] yPoints)
+ {
+ this.xPoints = cloneArray(xPoints);
+ this.yPoints = cloneArray(yPoints);
+ }
+
+ private int[] cloneArray( int[] a )
+ {
+ int[] result = new int[a.length];
+ for ( int i = 0; i < a.length; i++ )
+ result[i] = a[i];
+
+ return result;
+ }
+
+ /**
+ * Defines the width and height of the points in the polygon
+ * @param width
+ * @param height
+ */
+ public void setPolygonDrawArea( int width, int height )
+ {
+ this.drawAreaWidth = width;
+ this.drawAreaHeight = height;
+ }
+
+ public int getDrawAreaWidth()
+ {
+ return drawAreaWidth;
+ }
+
+ public int getDrawAreaHeight()
+ {
+ return drawAreaHeight;
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java
new file mode 100644
index 0000000000..59edf61d64
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java
@@ -0,0 +1,179 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Rich text unicode string. These strings can have fonts applied to
+ * arbitary parts of the string.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFRichTextString
+ implements Comparable
+{
+ /** Place holder for indicating that NO_FONT has been applied here */
+ public static final short NO_FONT = -1;
+
+ String string;
+ SortedMap formattingRuns = new TreeMap();
+
+ public HSSFRichTextString()
+ {
+ this("");
+ }
+
+ public HSSFRichTextString( String string )
+ {
+ this.string = string;
+ this.formattingRuns.put(new Integer(0), new Short(NO_FONT));
+ }
+
+ /**
+ * Applies a font to the specified characters of a string.
+ *
+ * @param startIndex The start index to apply the font to (inclusive)
+ * @param endIndex The end index to apply the font to (exclusive)
+ * @param fontIndex The font to use.
+ */
+ public void applyFont(int startIndex, int endIndex, short fontIndex)
+ {
+ if (startIndex > endIndex)
+ throw new IllegalArgumentException("Start index must be less than end index.");
+ if (startIndex < 0 || endIndex > length())
+ throw new IllegalArgumentException("Start and end index not in range.");
+ if (startIndex == endIndex)
+ return;
+
+ Integer from = new Integer(startIndex);
+ Integer to = new Integer(endIndex);
+ short fontAtIndex = NO_FONT;
+ if (endIndex != length())
+ fontAtIndex = getFontAtIndex(endIndex);
+ formattingRuns.subMap(from, to).clear();
+ formattingRuns.put(from, new Short(fontIndex));
+ if (endIndex != length())
+ {
+ if (fontIndex != fontAtIndex)
+ formattingRuns.put(to, new Short(fontAtIndex));
+ }
+ }
+
+ /**
+ * Applies a font to the specified characters of a string.
+ *
+ * @param startIndex The start index to apply the font to (inclusive)
+ * @param endIndex The end index to apply to font to (exclusive)
+ * @param font The index of the font to use.
+ */
+ public void applyFont(int startIndex, int endIndex, HSSFFont font)
+ {
+ applyFont(startIndex, endIndex, font.getIndex());
+ }
+
+ /**
+ * Sets the font of the entire string.
+ * @param font The font to use.
+ */
+ public void applyFont(HSSFFont font)
+ {
+ applyFont(0, string.length(), font);
+ }
+
+ /**
+ * Returns the plain string representation.
+ */
+ public String getString()
+ {
+ return string;
+ }
+
+ /**
+ * @return the number of characters in the font.
+ */
+ public int length()
+ {
+ return string.length();
+ }
+
+ /**
+ * Returns the font in use at a particular index.
+ *
+ * @param index The index.
+ * @return The font that's currently being applied at that
+ * index or null if no font is being applied or the
+ * index is out of range.
+ */
+ public short getFontAtIndex( int index )
+ {
+ if (index < 0 || index >= string.length())
+ throw new ArrayIndexOutOfBoundsException("Font index " + index + " out of bounds of string");
+ Integer key = new Integer(index + 1);
+ SortedMap head = formattingRuns.headMap(key);
+ if (head.isEmpty())
+ throw new IllegalStateException("Should not reach here. No font found.");
+ else
+ return ((Short) head.get(head.lastKey())).shortValue();
+ }
+
+ /**
+ * @return The number of formatting runs used. There will always be at
+ * least one of font NO_FONT.
+ *
+ * @see #NO_FONT
+ */
+ public int numFormattingRuns()
+ {
+ return formattingRuns.size();
+ }
+
+ /**
+ * The index within the string to which the specified formatting run applies.
+ * @param index the index of the formatting run
+ * @return the index within the string.
+ */
+ public int getIndexOfFormattingRun(int index)
+ {
+ Map.Entry[] runs = (Map.Entry[]) formattingRuns.entrySet().toArray(new Map.Entry[formattingRuns.size()] );
+ return ((Integer)runs[index].getKey()).intValue();
+ }
+
+ /**
+ * Gets the font used in a particular formatting run.
+ *
+ * @param index the index of the formatting run
+ * @return the font number used.
+ */
+ public short getFontOfFormattingRun(int index)
+ {
+ Map.Entry[] runs = (Map.Entry[]) formattingRuns.entrySet().toArray(new Map.Entry[formattingRuns.size()] );
+ return ((Short)(runs[index].getValue())).shortValue();
+ }
+
+ /**
+ * Compares one rich text string to another.
+ */
+ public int compareTo( Object o )
+ {
+ return 0; // todo
+ }
+
+ /**
+ * @return the plain text representation of this string.
+ */
+ public String toString()
+ {
+ return string;
+ }
+
+ /**
+ * Applies the specified font to the entire string.
+ *
+ * @param fontIndex the font to apply.
+ */
+ public void applyFont( short fontIndex )
+ {
+ applyFont(0, string.length(), fontIndex);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java
new file mode 100644
index 0000000000..61620f2351
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java
@@ -0,0 +1,195 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * An abstract shape.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public abstract class HSSFShape
+{
+ public static final int LINEWIDTH_ONE_PT = 12700;
+ public static final int LINEWIDTH_DEFAULT = 9525;
+
+ public static final int LINESTYLE_SOLID = 0; // Solid (continuous) pen
+ public static final int LINESTYLE_DASHSYS = 1; // PS_DASH system dash style
+ public static final int LINESTYLE_DOTSYS = 2; // PS_DOT system dash style
+ public static final int LINESTYLE_DASHDOTSYS = 3; // PS_DASHDOT system dash style
+ public static final int LINESTYLE_DASHDOTDOTSYS = 4; // PS_DASHDOTDOT system dash style
+ public static final int LINESTYLE_DOTGEL = 5; // square dot style
+ public static final int LINESTYLE_DASHGEL = 6; // dash style
+ public static final int LINESTYLE_LONGDASHGEL = 7; // long dash style
+ public static final int LINESTYLE_DASHDOTGEL = 8; // dash short dash
+ public static final int LINESTYLE_LONGDASHDOTGEL = 9; // long dash short dash
+ public static final int LINESTYLE_LONGDASHDOTDOTGEL = 10; // long dash short dash short dash
+ public static final int LINESTYLE_NONE = -1;
+
+ HSSFShape parent;
+ HSSFAnchor anchor;
+ int lineStyleColor = 0x08000040;
+ int fillColor = 0x08000009;
+ int lineWidth = LINEWIDTH_DEFAULT; // 12700 = 1pt
+ int lineStyle = LINESTYLE_SOLID;
+ boolean noFill = false;
+
+ /**
+ * Create a new shape with the specified parent and anchor.
+ */
+ HSSFShape( HSSFShape parent, HSSFAnchor anchor )
+ {
+ this.parent = parent;
+ this.anchor = anchor;
+ }
+
+ /**
+ * Gets the parent shape.
+ */
+ public HSSFShape getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * @return the anchor that is used by this shape.
+ */
+ public HSSFAnchor getAnchor()
+ {
+ return anchor;
+ }
+
+ /**
+ * Sets a particular anchor. A top-level shape must have an anchor of
+ * HSSFClientAnchor. A child anchor must have an anchor of HSSFChildAnchor
+ *
+ * @param anchor the anchor to use.
+ * @throws IllegalArgumentException when the wrong anchor is used for
+ * this particular shape.
+ *
+ * @see HSSFChildAnchor
+ * @see HSSFClientAnchor
+ */
+ public void setAnchor( HSSFAnchor anchor )
+ {
+ if ( parent == null )
+ {
+ if ( anchor instanceof HSSFChildAnchor )
+ throw new IllegalArgumentException( "Must use client anchors for shapes directly attached to sheet." );
+ }
+ else
+ {
+ if ( anchor instanceof HSSFClientAnchor )
+ throw new IllegalArgumentException( "Must use child anchors for shapes attached to groups." );
+ }
+
+ this.anchor = anchor;
+ }
+
+ /**
+ * The color applied to the lines of this shape.
+ */
+ public int getLineStyleColor()
+ {
+ return lineStyleColor;
+ }
+
+ /**
+ * The color applied to the lines of this shape.
+ */
+ public void setLineStyleColor( int lineStyleColor )
+ {
+ this.lineStyleColor = lineStyleColor;
+ }
+
+ /**
+ * The color applied to the lines of this shape.
+ */
+ public void setLineStyleColor( int red, int green, int blue )
+ {
+ this.lineStyleColor = ((blue) << 16) | ((green) << 8) | red;
+ }
+
+ /**
+ * The color used to fill this shape.
+ */
+ public int getFillColor()
+ {
+ return fillColor;
+ }
+
+ /**
+ * The color used to fill this shape.
+ */
+ public void setFillColor( int fillColor )
+ {
+ this.fillColor = fillColor;
+ }
+
+ /**
+ * The color used to fill this shape.
+ */
+ public void setFillColor( int red, int green, int blue )
+ {
+ this.fillColor = ((blue) << 16) | ((green) << 8) | red;
+ }
+
+ /**
+ * @return returns with width of the line in EMUs. 12700 = 1 pt.
+ */
+ public int getLineWidth()
+ {
+ return lineWidth;
+ }
+
+ /**
+ * Sets the width of the line. 12700 = 1 pt.
+ *
+ * @param lineWidth width in EMU's. 12700EMU's = 1 pt
+ *
+ * @see HSSFShape#LINEWIDTH_ONE_PT
+ */
+ public void setLineWidth( int lineWidth )
+ {
+ this.lineWidth = lineWidth;
+ }
+
+ /**
+ * @return One of the constants in LINESTYLE_*
+ */
+ public int getLineStyle()
+ {
+ return lineStyle;
+ }
+
+ /**
+ * Sets the line style.
+ *
+ * @param lineStyle One of the constants in LINESTYLE_*
+ */
+ public void setLineStyle( int lineStyle )
+ {
+ this.lineStyle = lineStyle;
+ }
+
+ /**
+ * @return true if this shape is not filled with a color.
+ */
+ public boolean isNoFill()
+ {
+ return noFill;
+ }
+
+ /**
+ * Sets whether this shape is filled or transparent.
+ */
+ public void setNoFill( boolean noFill )
+ {
+ this.noFill = noFill;
+ }
+
+ /**
+ * Count of all children and their childrens children.
+ */
+ public int countOfAllChildren()
+ {
+ return 1;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java
new file mode 100644
index 0000000000..f641fe9c0e
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java
@@ -0,0 +1,17 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.List;
+
+/**
+ * An interface that indicates whether a class can contain children.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public interface HSSFShapeContainer
+{
+ /**
+ * @return Any children contained by this shape.
+ */
+ List getChildren();
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java
new file mode 100644
index 0000000000..c33c8c6d34
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java
@@ -0,0 +1,148 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * A shape group may contain other shapes. It was no actual form on the
+ * sheet.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFShapeGroup
+ extends HSSFShape
+ implements HSSFShapeContainer
+{
+ List shapes = new ArrayList();
+ int x1 = 0;
+ int y1 = 0 ;
+ int x2 = 1023;
+ int y2 = 255;
+
+
+ public HSSFShapeGroup( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ }
+
+ /**
+ * Create another group under this group.
+ * @param anchor the position of the new group.
+ * @return the group
+ */
+ public HSSFShapeGroup createGroup(HSSFChildAnchor anchor)
+ {
+ HSSFShapeGroup group = new HSSFShapeGroup(this, anchor);
+ group.anchor = anchor;
+ shapes.add(group);
+ return group;
+ }
+
+ /**
+ * Create a new simple shape under this group.
+ * @param anchor the position of the shape.
+ * @return the shape
+ */
+ public HSSFSimpleShape createShape(HSSFChildAnchor anchor)
+ {
+ HSSFSimpleShape shape = new HSSFSimpleShape(this, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Create a new textbox under this group.
+ * @param anchor the position of the shape.
+ * @return the textbox
+ */
+ public HSSFTextbox createTextbox(HSSFChildAnchor anchor)
+ {
+ HSSFTextbox shape = new HSSFTextbox(this, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Creates a polygon
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created shape.
+ */
+ public HSSFPolygon createPolygon(HSSFChildAnchor anchor)
+ {
+ HSSFPolygon shape = new HSSFPolygon(this, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Return all children contained by this shape.
+ */
+ public List getChildren()
+ {
+ return shapes;
+ }
+
+ /**
+ * Sets the coordinate space of this group. All children are contrained
+ * to these coordinates.
+ */
+ public void setCoordinates( int x1, int y1, int x2, int y2 )
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+ /**
+ * The top left x coordinate of this group.
+ */
+ public int getX1()
+ {
+ return x1;
+ }
+
+ /**
+ * The top left y coordinate of this group.
+ */
+ public int getY1()
+ {
+ return y1;
+ }
+
+ /**
+ * The bottom right x coordinate of this group.
+ */
+ public int getX2()
+ {
+ return x2;
+ }
+
+ /**
+ * The bottom right y coordinate of this group.
+ */
+ public int getY2()
+ {
+ return y2;
+ }
+
+ /**
+ * Count of all children and their childrens children.
+ */
+ public int countOfAllChildren()
+ {
+ int count = shapes.size();
+ for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); )
+ {
+ HSSFShape shape = (HSSFShape) iterator.next();
+ count += shape.countOfAllChildren();
+ }
+ return count;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index c0fcfe0aa8..a48c87d43f 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -62,19 +62,15 @@ package org.apache.poi.hssf.usermodel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.io.PrintWriter;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
-import org.apache.poi.hssf.record.CellValueRecordInterface;
-import org.apache.poi.hssf.record.HCenterRecord;
-import org.apache.poi.hssf.record.RowRecord;
-import org.apache.poi.hssf.record.SCLRecord;
-import org.apache.poi.hssf.record.VCenterRecord;
-import org.apache.poi.hssf.record.WSBoolRecord;
-import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.util.Region;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
+import org.apache.poi.ddf.EscherRecord;
/**
* High level representation of a worksheet.
@@ -979,6 +975,9 @@ public class HSSFSheet
* <p>
* Additionally shifts merged regions that are completely defined in these
* rows (ie. merged 2 cells on a row to be shifted).
+ * <p>
+ * TODO Might want to add bounds checking here
+ *
* @param startRow the row to start shifting
* @param endRow the row to end shifting
* @param n the number of rows to shift
@@ -1001,8 +1000,9 @@ public class HSSFSheet
inc = -1;
}
- shiftMerged(startRow, endRow, n, true);
-
+ shiftMerged(startRow, endRow, n, true);
+ sheet.shiftRowBreaks(startRow, endRow, n);
+
for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc )
{
HSSFRow row = getRow( rowNum );
@@ -1146,6 +1146,149 @@ public class HSSFSheet
public boolean isDisplayRowColHeadings() {
return sheet.isDisplayRowColHeadings();
}
+
+ /**
+ * Sets a page break at the indicated row
+ * @param row
+ */
+ public void setRowBreak(int row) {
+ validateRow(row);
+ sheet.setRowBreak(row, (short)0, (short)255);
+ }
+
+ /**
+ * Determines if there is a page break at the indicated row
+ * @param row
+ * @return
+ */
+ public boolean isRowBroken(int row) {
+ return sheet.isRowBroken(row);
+ }
+
+ /**
+ * Removes the page break at the indicated row
+ * @param row
+ */
+ public void removeRowBreak(int row) {
+ sheet.removeRowBreak(row);
+ }
+
+ /**
+ * Retrieves all the horizontal page breaks
+ * @return
+ */
+ public int[] getRowBreaks(){
+ //we can probably cache this information, but this should be a sparsely used function
+ int[] returnValue = new int[sheet.getNumRowBreaks()];
+ Iterator iterator = sheet.getRowBreaks();
+ int i = 0;
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ returnValue[i++] = (int)breakItem.main;
+ }
+ return returnValue;
+ }
+
+ /**
+ * Retrieves all the vertical page breaks
+ * @return
+ */
+ public short[] getColumnBreaks(){
+ //we can probably cache this information, but this should be a sparsely used function
+ short[] returnValue = new short[sheet.getNumColumnBreaks()];
+ Iterator iterator = sheet.getColumnBreaks();
+ int i = 0;
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ returnValue[i++] = breakItem.main;
+ }
+ return returnValue;
+ }
+
+
+ /**
+ * Sets a page break at the indicated column
+ * @param column
+ */
+ public void setColumnBreak(short column) {
+ validateColumn(column);
+ sheet.setColumnBreak(column, (short)0, (short)65535);
+ }
+
+ /**
+ * Determines if there is a page break at the indicated column
+ * @param column
+ * @return
+ */
+ public boolean isColumnBroken(short column) {
+ return sheet.isColumnBroken(column);
+ }
+
+ /**
+ * Removes a page break at the indicated column
+ * @param column
+ */
+ public void removeColumnBreak(short column) {
+ sheet.removeColumnBreak(column);
+ }
+
+ /**
+ * Runs a bounds check for row numbers
+ * @param row
+ */
+ protected void validateRow(int row) {
+ if (row > 65535) throw new IllegalArgumentException("Maximum row number is 65535");
+ if (row < 0) throw new IllegalArgumentException("Minumum row number is 0");
+ }
+
+ /**
+ * Runs a bounds check for column numbers
+ * @param column
+ */
+ protected void validateColumn(short column) {
+ if (column > 255) throw new IllegalArgumentException("Maximum column number is 255");
+ if (column < 0) throw new IllegalArgumentException("Minimum column number is 0");
+ }
+
+ /**
+ * Aggregates the drawing records and dumps the escher record hierarchy
+ * to the standard output.
+ */
+ public void dumpDrawingRecords()
+ {
+ sheet.aggregateDrawingRecords(book.getDrawingManager());
+
+ EscherAggregate r = (EscherAggregate) getSheet().findFirstRecordBySid(EscherAggregate.sid);
+ List escherRecords = r.getEscherRecords();
+ for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord escherRecord = (EscherRecord) iterator.next();
+ PrintWriter w = new PrintWriter(System.out);
+ escherRecord.display(w, 0);
+ w.close();
+ }
+ }
+
+ /**
+ * Creates the toplevel drawing patriarch. This will have the effect of
+ * removing any existing drawings on this sheet.
+ *
+ * @return The new patriarch.
+ */
+ public HSSFPatriarch createDrawingPatriarch()
+ {
+ // Create the drawing group if it doesn't already exist.
+ book.createDrawingGroup();
+
+ sheet.aggregateDrawingRecords(book.getDrawingManager());
+ EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
+ HSSFPatriarch patriarch = new HSSFPatriarch(this);
+ agg.clear(); // Initially the behaviour will be to clear out any existing shapes in the sheet when
+ // creating a new patriarch.
+ agg.setPatriarch(patriarch);
+ return patriarch;
+ }
+
}
class SheetRowIterator implements Iterator {
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java
new file mode 100644
index 0000000000..c90fe6bb8c
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java
@@ -0,0 +1,64 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * Represents a simple shape such as a line, rectangle or oval.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFSimpleShape
+ extends HSSFShape
+{
+ // The commented out ones haven't been tested yet or aren't supported
+ // by HSSFSimpleShape.
+
+ public final static short OBJECT_TYPE_LINE = 1;
+ public final static short OBJECT_TYPE_RECTANGLE = 2;
+ public final static short OBJECT_TYPE_OVAL = 3;
+// public final static short OBJECT_TYPE_ARC = 4;
+// public final static short OBJECT_TYPE_CHART = 5;
+// public final static short OBJECT_TYPE_TEXT = 6;
+// public final static short OBJECT_TYPE_BUTTON = 7;
+// public final static short OBJECT_TYPE_PICTURE = 8;
+// public final static short OBJECT_TYPE_POLYGON = 9;
+// public final static short OBJECT_TYPE_CHECKBOX = 11;
+// public final static short OBJECT_TYPE_OPTION_BUTTON = 12;
+// public final static short OBJECT_TYPE_EDIT_BOX = 13;
+// public final static short OBJECT_TYPE_LABEL = 14;
+// public final static short OBJECT_TYPE_DIALOG_BOX = 15;
+// public final static short OBJECT_TYPE_SPINNER = 16;
+// public final static short OBJECT_TYPE_SCROLL_BAR = 17;
+// public final static short OBJECT_TYPE_LIST_BOX = 18;
+// public final static short OBJECT_TYPE_GROUP_BOX = 19;
+// public final static short OBJECT_TYPE_COMBO_BOX = 20;
+// public final static short OBJECT_TYPE_COMMENT = 25;
+// public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30;
+
+ int shapeType = OBJECT_TYPE_LINE;
+
+ HSSFSimpleShape( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ }
+
+ /**
+ * Gets the shape type.
+ * @return One of the OBJECT_TYPE_* constants.
+ *
+ * @see #OBJECT_TYPE_LINE
+ * @see #OBJECT_TYPE_OVAL
+ * @see #OBJECT_TYPE_RECTANGLE
+ */
+ public int getShapeType() { return shapeType; }
+
+ /**
+ * Sets the shape types.
+ *
+ * @param shapeType One of the OBJECT_TYPE_* constants.
+ *
+ * @see #OBJECT_TYPE_LINE
+ * @see #OBJECT_TYPE_OVAL
+ * @see #OBJECT_TYPE_RECTANGLE
+ */
+ public void setShapeType( int shapeType ){ this.shapeType = shapeType; }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java b/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java
new file mode 100644
index 0000000000..423e256fac
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java
@@ -0,0 +1,107 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * A textbox is a shape that may hold a rich text string.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFTextbox
+ extends HSSFSimpleShape
+{
+ public final static short OBJECT_TYPE_TEXT = 6;
+
+ int marginLeft, marginRight, marginTop, marginBottom;
+
+ HSSFRichTextString string = new HSSFRichTextString("");
+
+ /**
+ * Construct a new textbox with the given parent and anchor.
+ * @param parent
+ * @param anchor One of HSSFClientAnchor or HSSFChildAnchor
+ */
+ public HSSFTextbox( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ setShapeType(OBJECT_TYPE_TEXT);
+ }
+
+ /**
+ * @return the rich text string for this textbox.
+ */
+ public HSSFRichTextString getString()
+ {
+ return string;
+ }
+
+ /**
+ * @param string Sets the rich text string used by this object.
+ */
+ public void setString( HSSFRichTextString string )
+ {
+ this.string = string;
+ }
+
+ /**
+ * @return Returns the left margin within the textbox.
+ */
+ public int getMarginLeft()
+ {
+ return marginLeft;
+ }
+
+ /**
+ * Sets the left margin within the textbox.
+ */
+ public void setMarginLeft( int marginLeft )
+ {
+ this.marginLeft = marginLeft;
+ }
+
+ /**
+ * @return returns the right margin within the textbox.
+ */
+ public int getMarginRight()
+ {
+ return marginRight;
+ }
+
+ /**
+ * Sets the right margin within the textbox.
+ */
+ public void setMarginRight( int marginRight )
+ {
+ this.marginRight = marginRight;
+ }
+
+ /**
+ * @return returns the top margin within the textbox.
+ */
+ public int getMarginTop()
+ {
+ return marginTop;
+ }
+
+ /**
+ * Sets the top margin within the textbox.
+ */
+ public void setMarginTop( int marginTop )
+ {
+ this.marginTop = marginTop;
+ }
+
+ /**
+ * Gets the bottom margin within the textbox.
+ */
+ public int getMarginBottom()
+ {
+ return marginBottom;
+ }
+
+ /**
+ * Sets the bottom margin within the textbox.
+ */
+ public void setMarginBottom( int marginBottom )
+ {
+ this.marginBottom = marginBottom;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index 02bdc81167..9fa70e7471 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -653,6 +653,40 @@ public class HSSFWorkbook
}
/**
+ * Finds a font that matches the one with the supplied attributes
+ */
+ public HSSFFont findFont(short boldWeight, short color, short fontHeight,
+ String name, boolean italic, boolean strikeout,
+ short typeOffset, byte underline)
+ {
+// System.out.println( boldWeight + ", " + color + ", " + fontHeight + ", " + name + ", " + italic + ", " + strikeout + ", " + typeOffset + ", " + underline );
+ for (short i = 0; i < workbook.getNumberOfFontRecords(); i++)
+ {
+ if (i == 4)
+ continue;
+
+ FontRecord font = workbook.getFontRecordAt(i);
+ HSSFFont hssfFont = new HSSFFont(i, font);
+// System.out.println( hssfFont.getBoldweight() + ", " + hssfFont.getColor() + ", " + hssfFont.getFontHeight() + ", " + hssfFont.getFontName() + ", " + hssfFont.getItalic() + ", " + hssfFont.getStrikeout() + ", " + hssfFont.getTypeOffset() + ", " + hssfFont.getUnderline() );
+ if (hssfFont.getBoldweight() == boldWeight
+ && hssfFont.getColor() == color
+ && hssfFont.getFontHeight() == fontHeight
+ && hssfFont.getFontName().equals(name)
+ && hssfFont.getItalic() == italic
+ && hssfFont.getStrikeout() == strikeout
+ && hssfFont.getTypeOffset() == typeOffset
+ && hssfFont.getUnderline() == underline)
+ {
+// System.out.println( "Found font" );
+ return hssfFont;
+ }
+ }
+
+// System.out.println( "No font found" );
+ return null;
+ }
+
+ /**
* get the number of fonts in the font table
* @return number of fonts
*/
diff --git a/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java b/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java
new file mode 100644
index 0000000000..1c7b330cc3
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java
@@ -0,0 +1,81 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.*;
+import java.awt.*;
+import java.io.*;
+
+/**
+ * Allows the user to lookup the font metrics for a particular font without
+ * actually having the font on the system. The font details are loaded
+ * as a resource from the POI jar file (or classpath) and should be contained
+ * in path "/font_metrics.properties". The font widths are for a 10 point
+ * version of the font. Use a multiplier for other sizes.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+class StaticFontMetrics
+{
+ private static Properties fontMetricsProps;
+ private static Map fontDetailsMap = new HashMap();
+
+ /**
+ * Retrieves the fake font details for a given font.
+ * @param font the font to lookup.
+ * @return the fake font.
+ */
+ public static FontDetails getFontDetails(Font font)
+ {
+ if (fontMetricsProps == null)
+ {
+ InputStream metricsIn = null;
+ try
+ {
+ fontMetricsProps = new Properties();
+ if (System.getProperty("font.metrics.filename") != null)
+ {
+ String filename = System.getProperty("font.metrics.filename");
+ File file = new File(filename);
+ if (!file.exists())
+ throw new FileNotFoundException("font_metrics.properties not found at path " + file.getAbsolutePath());
+ metricsIn = new FileInputStream(file);
+ }
+ else
+ {
+ metricsIn = FontDetails.class.getResourceAsStream("/font_metrics.properties");
+ if (metricsIn == null)
+ throw new FileNotFoundException("font_metrics.properties not found in classpath");
+ }
+ fontMetricsProps.load(metricsIn);
+ }
+ catch ( IOException e )
+ {
+ throw new RuntimeException("Could not load font metrics: " + e.getMessage());
+ }
+ finally
+ {
+ if (metricsIn != null)
+ {
+ try
+ {
+ metricsIn.close();
+ }
+ catch ( IOException ignore ) { }
+ }
+ }
+ }
+
+ String fontName = font.getName();
+
+ if (fontDetailsMap.get(fontName) == null)
+ {
+ FontDetails fontDetails = FontDetails.create(fontName, fontMetricsProps);
+ fontDetailsMap.put( fontName, fontDetails );
+ return fontDetails;
+ }
+ else
+ {
+ return (FontDetails) fontDetailsMap.get(fontName);
+ }
+
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics.java b/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics.java
new file mode 100644
index 0000000000..336b961f42
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics.java
@@ -0,0 +1,71 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+import java.awt.*;
+
+/**
+ * Tests the capabilities of the EscherGraphics class.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestEscherGraphics extends TestCase
+{
+ private HSSFShapeGroup escherGroup;
+ private EscherGraphics graphics;
+
+ protected void setUp() throws Exception
+ {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("test");
+ escherGroup = sheet.createDrawingPatriarch().createGroup(new HSSFClientAnchor(0,0,1023,255,(short)0,0,(short) 0,0));
+ escherGroup = new HSSFShapeGroup(null, new HSSFChildAnchor());
+ graphics = new EscherGraphics(this.escherGroup, workbook, Color.black, 1.0f);
+ super.setUp();
+ }
+
+ public void testGetFont() throws Exception
+ {
+ Font f = graphics.getFont();
+ assertEquals("java.awt.Font[family=Arial,name=Arial,style=plain,size=10]", f.toString());
+ }
+
+ public void testGetFontMetrics() throws Exception
+ {
+ FontMetrics fontMetrics = graphics.getFontMetrics(graphics.getFont());
+ assertEquals(7, fontMetrics.charWidth('X'));
+ assertEquals("java.awt.Font[family=Arial,name=Arial,style=plain,size=10]", fontMetrics.getFont().toString());
+ }
+
+ public void testSetFont() throws Exception
+ {
+ Font f = new Font("Helvetica", 0, 12);
+ graphics.setFont(f);
+ assertEquals(f, graphics.getFont());
+ }
+
+ public void testSetColor() throws Exception
+ {
+ graphics.setColor(Color.red);
+ assertEquals(Color.red, graphics.getColor());
+ }
+
+ public void testFillRect() throws Exception
+ {
+ graphics.fillRect( 10, 10, 20, 20 );
+ HSSFSimpleShape s = (HSSFSimpleShape) escherGroup.getChildren().get(0);
+ assertEquals(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE, s.getShapeType());
+ assertEquals(10, s.getAnchor().getDx1());
+ assertEquals(10, s.getAnchor().getDy1());
+ assertEquals(30, s.getAnchor().getDy2());
+ assertEquals(30, s.getAnchor().getDx2());
+ }
+
+ public void testDrawString() throws Exception
+ {
+ graphics.drawString("This is a test", 10, 10);
+ HSSFTextbox t = (HSSFTextbox) escherGroup.getChildren().get(0);
+ assertEquals("This is a test", t.getString().toString());
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics2d.java
new file mode 100644
index 0000000000..22f0e28c45
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics2d.java
@@ -0,0 +1,74 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+import java.awt.*;
+import java.io.FileOutputStream;
+
+/**
+ * Tests the Graphics2d drawing capability.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestEscherGraphics2d extends TestCase
+{
+ private HSSFShapeGroup escherGroup;
+ private EscherGraphics2d graphics;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("test");
+ escherGroup = sheet.createDrawingPatriarch().createGroup(new HSSFClientAnchor(0,0,1023,255,(short)0,0,(short) 0,0));
+ escherGroup = new HSSFShapeGroup(null, new HSSFChildAnchor());
+ EscherGraphics g = new EscherGraphics(this.escherGroup, workbook, Color.black, 1.0f);
+ graphics = new EscherGraphics2d(g);
+
+ }
+
+ public void testDrawString() throws Exception
+ {
+ graphics.drawString("This is a test", 10, 10);
+ HSSFTextbox t = (HSSFTextbox) escherGroup.getChildren().get(0);
+ assertEquals("This is a test", t.getString().toString());
+ }
+
+ public void testFillRect() throws Exception
+ {
+ graphics.fillRect( 10, 10, 20, 20 );
+ HSSFSimpleShape s = (HSSFSimpleShape) escherGroup.getChildren().get(0);
+ assertEquals(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE, s.getShapeType());
+ assertEquals(10, s.getAnchor().getDx1());
+ assertEquals(10, s.getAnchor().getDy1());
+ assertEquals(30, s.getAnchor().getDy2());
+ assertEquals(30, s.getAnchor().getDx2());
+ }
+
+ public void testGetFontMetrics() throws Exception
+ {
+ FontMetrics fontMetrics = graphics.getFontMetrics(graphics.getFont());
+ assertEquals(7, fontMetrics.charWidth('X'));
+ assertEquals("java.awt.Font[family=Arial,name=Arial,style=plain,size=10]", fontMetrics.getFont().toString());
+ }
+
+ public void testSetFont() throws Exception
+ {
+ Font f = new Font("Helvetica", 0, 12);
+ graphics.setFont(f);
+ assertEquals(f, graphics.getFont());
+ }
+
+ public void testSetColor() throws Exception
+ {
+ graphics.setColor(Color.red);
+ assertEquals(Color.red, graphics.getColor());
+ }
+
+ public void testGetFont() throws Exception
+ {
+ Font f = graphics.getFont();
+ assertEquals("java.awt.Font[family=Arial,name=Arial,style=plain,size=10]", f.toString());
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestFontDetails.java b/src/java/org/apache/poi/hssf/usermodel/TestFontDetails.java
new file mode 100644
index 0000000000..532e62568b
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestFontDetails.java
@@ -0,0 +1,45 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+import java.util.Properties;
+
+/**
+ * Tests the implementation of the FontDetails class.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestFontDetails extends TestCase
+{
+ private Properties properties;
+ private FontDetails fontDetails;
+
+ protected void setUp() throws Exception
+ {
+ properties = new Properties();
+ properties.setProperty("font.Arial.height", "13");
+ properties.setProperty("font.Arial.characters", "a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ");
+ properties.setProperty("font.Arial.widths", "6, 6, 6, 6, 6, 3, 6, 6, 3, 4, 6, 3, 9, 6, 6, 6, 6, 4, 6, 3, 6, 7, 9, 6, 5, 5, 7, 7, 7, 7, 7, 6, 8, 7, 3, 6, 7, 6, 9, 7, 8, 7, 8, 7, 7, 5, 7, 7, 9, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ");
+ fontDetails = FontDetails.create("Arial", properties);
+
+ }
+
+ public void testCreate() throws Exception
+ {
+ assertEquals(13, fontDetails.getHeight());
+ assertEquals(6, fontDetails.getCharWidth('a'));
+ assertEquals(3, fontDetails.getCharWidth('f'));
+ }
+
+ public void testGetStringWidth() throws Exception
+ {
+ assertEquals(9, fontDetails.getStringWidth("af"));
+ }
+
+ public void testGetCharWidth() throws Exception
+ {
+ assertEquals(6, fontDetails.getCharWidth('a'));
+ assertEquals(9, fontDetails.getCharWidth('='));
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestHSSFClientAnchor.java b/src/java/org/apache/poi/hssf/usermodel/TestHSSFClientAnchor.java
new file mode 100644
index 0000000000..ab3e06a3d6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestHSSFClientAnchor.java
@@ -0,0 +1,44 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+/**
+ * Various tests for HSSFClientAnchor.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestHSSFClientAnchor extends TestCase
+{
+ public void testGetAnchorHeightInPoints() throws Exception
+ {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet("test");
+ HSSFClientAnchor a = new HSSFClientAnchor(0,0,1023,255,(short)0,0,(short)0,0);
+ float p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(11.953,p,0.001);
+
+ sheet.createRow(0).setHeightInPoints(14);
+ a = new HSSFClientAnchor(0,0,1023,255,(short)0,0,(short)0,0);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(13.945,p,0.001);
+
+ a = new HSSFClientAnchor(0,0,1023,127,(short)0,0,(short)0,0);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(6.945,p,0.001);
+
+ a = new HSSFClientAnchor(0,126,1023,127,(short)0,0,(short)0,0);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(0.054,p,0.001);
+
+ a = new HSSFClientAnchor(0,0,1023,0,(short)0,0,(short)0,1);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(14.0,p,0.001);
+
+ sheet.createRow(0).setHeightInPoints(12);
+ a = new HSSFClientAnchor(0,127,1023,127,(short)0,0,(short)0,1);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(12.0,p,0.001);
+
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestHSSFRichTextString.java b/src/java/org/apache/poi/hssf/usermodel/TestHSSFRichTextString.java
new file mode 100644
index 0000000000..69dcb78e7e
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestHSSFRichTextString.java
@@ -0,0 +1,50 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+public class TestHSSFRichTextString extends TestCase
+{
+ public void testApplyFont() throws Exception
+ {
+
+ HSSFRichTextString r = new HSSFRichTextString("testing");
+ assertEquals(1,r.numFormattingRuns());
+ r.applyFont(2,4, new HSSFFont((short)1, null));
+ assertEquals(3,r.numFormattingRuns());
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(0));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(1));
+ assertEquals(1, r.getFontAtIndex(2));
+ assertEquals(1, r.getFontAtIndex(3));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(4));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(5));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(6));
+
+ r.applyFont(6,7, new HSSFFont((short)2, null));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(0));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(1));
+ assertEquals(1, r.getFontAtIndex(2));
+ assertEquals(1, r.getFontAtIndex(3));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(4));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(5));
+ assertEquals(2, r.getFontAtIndex(6));
+
+ r.applyFont(HSSFRichTextString.NO_FONT);
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(0));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(1));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(2));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(3));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(4));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(5));
+
+ r.applyFont(new HSSFFont((short)1, null));
+ assertEquals(1, r.getFontAtIndex(0));
+ assertEquals(1, r.getFontAtIndex(1));
+ assertEquals(1, r.getFontAtIndex(2));
+ assertEquals(1, r.getFontAtIndex(3));
+ assertEquals(1, r.getFontAtIndex(4));
+ assertEquals(1, r.getFontAtIndex(5));
+ assertEquals(1, r.getFontAtIndex(6));
+
+ }
+
+}