diff options
author | Glen Stampoultzis <glens@apache.org> | 2004-04-09 11:45:38 +0000 |
---|---|---|
committer | Glen Stampoultzis <glens@apache.org> | 2004-04-09 11:45:38 +0000 |
commit | b6ea214cc1e42332b7198954c63a783935b4bdf4 (patch) | |
tree | 9ffdd5aea352c9c258ed2df5fa1a4deb4d63792e /src/java/org/apache/poi/hssf/model | |
parent | 0873a633af2dc09fd9eab9ee1b2d5835e0d7fc2d (diff) | |
download | poi-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.java | 126 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/ConvertAnchor.java | 50 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/DrawingManager.java | 136 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/LineShape.java | 105 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/PolygonShape.java | 143 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/Sheet.java | 232 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/SimpleFilledShape.java | 111 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/TestDrawingManager.java | 81 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/TextboxShape.java | 151 | ||||
-rw-r--r-- | src/java/org/apache/poi/hssf/model/Workbook.java | 52 |
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; + } } |