aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/poi/hssf/model
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/model
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/model')
-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
10 files changed, 1172 insertions, 15 deletions
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;
+ }
}