aboutsummaryrefslogtreecommitdiffstats
path: root/src
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
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')
-rw-r--r--src/contrib/src/org/apache/poi/contrib/metrics/FontMetricsDumper.java65
-rw-r--r--src/documentation/content/xdocs/hssf/quick-guide.xml209
-rw-r--r--src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawing.java255
-rw-r--r--src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawingWithGraphics.java87
-rw-r--r--src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java114
-rw-r--r--src/java/org/apache/poi/ddf/EscherArrayProperty.java162
-rw-r--r--src/java/org/apache/poi/ddf/EscherBSERecord.java383
-rw-r--r--src/java/org/apache/poi/ddf/EscherBlipRecord.java417
-rw-r--r--src/java/org/apache/poi/ddf/EscherBoolProperty.java50
-rw-r--r--src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java176
-rw-r--r--src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java317
-rw-r--r--src/java/org/apache/poi/ddf/EscherClientDataRecord.java130
-rw-r--r--src/java/org/apache/poi/ddf/EscherComplexProperty.java152
-rw-r--r--src/java/org/apache/poi/ddf/EscherContainerRecord.java168
-rw-r--r--src/java/org/apache/poi/ddf/EscherDgRecord.java163
-rw-r--r--src/java/org/apache/poi/ddf/EscherDggRecord.java241
-rw-r--r--src/java/org/apache/poi/ddf/EscherDump.java990
-rw-r--r--src/java/org/apache/poi/ddf/EscherOptRecord.java174
-rw-r--r--src/java/org/apache/poi/ddf/EscherProperties.java607
-rw-r--r--src/java/org/apache/poi/ddf/EscherProperty.java78
-rw-r--r--src/java/org/apache/poi/ddf/EscherPropertyFactory.java91
-rw-r--r--src/java/org/apache/poi/ddf/EscherPropertyMetaData.java51
-rw-r--r--src/java/org/apache/poi/ddf/EscherRGBProperty.java37
-rw-r--r--src/java/org/apache/poi/ddf/EscherRecord.java273
-rw-r--r--src/java/org/apache/poi/ddf/EscherRecordFactory.java16
-rw-r--r--src/java/org/apache/poi/ddf/EscherSerializationListener.java27
-rw-r--r--src/java/org/apache/poi/ddf/EscherShapePathProperty.java25
-rw-r--r--src/java/org/apache/poi/ddf/EscherSimpleProperty.java103
-rw-r--r--src/java/org/apache/poi/ddf/EscherSpRecord.java201
-rw-r--r--src/java/org/apache/poi/ddf/EscherSpgrRecord.java192
-rw-r--r--src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java171
-rw-r--r--src/java/org/apache/poi/ddf/EscherTextboxRecord.java215
-rw-r--r--src/java/org/apache/poi/ddf/NullEscherSerializationListener.java20
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherBSERecord.java87
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherBlipRecord.java110
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherBoolProperty.java13
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherChildAnchorRecord.java73
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherClientAnchorRecord.java91
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherClientDataRecord.java57
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherContainerRecord.java98
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherDgRecord.java62
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherDggRecord.java89
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherOptRecord.java140
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherPropertyFactory.java42
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherSpRecord.java62
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherSpgrRecord.java73
-rw-r--r--src/java/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java73
-rw-r--r--src/java/org/apache/poi/ddf/TestUnknownEscherRecord.java106
-rw-r--r--src/java/org/apache/poi/ddf/UnknownEscherRecord.java234
-rw-r--r--src/java/org/apache/poi/ddf/package.html11
-rw-r--r--src/java/org/apache/poi/dev/RecordGenerator.java9
-rw-r--r--src/java/org/apache/poi/hssf/dev/BiffViewer.java163
-rw-r--r--src/java/org/apache/poi/hssf/model/AbstractShape.java126
-rw-r--r--src/java/org/apache/poi/hssf/model/ConvertAnchor.java50
-rw-r--r--src/java/org/apache/poi/hssf/model/DrawingManager.java136
-rw-r--r--src/java/org/apache/poi/hssf/model/LineShape.java105
-rw-r--r--src/java/org/apache/poi/hssf/model/PolygonShape.java143
-rw-r--r--src/java/org/apache/poi/hssf/model/Sheet.java232
-rw-r--r--src/java/org/apache/poi/hssf/model/SimpleFilledShape.java111
-rw-r--r--src/java/org/apache/poi/hssf/model/TestDrawingManager.java81
-rw-r--r--src/java/org/apache/poi/hssf/model/TextboxShape.java151
-rw-r--r--src/java/org/apache/poi/hssf/model/Workbook.java52
-rw-r--r--src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java266
-rw-r--r--src/java/org/apache/poi/hssf/record/CommonObjectDataSubRecord.java498
-rw-r--r--src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java30
-rw-r--r--src/java/org/apache/poi/hssf/record/DrawingRecord.java97
-rw-r--r--src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java35
-rw-r--r--src/java/org/apache/poi/hssf/record/DrawingSelectionRecord.java30
-rw-r--r--src/java/org/apache/poi/hssf/record/EndSubRecord.java176
-rw-r--r--src/java/org/apache/poi/hssf/record/EscherAggregate.java696
-rw-r--r--src/java/org/apache/poi/hssf/record/GroupMarkerSubRecord.java176
-rw-r--r--src/java/org/apache/poi/hssf/record/HorizontalPageBreakRecord.java107
-rw-r--r--src/java/org/apache/poi/hssf/record/ObjRecord.java227
-rw-r--r--src/java/org/apache/poi/hssf/record/PageBreakRecord.java304
-rw-r--r--src/java/org/apache/poi/hssf/record/RecordFactory.java10
-rw-r--r--src/java/org/apache/poi/hssf/record/SubRecord.java98
-rw-r--r--src/java/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java127
-rw-r--r--src/java/org/apache/poi/hssf/record/TestDrawingGroupRecord.java36
-rw-r--r--src/java/org/apache/poi/hssf/record/TestEndSubRecord.java102
-rw-r--r--src/java/org/apache/poi/hssf/record/TestEscherAggregate.java124
-rw-r--r--src/java/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java137
-rw-r--r--src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java519
-rw-r--r--src/java/org/apache/poi/hssf/record/TextObjectRecord.java216
-rw-r--r--src/java/org/apache/poi/hssf/record/UnknownRecord.java7
-rw-r--r--src/java/org/apache/poi/hssf/record/VerticalPageBreakRecord.java107
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java773
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java450
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java567
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/FontDetails.java143
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java40
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java37
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java207
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFFont.java8
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java55
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java159
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java66
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java179
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFShape.java195
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java17
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java148
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java161
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java64
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java107
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java34
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java81
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics.java71
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics2d.java74
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestFontDetails.java45
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestHSSFClientAnchor.java44
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/TestHSSFRichTextString.java50
-rw-r--r--src/java/org/apache/poi/util/DrawingDump.java24
-rw-r--r--src/records/definitions/common_object_data_record.xml52
-rw-r--r--src/records/definitions/ftend_record.xml9
-rw-r--r--src/records/definitions/subrecord.xsl326
-rw-r--r--src/records/definitions/subrecord_test.xsl155
-rw-r--r--src/records/definitions/textobject_record.xml41
-rw-r--r--src/resources/fontmetrics/font_metrics.properties912
-rw-r--r--src/testcases/org/apache/poi/hssf/model/SheetTest.java132
-rw-r--r--src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java21
-rw-r--r--src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPalette.java16
-rw-r--r--src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java48
-rwxr-xr-xsrc/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java17
122 files changed, 18684 insertions, 109 deletions
diff --git a/src/contrib/src/org/apache/poi/contrib/metrics/FontMetricsDumper.java b/src/contrib/src/org/apache/poi/contrib/metrics/FontMetricsDumper.java
new file mode 100644
index 0000000000..2a02ae9fbf
--- /dev/null
+++ b/src/contrib/src/org/apache/poi/contrib/metrics/FontMetricsDumper.java
@@ -0,0 +1,65 @@
+package org.apache.poi.contrib.metrics;
+
+import java.awt.*;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+public class FontMetricsDumper
+{
+ public static void main( String[] args ) throws IOException
+ {
+
+ Properties props = new Properties();
+
+ Font[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
+ for ( int i = 0; i < allFonts.length; i++ )
+ {
+ String fontName = allFonts[i].getFontName();
+
+ Font font = new Font(fontName, Font.BOLD, 10);
+ FontMetrics fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
+ int fontHeight = fontMetrics.getHeight();
+
+ props.setProperty("font." + fontName + ".height", fontHeight+"");
+ StringBuffer characters = new StringBuffer();
+ for (char c = 'a'; c <= 'z'; c++)
+ {
+ characters.append( c + ", " );
+ }
+ for (char c = 'A'; c <= 'Z'; c++)
+ {
+ characters.append( c + ", " );
+ }
+ for (char c = '0'; c <= '9'; c++)
+ {
+ characters.append( c + ", " );
+ }
+ StringBuffer widths = new StringBuffer();
+ for (char c = 'a'; c <= 'z'; c++)
+ {
+ widths.append( fontMetrics.getWidths()[c] + ", " );
+ }
+ for (char c = 'A'; c <= 'Z'; c++)
+ {
+ widths.append( fontMetrics.getWidths()[c] + ", " );
+ }
+ for (char c = '0'; c <= '9'; c++)
+ {
+ widths.append( fontMetrics.getWidths()[c] + ", " );
+ }
+ props.setProperty("font." + fontName + ".characters", characters.toString());
+ props.setProperty("font." + fontName + ".widths", widths.toString());
+ }
+
+ FileOutputStream fileOut = new FileOutputStream("font_metrics.properties");
+ try
+ {
+ props.store(fileOut, "Font Metrics");
+ }
+ finally
+ {
+ fileOut.close();
+ }
+ }
+}
diff --git a/src/documentation/content/xdocs/hssf/quick-guide.xml b/src/documentation/content/xdocs/hssf/quick-guide.xml
index 8940bc5fe1..0ef0ccb629 100644
--- a/src/documentation/content/xdocs/hssf/quick-guide.xml
+++ b/src/documentation/content/xdocs/hssf/quick-guide.xml
@@ -40,6 +40,9 @@
<li><link href="#Splits">Create split and freeze panes.</link></li>
<li><link href="#Repeating">Repeating rows and columns.</link></li>
<li><link href="#HeaderFooter">Headers and Footers.</link></li>
+ <li><link href="#DrawingShapes">Drawing Shapes.</link></li>
+ <li><link href="#StylingShapes">Styling Shapes.</link></li>
+ <li><link href="#Graphics2d">Shapes and Graphics2d.</link></li>
</ul>
</section>
<section><title>Features</title>
@@ -692,6 +695,212 @@
fileOut.close();
</source>
</section>
+
+ <anchor id="DrawingShapes"/>
+ <section><title>Drawing Shapes</title>
+ <p>
+ POI supports drawing shapes using the Microsoft Office
+ drawing tools. Shapes on a sheet are organized in a
+ hiearchy of groups and and shapes. The top-most shape
+ is the patriarch. This is not visisble on the sheet
+ at all. To start drawing you need to call <code>createPatriarch</code>
+ on the <code>HSSFSheet</code> class. This has the
+ effect erasing any other shape information stored
+ in that sheet. By default POI will leave shape
+ records alone in the sheet unless you make a call to
+ this method.
+ </p>
+ <p>
+ To create a shape you have to go through the following
+ steps:
+ </p>
+ <ol>
+ <li>Create the patriarch.</li>
+ <li>Create an anchor to position the shape on the sheet.</li>
+ <li>Ask the patriarch to create the shape.</li>
+ <li>Set the shape type (line, oval, rectangle etc...)</li>
+ <li>Set any other style details converning the shape. (eg:
+ line thickness, etc...)</li>
+ </ol>
+ <source>
+ HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
+ a = new HSSFClientAnchor( 0, 0, 1023, 255, (short) 1, 0, (short) 1, 0 );
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ </source>
+ <p>
+ Text boxes are created using a different call:
+ </p>
+ <source>
+ HSSFTextbox textbox1 = patriarch.createTextbox(
+ new HSSFClientAnchor(0,0,0,0,(short)1,1,(short)2,2));
+ textbox1.setString(new HSSFRichTextString("This is a test") );
+ </source>
+ <p>
+ It's possible to use different fonts to style parts of
+ the text in the textbox. Here's how:
+ </p>
+ <source>
+ HSSFFont font = wb.createFont();
+ font.setItalic(true);
+ font.setUnderline(HSSFFont.U_DOUBLE);
+ HSSFRichTextString string = new HSSFRichTextString("Woo!!!");
+ string.applyFont(2,5,font);
+ textbox.setString(string );
+ </source>
+ <p>
+ Just as can be done manually using Excel, it is possible
+ to group shapes together. This is done by calling
+ <code>createGroup()</code> and then creating the shapes
+ using those groups.
+ </p>
+ <p>
+ It's also possible to create groups within groups.
+ </p>
+ <warning>Any group you create should contain at least two
+ other shapes or subgroups.</warning>
+ <p>
+ Here's how to create a shape group:
+ </p>
+ <source>
+ // Create a shape group.
+ HSSFShapeGroup group = patriarch.createGroup(
+ new HSSFClientAnchor(0,0,900,200,(short)2,2,(short)2,2));
+
+ // Create a couple of lines in the group.
+ HSSFSimpleShape shape1 = group.createShape(new HSSFChildAnchor(3,3,500,500));
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ ( (HSSFChildAnchor) shape1.getAnchor() ).setAnchor((short)3,3,500,500);
+ HSSFSimpleShape shape2 = group.createShape(new HSSFChildAnchor((short)1,200,400,600));
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ </source>
+ <p>
+ If you're being observant you'll noticed that the shapes
+ that are added to the group use a new type of anchor:
+ the <code>HSSFChildAnchor</code>. What happens is that
+ the created group has it's own coordinate space for
+ shapes that are placed into it. POI defaults this to
+ (0,0,1023,255) but you are able to change it as desired.
+ Here's how:
+ </p>
+ <source>
+ myGroup.setCoordinates(10,10,20,20); // top-left, bottom-right
+ </source>
+ <p>
+ If you create a group within a group it's also going
+ to have it's own coordinate space.
+ </p>
+ </section>
+
+ <anchor id="StylingShapes"/>
+ <section><title>Styling Shapes</title>
+ <p>
+ By default shapes can look a little plain. It's possible
+ to apply different styles to the shapes however. The
+ sorts of things that can currently be done are:
+ </p>
+ <ul>
+ <li>Change the fill color.</li>
+ <li>Make a shape with no fill color.</li>
+ <li>Change the thickness of the lines.</li>
+ <li>Change the style of the lines. Eg: dashed, dotted.</li>
+ <li>Change the line color.</li>
+ </ul>
+ <p>
+ Here's an examples of how this is done:
+ </p>
+ <source>
+ HSSFSimpleShape s = patriarch.createSimpleShape(a);
+ s.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
+ s.setLineStyleColor(10,10,10);
+ s.setFillColor(90,10,200);
+ s.setLineWidth(HSSFShape.LINEWIDTH_ONE_PT * 3);
+ s.setLineStyle(HSSFShape.LINESTYLE_DOTSYS);
+ </source>
+ </section>
+ <anchor id="Graphics2d"/>
+ <section><title>Shapes and Graphics2d</title>
+ <p>
+ While the native POI shape drawing commands are the
+ recommended way to draw shapes in a shape it's sometimes
+ desirable to use a standard API for compatibility with
+ external libraries. With this in mind we created some
+ wrappers for <code>Graphics</code> and <code>Graphics2d</code>.
+ </p>
+ <warning>
+ It's important to not however before continuing that
+ <code>Graphics2d</code> is a poor match to the capabilities
+ of the Microsoft Office drawing commands. The older
+ <code>Graphics</code> class offers a closer match but is
+ still a square peg in a round hole.
+ </warning>
+ <p>
+ All Graphics commands are issued into an <code>HSSFShapeGroup</code>.
+ Here's how it's done:
+ </p>
+ <source>
+ a = new HSSFClientAnchor( 0, 0, 1023, 255, (short) 1, 0, (short) 1, 0 );
+ group = patriarch.createGroup( a );
+ group.setCoordinates( 0, 0, 80 * 4 , 12 * 23 );
+ float verticalPointsPerPixel = a.getAnchorHeightInPoints(sheet) / (float)Math.abs(group.getY2() - group.getY1());
+ g = new EscherGraphics( group, wb, Color.black, verticalPointsPerPixel );
+ g2d = new EscherGraphics2d( g );
+ drawChemicalStructure( g2d );
+ </source>
+ <p>
+ The first thing we do is create the group and set it's coordinates
+ to match what we plan to draw. Next we calculate a reasonable
+ fontSizeMultipler then create the EscherGraphics object.
+ Since what we really want is a <code>Graphics2d</code>
+ object we create an EscherGraphics2d object and pass in
+ the graphics object we created. Finally we call a routine
+ that draws into the EscherGraphics2d object.
+ </p>
+ <p>
+ The vertical points per pixel deserves some more explanation.
+ One of the difficulties in converting Graphics calls
+ into escher drawing calls is that Excel does not have
+ the concept of absolute pixel positions. It measures
+ it's cell widths in 'characters' and the cell heights in points.
+ Unfortunately it's not defined exactly what type of character it's
+ measuring. Presumably this is due to the fact that the Excel will be
+ using different fonts on different platforms or even within the same
+ platform.
+ </p>
+ <p>
+ Because of this constraint we've had to implement the concept of a
+ verticalPointsPerPixel. This the amount the font should be scaled by when
+ you issue commands such as drawString(). To calculate this value
+ use the follow formula:
+ </p>
+ <source>
+ multipler = groupHeightInPoints / heightOfGroup
+ </source>
+ <p>
+ The height of the group is calculated fairly simply by calculating the
+ difference between the y coordinates of the bounding box of the shape. The
+ height of the group can be calculated by using a convenience called
+ <code>HSSFClientAnchor.getAnchorHeightInPoints()</code>.
+ </p>
+ <p>
+ Many of the functions supported by the graphics classes
+ are not complete. Here's some of the functions that are known
+ to work.
+ </p>
+ <ul>
+ <li>fillRect()</li>
+ <li>fillOval()</li>
+ <li>drawString()</li>
+ <li>drawOval()</li>
+ <li>drawLine()</li>
+ <li>clearRect()</li>
+ </ul>
+ <p>
+ Functions that are not supported will return and log a message
+ using the POI logging infrastructure (disabled by default).
+ </p>
+ </section>
+
</section>
</section>
</body>
diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawing.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawing.java
new file mode 100644
index 0000000000..255fed7cb2
--- /dev/null
+++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawing.java
@@ -0,0 +1,255 @@
+package org.apache.poi.hssf.usermodel.examples;
+
+import org.apache.poi.hssf.usermodel.*;
+
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+
+/**
+ * Demonstrates how to use the office drawing capabilities of POI.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class OfficeDrawing
+{
+ public static void main(String[] args)
+ throws IOException
+ {
+ // Create the workbook and sheets.
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet1 = wb.createSheet("new sheet");
+ HSSFSheet sheet2 = wb.createSheet("second sheet");
+ HSSFSheet sheet3 = wb.createSheet("third sheet");
+ HSSFSheet sheet4 = wb.createSheet("fourth sheet");
+
+ // Draw stuff in them
+ drawSheet1( sheet1 );
+ drawSheet2( sheet2 );
+ drawSheet3( sheet3 );
+ drawSheet4( sheet4, wb );
+
+ // Write the file out.
+ FileOutputStream fileOut = new FileOutputStream("workbook.xls");
+ wb.write(fileOut);
+ fileOut.close();
+ }
+
+ private static void drawSheet1( HSSFSheet sheet1 )
+ {
+ // Create a row and size one of the cells reasonably large.
+ HSSFRow row = sheet1.createRow(2);
+ row.setHeight((short) 2800);
+ sheet1.setColumnWidth((short) 2, (short) 9000);
+
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes.
+ HSSFPatriarch patriarch = sheet1.createDrawingPatriarch();
+
+ // Draw some lines and an oval.
+ drawLinesToCenter( patriarch );
+ drawManyLines( patriarch );
+ drawOval( patriarch );
+ drawPolygon( patriarch );
+
+ // Draw a rectangle.
+ HSSFSimpleShape rect = patriarch.createSimpleShape( new HSSFClientAnchor(100, 100, 900, 200, (short)0, 0, (short)0, 0) );
+ rect.setShapeType(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE);
+ }
+
+ private static void drawSheet2( HSSFSheet sheet2 )
+ {
+ // Create a row and size one of the cells reasonably large.
+ HSSFRow row = sheet2.createRow(2);
+ row.setHeightInPoints(240);
+ sheet2.setColumnWidth((short) 2, (short) 9000);
+
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes. This will clear out any existing shapes for that sheet.
+ HSSFPatriarch patriarch = sheet2.createDrawingPatriarch();
+
+ // Draw a grid in one of the cells.
+ drawGrid( patriarch );
+ }
+
+ private static void drawSheet3( HSSFSheet sheet3 )
+ {
+ // Create a row and size one of the cells reasonably large
+ HSSFRow row = sheet3.createRow(2);
+ row.setHeightInPoints(140);
+ sheet3.setColumnWidth((short) 2, (short) 9000);
+
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes. This will clear out any existing shapes for that sheet.
+ HSSFPatriarch patriarch = sheet3.createDrawingPatriarch();
+
+ // Create a shape group.
+ HSSFShapeGroup group = patriarch.createGroup(
+ new HSSFClientAnchor(0,0,900,200,(short)2,2,(short)2,2));
+
+ // Create a couple of lines in the group.
+ HSSFSimpleShape shape1 = group.createShape(new HSSFChildAnchor(3,3,500,500));
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ ( (HSSFChildAnchor) shape1.getAnchor() ).setAnchor((short)3,3,500,500);
+ HSSFSimpleShape shape2 = group.createShape(new HSSFChildAnchor((short)1,200,400,600));
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+
+ }
+
+ private static void drawSheet4( HSSFSheet sheet4, HSSFWorkbook wb )
+ {
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes. This will clear out any existing shapes for that sheet.
+ HSSFPatriarch patriarch = sheet4.createDrawingPatriarch();
+
+ // Create a couple of textboxes
+ HSSFTextbox textbox1 = patriarch.createTextbox(
+ new HSSFClientAnchor(0,0,0,0,(short)1,1,(short)2,2));
+ textbox1.setString(new HSSFRichTextString("This is a test") );
+ HSSFTextbox textbox2 = patriarch.createTextbox(
+ new HSSFClientAnchor(0,0,900,100,(short)3,3,(short)3,4));
+ textbox2.setString(new HSSFRichTextString("Woo") );
+ textbox2.setFillColor(200,0,0);
+ textbox2.setLineStyle(HSSFSimpleShape.LINESTYLE_DOTGEL);
+
+ // Create third one with some fancy font styling.
+ HSSFTextbox textbox3 = patriarch.createTextbox(
+ new HSSFClientAnchor(0,0,900,100,(short)4,4,(short)5,4+1));
+ HSSFFont font = wb.createFont();
+ font.setItalic(true);
+ font.setUnderline(HSSFFont.U_DOUBLE);
+ HSSFRichTextString string = new HSSFRichTextString("Woo!!!");
+ string.applyFont(2,5,font);
+ textbox3.setString(string );
+ textbox3.setFillColor(0x08000030);
+ textbox3.setLineStyle(HSSFSimpleShape.LINESTYLE_NONE); // no line around the textbox.
+ textbox3.setNoFill(true); // make it transparent
+ }
+
+ private static void drawOval( HSSFPatriarch patriarch )
+ {
+ // Create an oval and style to taste.
+ HSSFClientAnchor a = new HSSFClientAnchor();
+ a.setAnchor((short)2, 2, 20, 20, (short) 2, 2, 190, 80);
+ HSSFSimpleShape s = patriarch.createSimpleShape(a);
+ s.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
+ s.setLineStyleColor(10,10,10);
+ s.setFillColor(90,10,200);
+ s.setLineWidth(HSSFShape.LINEWIDTH_ONE_PT * 3);
+ s.setLineStyle(HSSFShape.LINESTYLE_DOTSYS);
+ }
+
+ private static void drawPolygon( HSSFPatriarch patriarch )
+ {
+ // HSSFClientAnchor a = new HSSFClientAnchor( 0, 0, 1023, 255, (short) 2, 2, (short) 3, 3 );
+ // HSSFPolygon p = patriarch.createPolygon(a);
+ // p.setPolygonDrawArea(100,100);
+ // p.setPoints( new int[]{30, 90, 50}, new int[]{88, 5, 44} );
+
+
+ HSSFClientAnchor a = new HSSFClientAnchor();
+ a.setAnchor( (short) 2, 2, 0, 0, (short) 3, 3, 1023, 255 );
+ HSSFShapeGroup g = patriarch.createGroup( a );
+ g.setCoordinates(0,0,200,200);
+ HSSFPolygon p1 = g.createPolygon( new HSSFChildAnchor( 0, 0, 200, 200 ) );
+ p1.setPolygonDrawArea( 100, 100 );
+ p1.setPoints( new int[]{0, 90, 50}, new int[]{5, 5, 44} );
+ p1.setFillColor( 0, 255, 0 );
+ HSSFPolygon p2 = g.createPolygon( new HSSFChildAnchor( 20, 20, 200, 200 ) );
+ p2.setPolygonDrawArea( 200, 200 );
+ p2.setPoints( new int[]{120, 20, 150}, new int[]{105, 30, 195} );
+ p2.setFillColor( 255, 0, 0 );
+ }
+
+ private static void drawManyLines( HSSFPatriarch patriarch )
+ {
+ // Draw bunch of lines
+ int x1 = 100;
+ int y1 = 100;
+ int x2 = 800;
+ int y2 = 200;
+ int color = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ HSSFClientAnchor a2 = new HSSFClientAnchor();
+ a2.setAnchor((short) 2, 2, x1, y1, (short) 2, 2, x2, y2);
+ HSSFSimpleShape shape2 = patriarch.createSimpleShape(a2);
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ shape2.setLineStyleColor(color);
+ y1 -= 10;
+ y2 -= 10;
+ color += 30;
+ }
+ }
+
+ private static void drawGrid( HSSFPatriarch patriarch )
+ {
+ // This draws a grid of lines. Since the coordinates space fixed at
+ // 1024 by 256 we use a ratio to get a reasonably square grids.
+
+ double xRatio = 3.22;
+ double yRatio = 0.6711;
+
+ int x1 = 000;
+ int y1 = 000;
+ int x2 = 000;
+ int y2 = 200;
+ for (int i = 0; i < 20; i++)
+ {
+ HSSFClientAnchor a2 = new HSSFClientAnchor();
+ a2.setAnchor((short) 2, 2, (int) ( x1 * xRatio ), (int) ( y1 * yRatio ),
+ (short) 2, 2, (int) ( x2 * xRatio ), (int) ( y2 * yRatio ));
+ HSSFSimpleShape shape2 = patriarch.createSimpleShape(a2);
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+
+ x1 += 10;
+ x2 += 10;
+ }
+
+ x1 = 000;
+ y1 = 000;
+ x2 = 200;
+ y2 = 000;
+ for (int i = 0; i < 20; i++)
+ {
+ HSSFClientAnchor a2 = new HSSFClientAnchor();
+ a2.setAnchor((short) 2, 2, (int) ( x1 * xRatio ), (int) ( y1 * yRatio ),
+ (short) 2, 2, (int) ( x2 * xRatio ), (int) ( y2 * yRatio ));
+ HSSFSimpleShape shape2 = patriarch.createSimpleShape(a2);
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+
+ y1 += 10;
+ y2 += 10;
+ }
+ }
+
+ private static void drawLinesToCenter( HSSFPatriarch patriarch )
+ {
+ // Draw some lines from and to the corners
+ {
+ HSSFClientAnchor a1 = new HSSFClientAnchor();
+ a1.setAnchor( (short)2, 2, 0, 0, (short) 2, 2, 512, 128);
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ }
+ {
+ HSSFClientAnchor a1 = new HSSFClientAnchor();
+ a1.setAnchor( (short)2, 2, 512, 128, (short) 2, 2, 1024, 0);
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ }
+ {
+ HSSFClientAnchor a1 = new HSSFClientAnchor();
+ a1.setAnchor( (short)1, 1, 0, 0, (short) 1, 1, 512, 100);
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ }
+ {
+ HSSFClientAnchor a1 = new HSSFClientAnchor();
+ a1.setAnchor( (short)1, 1, 512, 100, (short) 1, 1, 1024, 0);
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ }
+
+ }
+}
diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawingWithGraphics.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawingWithGraphics.java
new file mode 100644
index 0000000000..0c54b0f19e
--- /dev/null
+++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/OfficeDrawingWithGraphics.java
@@ -0,0 +1,87 @@
+package org.apache.poi.hssf.usermodel.examples;
+
+import org.apache.poi.hssf.usermodel.*;
+
+import java.awt.*;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Demonstrates the use of the EscherGraphics2d library.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class OfficeDrawingWithGraphics
+{
+ public static void main( String[] args ) throws IOException
+ {
+ // Create a workbook with one sheet and size the first three somewhat
+ // larger so we can fit the chemical structure diagram in.
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet( "my drawing" );
+ sheet.setColumnWidth((short)1, (short)(256 * 27));
+ HSSFRow row1 = sheet.createRow(0);
+ row1.setHeightInPoints(10 * 15);
+ HSSFRow row2 = sheet.createRow(1);
+ row2.setHeightInPoints(5 * 15);
+ HSSFRow row3 = sheet.createRow(2);
+ row3.setHeightInPoints(10 * 15);
+
+ // Add some cells so we can test that the anchoring works when we
+ // sort them.
+ row1.createCell((short)0).setCellValue("C");
+ row2.createCell((short)0).setCellValue("A");
+ row3.createCell((short)0).setCellValue("B");
+
+ // Create the top level drawing patriarch.
+ HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
+
+ HSSFClientAnchor a;
+ HSSFShapeGroup group;
+ EscherGraphics g;
+ EscherGraphics2d g2d;
+ // Anchor entirely within one cell.
+ a = new HSSFClientAnchor( 0, 0, 1023, 255, (short) 1, 0, (short) 1, 0 );
+ group = patriarch.createGroup( a );
+ group.setCoordinates( 0, 0, 320, 276 );
+ float verticalPointsPerPixel = a.getAnchorHeightInPoints(sheet) / (float)Math.abs(group.getY2() - group.getY1());
+ g = new EscherGraphics( group, wb, Color.black, verticalPointsPerPixel );
+ g2d = new EscherGraphics2d( g );
+ drawStar( g2d );
+
+ a = new HSSFClientAnchor( 0, 0, 1023, 255, (short) 1, 1, (short) 1, 1 );
+ group = patriarch.createGroup( a );
+ group.setCoordinates( 0, 0, 640, 276 );
+ verticalPointsPerPixel = a.getAnchorHeightInPoints(sheet) / (float)Math.abs(group.getY2() - group.getY1());
+// verticalPixelsPerPoint = (float)Math.abs(group.getY2() - group.getY1()) / a.getAnchorHeightInPoints(sheet);
+ g = new EscherGraphics( group, wb, Color.black, verticalPointsPerPixel );
+ g2d = new EscherGraphics2d( g );
+ drawStar( g2d );
+
+ FileOutputStream out = new FileOutputStream("workbook.xls");
+ wb.write(out);
+ out.close();
+
+ }
+
+ private static void drawStar( EscherGraphics2d g2d )
+ {
+ g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
+ for (double i = 0; i < Math.PI; i += 0.1)
+ {
+ g2d.setColor( new Color((int)(i * 5343062d) ) );
+ int x1 = (int) ( Math.cos(i) * 160.0 ) + 160;
+ int y1 = (int) ( Math.sin(i) * 138.0 ) + 138;
+ int x2 = (int) ( -Math.cos(i) * 160.0 ) + 160;
+ int y2 = (int) ( -Math.sin(i) * 138.0 ) + 138;
+ g2d.drawLine(x1,y1,x2,y2);
+ }
+ g2d.setFont(new Font("SansSerif",Font.BOLD | Font.ITALIC, 20));
+ g2d.drawString("EscherGraphics2d",70,100);
+ g2d.setColor(Color.yellow);
+ g2d.fillOval( 160-20,138-20,40,40);
+ g2d.setColor(Color.black);
+ g2d.fillPolygon(new int[] {-10+160,0+160,10+160,0+160}, new int[] {0+138,10+138,0+138,-10+138}, 4);
+ g2d.drawPolygon(new int[] {-160+160,0+160,160+160,0+160}, new int[] {0+138,138+138,0+138,-138+138}, 4);
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java b/src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java
new file mode 100644
index 0000000000..09d8c5f816
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/DefaultEscherRecordFactory.java
@@ -0,0 +1,114 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.hssf.record.RecordFormatException;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Generates escher records when provided the byte array containing those records.
+ *
+ * @author Glen Stampoultzis
+ * @see EscherRecordFactory
+ */
+public class DefaultEscherRecordFactory
+ implements EscherRecordFactory
+{
+ private static Class[] escherRecordClasses = {
+ EscherBSERecord.class, EscherOptRecord.class, EscherClientAnchorRecord.class, EscherDgRecord.class,
+ EscherSpgrRecord.class, EscherSpRecord.class, EscherClientDataRecord.class, EscherDggRecord.class,
+ EscherSplitMenuColorsRecord.class, EscherChildAnchorRecord.class, EscherTextboxRecord.class
+ };
+ private static Map recordsMap = recordsToMap( escherRecordClasses );
+
+ /**
+ * Creates an instance of the escher record factory
+ */
+ public DefaultEscherRecordFactory()
+ {
+ }
+
+ /**
+ * Generates an escher record including the any children contained under that record.
+ * An exception is thrown if the record could not be generated.
+ *
+ * @param data The byte array containing the records
+ * @param offset The starting offset into the byte array
+ * @return The generated escher record
+ */
+ public EscherRecord createRecord( byte[] data, int offset )
+ {
+ EscherRecord.EscherRecordHeader header = EscherRecord.EscherRecordHeader.readHeader( data, offset );
+ if ( ( header.getOptions() & (short) 0x000F ) == (short) 0x000F )
+ {
+ EscherContainerRecord r = new EscherContainerRecord();
+ r.setRecordId( header.getRecordId() );
+ r.setOptions( header.getOptions() );
+ return r;
+ }
+ else if ( header.getRecordId() >= EscherBlipRecord.RECORD_ID_START && header.getRecordId() <= EscherBlipRecord.RECORD_ID_END )
+ {
+ EscherBlipRecord r = new EscherBlipRecord();
+ r.setRecordId( header.getRecordId() );
+ r.setOptions( header.getOptions() );
+ return r;
+ }
+ else
+ {
+ Constructor recordConstructor = (Constructor) recordsMap.get( new Short( header.getRecordId() ) );
+ EscherRecord escherRecord = null;
+ if ( recordConstructor != null )
+ {
+ try
+ {
+ escherRecord = (EscherRecord) recordConstructor.newInstance( new Object[]{} );
+ escherRecord.setRecordId( header.getRecordId() );
+ escherRecord.setOptions( header.getOptions() );
+ }
+ catch ( Exception e )
+ {
+ escherRecord = null;
+ }
+ }
+ return escherRecord == null ? new UnknownEscherRecord() : escherRecord;
+ }
+ }
+
+ /**
+ * Converts from a list of classes into a map that contains the record id as the key and
+ * the Constructor in the value part of the map. It does this by using reflection to look up
+ * the RECORD_ID field then using reflection again to find a reference to the constructor.
+ *
+ * @param records The records to convert
+ * @return The map containing the id/constructor pairs.
+ */
+ private static Map recordsToMap( Class[] records )
+ {
+ Map result = new HashMap();
+ Constructor constructor;
+
+ for ( int i = 0; i < records.length; i++ )
+ {
+ Class record = null;
+ short sid = 0;
+
+ record = records[i];
+ try
+ {
+ sid = record.getField( "RECORD_ID" ).getShort( null );
+ constructor = record.getConstructor( new Class[]
+ {
+ } );
+ }
+ catch ( Exception illegalArgumentException )
+ {
+ throw new RecordFormatException(
+ "Unable to determine record types" );
+ }
+ result.put( new Short( sid ), constructor );
+ }
+ return result;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherArrayProperty.java b/src/java/org/apache/poi/ddf/EscherArrayProperty.java
new file mode 100644
index 0000000000..8979b30a29
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherArrayProperty.java
@@ -0,0 +1,162 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.HexDump;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Escher array properties are the most wierd construction ever invented
+ * with all sorts of special cases. I'm hopeful I've got them all.
+ *
+ * @author Glen Stampoultzis (glens at superlinksoftware.com)
+ */
+public class EscherArrayProperty
+ extends EscherComplexProperty
+{
+ private static final int FIXED_SIZE = 3 * 2;
+
+ public EscherArrayProperty( short id, byte[] complexData )
+ {
+ super( id, checkComplexData(complexData) );
+ }
+
+ public EscherArrayProperty( short propertyNumber, boolean isBlipId, byte[] complexData )
+ {
+ super( propertyNumber, isBlipId, checkComplexData(complexData) );
+ }
+
+ private static byte[] checkComplexData( byte[] complexData )
+ {
+ if (complexData == null || complexData.length == 0)
+ complexData = new byte[6];
+
+ return complexData;
+ }
+
+ public int getNumberOfElementsInArray()
+ {
+ return LittleEndian.getUShort( complexData, 0 );
+ }
+
+ public void setNumberOfElementsInArray( int numberOfElements )
+ {
+ int expectedArraySize = numberOfElements * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
+ if ( expectedArraySize != complexData.length )
+ {
+ byte[] newArray = new byte[expectedArraySize];
+ System.arraycopy( complexData, 0, newArray, 0, complexData.length );
+ complexData = newArray;
+ }
+ LittleEndian.putShort( complexData, 0, (short) numberOfElements );
+ }
+
+ public int getNumberOfElementsInMemory()
+ {
+ return LittleEndian.getUShort( complexData, 2 );
+ }
+
+ public void setNumberOfElementsInMemory( int numberOfElements )
+ {
+ int expectedArraySize = numberOfElements * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
+ if ( expectedArraySize != complexData.length )
+ {
+ byte[] newArray = new byte[expectedArraySize];
+ System.arraycopy( complexData, 0, newArray, 0, expectedArraySize );
+ complexData = newArray;
+ }
+ LittleEndian.putShort( complexData, 2, (short) numberOfElements );
+ }
+
+ public short getSizeOfElements()
+ {
+ return LittleEndian.getShort( complexData, 4 );
+ }
+
+ public void setSizeOfElements( int sizeOfElements )
+ {
+ LittleEndian.putShort( complexData, 4, (short) sizeOfElements );
+
+ int expectedArraySize = getNumberOfElementsInArray() * getActualSizeOfElements(getSizeOfElements()) + FIXED_SIZE;
+ if ( expectedArraySize != complexData.length )
+ {
+ // Keep just the first 6 bytes. The rest is no good to us anyway.
+ byte[] newArray = new byte[expectedArraySize];
+ System.arraycopy( complexData, 0, newArray, 0, 6 );
+ complexData = newArray;
+ }
+ }
+
+ public byte[] getElement( int index )
+ {
+ int actualSize = getActualSizeOfElements(getSizeOfElements());
+ byte[] result = new byte[actualSize];
+ System.arraycopy(complexData, FIXED_SIZE + index * actualSize, result, 0, result.length );
+ return result;
+ }
+
+ public void setElement( int index, byte[] element )
+ {
+ int actualSize = getActualSizeOfElements(getSizeOfElements());
+ System.arraycopy( element, 0, complexData, FIXED_SIZE + index * actualSize, actualSize);
+ }
+
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+ StringBuffer results = new StringBuffer();
+ results.append(" {EscherArrayProperty:" + nl);
+ results.append(" Num Elements: " + getNumberOfElementsInArray() + nl);
+ results.append(" Num Elements In Memory: " + getNumberOfElementsInMemory() + nl);
+ results.append(" Size of elements: " + getSizeOfElements() + nl);
+ for (int i = 0; i < getNumberOfElementsInArray(); i++)
+ {
+ results.append(" Element " + i + ": " + HexDump.toHex(getElement(i)) + nl);
+ }
+ results.append("}" + nl);
+
+ return "propNum: " + getPropertyNumber()
+ + ", propName: " + EscherProperties.getPropertyName( getPropertyNumber() )
+ + ", complex: " + isComplex()
+ + ", blipId: " + isBlipId()
+ + ", data: " + nl + results.toString();
+ }
+
+ /**
+ * We have this method because the way in which arrays in escher works
+ * is screwed for seemly arbitary reasons. While most properties are
+ * fairly consistent and have a predictable array size, escher arrays
+ * have special cases.
+ *
+ * @param data The data array containing the escher array information
+ * @param offset The offset into the array to start reading from.
+ * @return the number of bytes used by this complex property.
+ */
+ public int setArrayData( byte[] data, int offset )
+ {
+ short numElements = LittleEndian.getShort(data, offset);
+ short numReserved = LittleEndian.getShort(data, offset + 2);
+ short sizeOfElements = LittleEndian.getShort(data, offset + 4);
+
+ int arraySize = getActualSizeOfElements(sizeOfElements) * numElements;
+ if (arraySize == complexData.length)
+ complexData = new byte[arraySize + 6]; // Calculation missing the header for some reason
+ System.arraycopy(data, offset, complexData, 0, complexData.length );
+ return complexData.length;
+ }
+
+ /**
+ * Sometimes the element size is stored as a negative number. We
+ * negate it and shift it to get the real value.
+ */
+ public static int getActualSizeOfElements(short sizeOfElements)
+ {
+ if (sizeOfElements < 0)
+ return (short) ( ( -sizeOfElements ) >> 2 );
+ else
+ return sizeOfElements;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherBSERecord.java b/src/java/org/apache/poi/ddf/EscherBSERecord.java
new file mode 100644
index 0000000000..2d170ab070
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherBSERecord.java
@@ -0,0 +1,383 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.HexDump;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
+/**
+ * The BSE record is related closely to the <code>EscherBlipRecord</code> and stores
+ * extra information about the blip.
+ *
+ * @author Glen Stampoultzis
+ * @see EscherBlipRecord
+ */
+public class EscherBSERecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF007;
+ public static final String RECORD_DESCRIPTION = "MsofbtBSE";
+
+ public static final byte BT_ERROR = 0;
+ public static final byte BT_UNKNOWN = 1;
+ public static final byte BT_EMF = 2;
+ public static final byte BT_WMF = 3;
+ public static final byte BT_PICT = 4;
+ public static final byte BT_JPEG = 5;
+ public static final byte BT_PNG = 6;
+ public static final byte BT_DIB = 7;
+
+ private byte field_1_blipTypeWin32;
+ private byte field_2_blipTypeMacOS;
+ private byte[] field_3_uid; // 16 bytes
+ private short field_4_tag;
+ private int field_5_size;
+ private int field_6_ref;
+ private int field_7_offset;
+ private byte field_8_usage;
+ private byte field_9_name;
+ private byte field_10_unused2;
+ private byte field_11_unused3;
+
+ private byte[] remainingData;
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset,
+ EscherRecordFactory recordFactory
+ )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ field_1_blipTypeWin32 = data[pos];
+ field_2_blipTypeMacOS = data[pos + 1];
+ System.arraycopy( data, pos + 2, field_3_uid = new byte[16], 0, 16 );
+ field_4_tag = LittleEndian.getShort( data, pos + 18 );
+ field_5_size = LittleEndian.getInt( data, pos + 20 );
+ field_6_ref = LittleEndian.getInt( data, pos + 24 );
+ field_7_offset = LittleEndian.getInt( data, pos + 28 );
+ field_8_usage = data[pos + 32];
+ field_9_name = data[pos + 33];
+ field_10_unused2 = data[pos + 34];
+ field_11_unused3 = data[pos + 35];
+ bytesRemaining -= 36;
+ remainingData = new byte[bytesRemaining];
+ System.arraycopy( data, pos + 36, remainingData, 0, bytesRemaining );
+ return bytesRemaining + 8 + 36;
+
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ int remainingBytes = remainingData.length + 36;
+ LittleEndian.putInt( data, offset + 4, remainingBytes );
+
+ data[offset + 8] = field_1_blipTypeWin32;
+ data[offset + 9] = field_2_blipTypeMacOS;
+ for ( int i = 0; i < 16; i++ )
+ data[offset + 10 + i] = field_3_uid[i];
+ LittleEndian.putShort( data, offset + 26, field_4_tag );
+ LittleEndian.putInt( data, offset + 28, field_5_size );
+ LittleEndian.putInt( data, offset + 32, field_6_ref );
+ LittleEndian.putInt( data, offset + 36, field_7_offset );
+ data[offset + 40] = field_8_usage;
+ data[offset + 41] = field_9_name;
+ data[offset + 42] = field_10_unused2;
+ data[offset + 43] = field_11_unused3;
+ System.arraycopy( remainingData, 0, data, offset + 44, remainingData.length );
+ int pos = offset + 8 + 36 + remainingData.length;
+
+ listener.afterRecordSerialize(pos, getRecordId(), pos - offset, this);
+ return pos - offset;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 1 + 1 + 16 + 2 + 4 + 4 + 4 + 1 + 1 + 1 + 1 + remainingData.length;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "BSE";
+ }
+
+ /**
+ * The expected blip type under windows (failure to match this blip type will result in
+ * Excel converting to this format).
+ */
+ public byte getBlipTypeWin32()
+ {
+ return field_1_blipTypeWin32;
+ }
+
+ /**
+ * Set the expected win32 blip type
+ */
+ public void setBlipTypeWin32( byte blipTypeWin32 )
+ {
+ this.field_1_blipTypeWin32 = blipTypeWin32;
+ }
+
+ /**
+ * The expected blip type under MacOS (failure to match this blip type will result in
+ * Excel converting to this format).
+ */
+ public byte getBlipTypeMacOS()
+ {
+ return field_2_blipTypeMacOS;
+ }
+
+ /**
+ * Set the expected MacOS blip type
+ */
+ public void setBlipTypeMacOS( byte blipTypeMacOS )
+ {
+ this.field_2_blipTypeMacOS = blipTypeMacOS;
+ }
+
+ /**
+ * 16 byte MD4 checksum.
+ */
+ public byte[] getUid()
+ {
+ return field_3_uid;
+ }
+
+ /**
+ * 16 byte MD4 checksum.
+ */
+ public void setUid( byte[] uid )
+ {
+ this.field_3_uid = uid;
+ }
+
+ /**
+ * unused
+ */
+ public short getTag()
+ {
+ return field_4_tag;
+ }
+
+ /**
+ * unused
+ */
+ public void setTag( short tag )
+ {
+ this.field_4_tag = tag;
+ }
+
+ /**
+ * Blip size in stream.
+ */
+ public int getSize()
+ {
+ return field_5_size;
+ }
+
+ /**
+ * Blip size in stream.
+ */
+ public void setSize( int size )
+ {
+ this.field_5_size = size;
+ }
+
+ /**
+ * The reference count of this blip.
+ */
+ public int getRef()
+ {
+ return field_6_ref;
+ }
+
+ /**
+ * The reference count of this blip.
+ */
+ public void setRef( int ref )
+ {
+ this.field_6_ref = ref;
+ }
+
+ /**
+ * File offset in the delay stream.
+ */
+ public int getOffset()
+ {
+ return field_7_offset;
+ }
+
+ /**
+ * File offset in the delay stream.
+ */
+ public void setOffset( int offset )
+ {
+ this.field_7_offset = offset;
+ }
+
+ /**
+ * Defines the way this blip is used.
+ */
+ public byte getUsage()
+ {
+ return field_8_usage;
+ }
+
+ /**
+ * Defines the way this blip is used.
+ */
+ public void setUsage( byte usage )
+ {
+ this.field_8_usage = usage;
+ }
+
+ /**
+ * The length in characters of the blip name.
+ */
+ public byte getName()
+ {
+ return field_9_name;
+ }
+
+ /**
+ * The length in characters of the blip name.
+ */
+ public void setName( byte name )
+ {
+ this.field_9_name = name;
+ }
+
+ public byte getUnused2()
+ {
+ return field_10_unused2;
+ }
+
+ public void setUnused2( byte unused2 )
+ {
+ this.field_10_unused2 = unused2;
+ }
+
+ public byte getUnused3()
+ {
+ return field_11_unused3;
+ }
+
+ public void setUnused3( byte unused3 )
+ {
+ this.field_11_unused3 = unused3;
+ }
+
+ /**
+ * Any remaining data in this record.
+ */
+ public byte[] getRemainingData()
+ {
+ return remainingData;
+ }
+
+ /**
+ * Any remaining data in this record.
+ */
+ public void setRemainingData( byte[] remainingData )
+ {
+ this.remainingData = remainingData;
+ }
+
+ /**
+ * Calculate the string representation of this object
+ */
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separator" );
+
+ String extraData;
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ try
+ {
+ HexDump.dump( this.remainingData, 0, b, 0 );
+ extraData = b.toString();
+ }
+ catch ( Exception e )
+ {
+ extraData = e.toString();
+ }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex( RECORD_ID ) + nl +
+ " Options: 0x" + HexDump.toHex( getOptions() ) + nl +
+ " BlipTypeWin32: " + field_1_blipTypeWin32 + nl +
+ " BlipTypeMacOS: " + field_2_blipTypeMacOS + nl +
+ " SUID: " + HexDump.toHex(field_3_uid) + nl +
+ " Tag: " + field_4_tag + nl +
+ " Size: " + field_5_size + nl +
+ " Ref: " + field_6_ref + nl +
+ " Offset: " + field_7_offset + nl +
+ " Usage: " + field_8_usage + nl +
+ " Name: " + field_9_name + nl +
+ " Unused2: " + field_10_unused2 + nl +
+ " Unused3: " + field_11_unused3 + nl +
+ " Extra Data:" + nl + extraData;
+
+
+ }
+
+ /**
+ * Retrieve the string representation given a blip id.
+ */
+ public String getBlipType( byte b )
+ {
+ switch ( b )
+ {
+ case BT_ERROR:
+ return " ERROR";
+ case BT_UNKNOWN:
+ return " UNKNOWN";
+ case BT_EMF:
+ return " EMF";
+ case BT_WMF:
+ return " WMF";
+ case BT_PICT:
+ return " PICT";
+ case BT_JPEG:
+ return " JPEG";
+ case BT_PNG:
+ return " PNG";
+ case BT_DIB:
+ return " DIB";
+ default:
+ if ( b < 32 )
+ return " NotKnown";
+ else
+ return " Client";
+ }
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherBlipRecord.java b/src/java/org/apache/poi/ddf/EscherBlipRecord.java
new file mode 100644
index 0000000000..ca8be718b4
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherBlipRecord.java
@@ -0,0 +1,417 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.hssf.record.RecordFormatException;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.DeflaterOutputStream;
+
+/**
+ * The blip record is used to hold details about large binary objects that occur in escher such
+ * as JPEG, GIF, PICT and WMF files. The contents of the stream is usually compressed. Inflate
+ * can be used to decompress the data.
+ *
+ * @author Glen Stampoultzis
+ * @see java.util.zip.Inflater
+ */
+public class EscherBlipRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID_START = (short) 0xF018;
+ public static final short RECORD_ID_END = (short) 0xF117;
+ public static final String RECORD_DESCRIPTION = "msofbtBlip";
+ private static final int HEADER_SIZE = 8;
+
+ private byte[] field_1_secondaryUID;
+ private int field_2_cacheOfSize;
+ private int field_3_boundaryTop;
+ private int field_4_boundaryLeft;
+ private int field_5_boundaryWidth;
+ private int field_6_boundaryHeight;
+ private int field_7_width;
+ private int field_8_height;
+ private int field_9_cacheOfSavedSize;
+ private byte field_10_compressionFlag;
+ private byte field_11_filter;
+ private byte[] field_12_data;
+
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset,
+ EscherRecordFactory recordFactory
+ )
+ {
+ int bytesAfterHeader = readHeader( data, offset );
+ int pos = offset + HEADER_SIZE;
+
+ int size = 0;
+ field_1_secondaryUID = new byte[16];
+ System.arraycopy( data, pos + size, field_1_secondaryUID, 0, 16 ); size += 16;
+ field_2_cacheOfSize = LittleEndian.getInt( data, pos + size );size+=4;
+ field_3_boundaryTop = LittleEndian.getInt( data, pos + size );size+=4;
+ field_4_boundaryLeft = LittleEndian.getInt( data, pos + size );size+=4;
+ field_5_boundaryWidth = LittleEndian.getInt( data, pos + size );size+=4;
+ field_6_boundaryHeight = LittleEndian.getInt( data, pos + size );size+=4;
+ field_7_width = LittleEndian.getInt( data, pos + size );size+=4;
+ field_8_height = LittleEndian.getInt( data, pos + size );size+=4;
+ field_9_cacheOfSavedSize = LittleEndian.getInt( data, pos + size );size+=4;
+ field_10_compressionFlag = data[pos + size]; size++;
+ field_11_filter = data[pos + size]; size++;
+
+ int bytesRemaining = bytesAfterHeader - size;
+ field_12_data = new byte[bytesRemaining];
+ System.arraycopy(data, pos + size, field_12_data, 0, bytesRemaining);
+
+ return bytesRemaining + HEADER_SIZE + bytesAfterHeader;
+ }
+
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ *
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize(offset, getRecordId(), this);
+
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ int remainingBytes = field_12_data.length + 36;
+ LittleEndian.putInt( data, offset + 4, remainingBytes );
+
+ int pos = offset + HEADER_SIZE;
+ System.arraycopy(field_1_secondaryUID, 0, data, pos, 16 ); pos += 16;
+ LittleEndian.putInt( data, pos, field_2_cacheOfSize); pos += 4;
+ LittleEndian.putInt( data, pos, field_3_boundaryTop); pos += 4;
+ LittleEndian.putInt( data, pos, field_4_boundaryLeft); pos += 4;
+ LittleEndian.putInt( data, pos, field_5_boundaryWidth); pos += 4;
+ LittleEndian.putInt( data, pos, field_6_boundaryHeight); pos += 4;
+ LittleEndian.putInt( data, pos, field_7_width); pos += 4;
+ LittleEndian.putInt( data, pos, field_8_height); pos += 4;
+ LittleEndian.putInt( data, pos, field_9_cacheOfSavedSize); pos += 4;
+ data[pos++] = field_10_compressionFlag;
+ data[pos++] = field_11_filter;
+ System.arraycopy(field_12_data, 0, data, pos, field_12_data.length); pos += field_12_data.length;
+
+ listener.afterRecordSerialize(pos, getRecordId(), pos - offset, this);
+ return pos - offset;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 58 + field_12_data.length;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "Blip";
+ }
+
+ /**
+ * Retrieve the secondary UID
+ */
+ public byte[] getSecondaryUID()
+ {
+ return field_1_secondaryUID;
+ }
+
+ /**
+ * Set the secondary UID
+ */
+ public void setSecondaryUID( byte[] field_1_secondaryUID )
+ {
+ this.field_1_secondaryUID = field_1_secondaryUID;
+ }
+
+ /**
+ * Retrieve the cache of the metafile size
+ */
+ public int getCacheOfSize()
+ {
+ return field_2_cacheOfSize;
+ }
+
+ /**
+ * Set the cache of the metafile size
+ */
+ public void setCacheOfSize( int field_2_cacheOfSize )
+ {
+ this.field_2_cacheOfSize = field_2_cacheOfSize;
+ }
+
+ /**
+ * Retrieve the top boundary of the metafile drawing commands
+ */
+ public int getBoundaryTop()
+ {
+ return field_3_boundaryTop;
+ }
+
+ /**
+ * Set the top boundary of the metafile drawing commands
+ */
+ public void setBoundaryTop( int field_3_boundaryTop )
+ {
+ this.field_3_boundaryTop = field_3_boundaryTop;
+ }
+
+ /**
+ * Retrieve the left boundary of the metafile drawing commands
+ */
+ public int getBoundaryLeft()
+ {
+ return field_4_boundaryLeft;
+ }
+
+ /**
+ * Set the left boundary of the metafile drawing commands
+ */
+ public void setBoundaryLeft( int field_4_boundaryLeft )
+ {
+ this.field_4_boundaryLeft = field_4_boundaryLeft;
+ }
+
+ /**
+ * Retrieve the boundary width of the metafile drawing commands
+ */
+ public int getBoundaryWidth()
+ {
+ return field_5_boundaryWidth;
+ }
+
+ /**
+ * Set the boundary width of the metafile drawing commands
+ */
+ public void setBoundaryWidth( int field_5_boundaryWidth )
+ {
+ this.field_5_boundaryWidth = field_5_boundaryWidth;
+ }
+
+ /**
+ * Retrieve the boundary height of the metafile drawing commands
+ */
+ public int getBoundaryHeight()
+ {
+ return field_6_boundaryHeight;
+ }
+
+ /**
+ * Set the boundary height of the metafile drawing commands
+ */
+ public void setBoundaryHeight( int field_6_boundaryHeight )
+ {
+ this.field_6_boundaryHeight = field_6_boundaryHeight;
+ }
+
+ /**
+ * Retrieve the width of the metafile in EMU's (English Metric Units).
+ */
+ public int getWidth()
+ {
+ return field_7_width;
+ }
+
+ /**
+ * Set the width of the metafile in EMU's (English Metric Units).
+ */
+ public void setWidth( int width )
+ {
+ this.field_7_width = width;
+ }
+
+ /**
+ * Retrieve the height of the metafile in EMU's (English Metric Units).
+ */
+ public int getHeight()
+ {
+ return field_8_height;
+ }
+
+ /**
+ * Set the height of the metafile in EMU's (English Metric Units).
+ */
+ public void setHeight( int height )
+ {
+ this.field_8_height = height;
+ }
+
+ /**
+ * Retrieve the cache of the saved size
+ */
+ public int getCacheOfSavedSize()
+ {
+ return field_9_cacheOfSavedSize;
+ }
+
+ /**
+ * Set the cache of the saved size
+ */
+ public void setCacheOfSavedSize( int field_9_cacheOfSavedSize )
+ {
+ this.field_9_cacheOfSavedSize = field_9_cacheOfSavedSize;
+ }
+
+ /**
+ * Is the contents of the blip compressed?
+ */
+ public byte getCompressionFlag()
+ {
+ return field_10_compressionFlag;
+ }
+
+ /**
+ * Set whether the contents of the blip is compressed
+ */
+ public void setCompressionFlag( byte field_10_compressionFlag )
+ {
+ this.field_10_compressionFlag = field_10_compressionFlag;
+ }
+
+ /**
+ * Filter should always be 0
+ */
+ public byte getFilter()
+ {
+ return field_11_filter;
+ }
+
+ /**
+ * Filter should always be 0
+ */
+ public void setFilter( byte field_11_filter )
+ {
+ this.field_11_filter = field_11_filter;
+ }
+
+ /**
+ * The BLIP data
+ */
+ public byte[] getData()
+ {
+ return field_12_data;
+ }
+
+ /**
+ * The BLIP data
+ */
+ public void setData( byte[] field_12_data )
+ {
+ this.field_12_data = field_12_data;
+ }
+
+ /**
+ * The string representation of this record.
+ *
+ * @return A string
+ */
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separator" );
+
+ String extraData;
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ try
+ {
+ HexDump.dump( this.field_12_data, 0, b, 0 );
+ extraData = b.toString();
+ }
+ catch ( Exception e )
+ {
+ extraData = e.toString();
+ }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
+ " Options: 0x" + HexDump.toHex( getOptions() ) + nl +
+ " Secondary UID: " + HexDump.toHex( field_1_secondaryUID ) + nl +
+ " CacheOfSize: " + field_2_cacheOfSize + nl +
+ " BoundaryTop: " + field_3_boundaryTop + nl +
+ " BoundaryLeft: " + field_4_boundaryLeft + nl +
+ " BoundaryWidth: " + field_5_boundaryWidth + nl +
+ " BoundaryHeight: " + field_6_boundaryHeight + nl +
+ " X: " + field_7_width + nl +
+ " Y: " + field_8_height + nl +
+ " CacheOfSavedSize: " + field_9_cacheOfSavedSize + nl +
+ " CompressionFlag: " + field_10_compressionFlag + nl +
+ " Filter: " + field_11_filter + nl +
+ " Data:" + nl + extraData;
+ }
+
+ /**
+ * Compress the contents of the provided array
+ *
+ * @param data An uncompressed byte array
+ * @see DeflaterOutputStream#write(int b)
+ */
+ public static byte[] compress( byte[] data )
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream( out );
+ try
+ {
+ for ( int i = 0; i < data.length; i++ )
+ deflaterOutputStream.write( data[i] );
+ }
+ catch ( IOException e )
+ {
+ throw new RecordFormatException( e.toString() );
+ }
+
+ return out.toByteArray();
+ }
+
+ /**
+ * Decompresses a byte array.
+ *
+ * @param data The compressed byte array
+ * @param pos The starting position into the byte array
+ * @param length The number of compressed bytes to decompress
+ * @return An uncompressed byte array
+ * @see InflaterInputStream#read
+ */
+ public static byte[] decompress( byte[] data, int pos, int length )
+ {
+ byte[] compressedData = new byte[length];
+ System.arraycopy( data, pos + 50, compressedData, 0, length );
+ InputStream compressedInputStream = new ByteArrayInputStream( compressedData );
+ InflaterInputStream inflaterInputStream = new InflaterInputStream( compressedInputStream );
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int c;
+ try
+ {
+ while ( ( c = inflaterInputStream.read() ) != -1 )
+ out.write( c );
+ }
+ catch ( IOException e )
+ {
+ throw new RecordFormatException( e.toString() );
+ }
+ return out.toByteArray();
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherBoolProperty.java b/src/java/org/apache/poi/ddf/EscherBoolProperty.java
new file mode 100644
index 0000000000..86a7a0a5c5
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherBoolProperty.java
@@ -0,0 +1,50 @@
+package org.apache.poi.ddf;
+
+/**
+ * Represents a boolean property. The actual utility of this property is in doubt because many
+ * of the properties marked as boolean seem to actually contain special values. In other words
+ * they're not true booleans.
+ *
+ * @author Glen Stampoultzis
+ * @see EscherSimpleProperty
+ * @see EscherProperty
+ */
+public class EscherBoolProperty
+ extends EscherSimpleProperty
+{
+ /**
+ * Create an instance of an escher boolean property.
+ *
+ * @param propertyNumber The property number
+ * @param value The 32 bit value of this bool property
+ */
+ public EscherBoolProperty( short propertyNumber, int value )
+ {
+ super( propertyNumber, false, false, value );
+ }
+
+ /**
+ * Whether this boolean property is true
+ */
+ public boolean isTrue()
+ {
+ return propertyValue != 0;
+ }
+
+ /**
+ * Whether this boolean property is false
+ */
+ public boolean isFalse()
+ {
+ return propertyValue == 0;
+ }
+
+// public String toString()
+// {
+// return "propNum: " + getPropertyNumber()
+// + ", complex: " + isComplex()
+// + ", blipId: " + isBlipId()
+// + ", value: " + (getValue() != 0);
+// }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java b/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java
new file mode 100644
index 0000000000..f53ba7d726
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java
@@ -0,0 +1,176 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * The escher child achor record is used to specify the position of a shape under an
+ * existing group. The first level of shape records use a EscherClientAnchor record instead.
+ *
+ * @author Glen Stampoultzis
+ * @see EscherChildAnchorRecord
+ */
+public class EscherChildAnchorRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF00F;
+ public static final String RECORD_DESCRIPTION = "MsofbtChildAnchor";
+
+ private int field_1_dx1;
+ private int field_2_dy1;
+ private int field_3_dx2;
+ private int field_4_dy2;
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ int size = 0;
+ field_1_dx1 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_2_dy1 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_3_dx2 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_4_dy2 = LittleEndian.getInt( data, pos + size );size+=4;
+ return 8 + size;
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+ int pos = offset;
+ LittleEndian.putShort( data, pos, getOptions() ); pos += 2;
+ LittleEndian.putShort( data, pos, getRecordId() ); pos += 2;
+ LittleEndian.putInt( data, pos, getRecordSize()-8 ); pos += 4;
+ LittleEndian.putInt( data, pos, field_1_dx1 ); pos += 4;
+ LittleEndian.putInt( data, pos, field_2_dy1 ); pos += 4;
+ LittleEndian.putInt( data, pos, field_3_dx2 ); pos += 4;
+ LittleEndian.putInt( data, pos, field_4_dy2 ); pos += 4;
+
+ listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
+ return pos - offset;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 4 * 4;
+ }
+
+ /**
+ * The record id for the EscherChildAnchorRecord.
+ */
+ public short getRecordId()
+ {
+ return RECORD_ID;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "ChildAnchor";
+ }
+
+ /**
+ * The string representation of this record
+ */
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
+ " Options: 0x" + HexDump.toHex(getOptions()) + nl +
+ " X1: " + field_1_dx1 + nl +
+ " Y1: " + field_2_dy1 + nl +
+ " X2: " + field_3_dx2 + nl +
+ " Y2: " + field_4_dy2 + nl ;
+
+ }
+
+ /**
+ * Retrieves offset within the parent coordinate space for the top left point.
+ */
+ public int getDx1()
+ {
+ return field_1_dx1;
+ }
+
+ /**
+ * Sets offset within the parent coordinate space for the top left point.
+ */
+ public void setDx1( int field_1_dx1 )
+ {
+ this.field_1_dx1 = field_1_dx1;
+ }
+
+ /**
+ * Gets offset within the parent coordinate space for the top left point.
+ */
+ public int getDy1()
+ {
+ return field_2_dy1;
+ }
+
+ /**
+ * Sets offset within the parent coordinate space for the top left point.
+ */
+ public void setDy1( int field_2_dy1 )
+ {
+ this.field_2_dy1 = field_2_dy1;
+ }
+
+ /**
+ * Retrieves offset within the parent coordinate space for the bottom right point.
+ */
+ public int getDx2()
+ {
+ return field_3_dx2;
+ }
+
+ /**
+ * Sets offset within the parent coordinate space for the bottom right point.
+ */
+ public void setDx2( int field_3_dx2 )
+ {
+ this.field_3_dx2 = field_3_dx2;
+ }
+
+ /**
+ * Gets the offset within the parent coordinate space for the bottom right point.
+ */
+ public int getDy2()
+ {
+ return field_4_dy2;
+ }
+
+ /**
+ * Sets the offset within the parent coordinate space for the bottom right point.
+ */
+ public void setDy2( int field_4_dy2 )
+ {
+ this.field_4_dy2 = field_4_dy2;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java b/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java
new file mode 100644
index 0000000000..d61d77b7a7
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java
@@ -0,0 +1,317 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * The escher client anchor specifies which rows and cells the shape is bound to as well as
+ * the offsets within those cells. Each cell is 1024 units wide by 256 units long regardless
+ * of the actual size of the cell. The EscherClientAnchorRecord only applies to the top-most
+ * shapes. Shapes contained in groups are bound using the EscherChildAnchorRecords.
+ *
+ * @author Glen Stampoultzis
+ * @see EscherChildAnchorRecord
+ */
+public class EscherClientAnchorRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF010;
+ public static final String RECORD_DESCRIPTION = "MsofbtClientAnchor";
+
+ private short field_1_flag;
+ private short field_2_col1;
+ private short field_3_dx1;
+ private short field_4_row1;
+ private short field_5_dy1;
+ private short field_6_col2;
+ private short field_7_dx2;
+ private short field_8_row2;
+ private short field_9_dy2;
+ private byte[] remainingData;
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ int size = 0;
+ field_1_flag = LittleEndian.getShort( data, pos + size ); size += 2;
+ field_2_col1 = LittleEndian.getShort( data, pos + size ); size += 2;
+ field_3_dx1 = LittleEndian.getShort( data, pos + size ); size += 2;
+ field_4_row1 = LittleEndian.getShort( data, pos + size ); size += 2;
+ field_5_dy1 = LittleEndian.getShort( data, pos + size ); size += 2;
+ field_6_col2 = LittleEndian.getShort( data, pos + size ); size += 2;
+ field_7_dx2 = LittleEndian.getShort( data, pos + size ); size += 2;
+ field_8_row2 = LittleEndian.getShort( data, pos + size ); size += 2;
+ field_9_dy2 = LittleEndian.getShort( data, pos + size ); size += 2;
+ bytesRemaining -= size;
+ remainingData = new byte[bytesRemaining];
+ System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
+ return 8 + size + bytesRemaining;
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ if (remainingData == null) remainingData = new byte[0];
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ int remainingBytes = remainingData.length + 18;
+ LittleEndian.putInt( data, offset + 4, remainingBytes );
+ LittleEndian.putShort( data, offset + 8, field_1_flag );
+ LittleEndian.putShort( data, offset + 10, field_2_col1 );
+ LittleEndian.putShort( data, offset + 12, field_3_dx1 );
+ LittleEndian.putShort( data, offset + 14, field_4_row1 );
+ LittleEndian.putShort( data, offset + 16, field_5_dy1 );
+ LittleEndian.putShort( data, offset + 18, field_6_col2 );
+ LittleEndian.putShort( data, offset + 20, field_7_dx2 );
+ LittleEndian.putShort( data, offset + 22, field_8_row2 );
+ LittleEndian.putShort( data, offset + 24, field_9_dy2 );
+ System.arraycopy( remainingData, 0, data, offset + 26, remainingData.length );
+ int pos = offset + 8 + 18 + remainingData.length;
+
+ listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
+ return pos - offset;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 18 + (remainingData == null ? 0 : remainingData.length);
+ }
+
+ /**
+ * The record id for this record.
+ */
+ public short getRecordId()
+ {
+ return RECORD_ID;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "ClientAnchor";
+ }
+
+ /**
+ * Returns the string representation for this record.
+ *
+ * @return A string
+ */
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+ String extraData;
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ try
+ {
+ HexDump.dump(this.remainingData, 0, b, 0);
+ extraData = b.toString();
+ }
+ catch ( Exception e )
+ {
+ extraData = "error";
+ }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
+ " Options: 0x" + HexDump.toHex(getOptions()) + nl +
+ " Flag: " + field_1_flag + nl +
+ " Col1: " + field_2_col1 + nl +
+ " DX1: " + field_3_dx1 + nl +
+ " Row1: " + field_4_row1 + nl +
+ " DY1: " + field_5_dy1 + nl +
+ " Col2: " + field_6_col2 + nl +
+ " DX2: " + field_7_dx2 + nl +
+ " Row2: " + field_8_row2 + nl +
+ " DY2: " + field_9_dy2 + nl +
+ " Extra Data:" + nl + extraData;
+
+ }
+
+ /**
+ * 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells.
+ */
+ public short getFlag()
+ {
+ return field_1_flag;
+ }
+
+ /**
+ * 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells.
+ */
+ public void setFlag( short field_1_flag )
+ {
+ this.field_1_flag = field_1_flag;
+ }
+
+ /**
+ * The column number for the top-left position. 0 based.
+ */
+ public short getCol1()
+ {
+ return field_2_col1;
+ }
+
+ /**
+ * The column number for the top-left position. 0 based.
+ */
+ public void setCol1( short field_2_col1 )
+ {
+ this.field_2_col1 = field_2_col1;
+ }
+
+ /**
+ * The x offset within the top-left cell. Range is from 0 to 1023.
+ */
+ public short getDx1()
+ {
+ return field_3_dx1;
+ }
+
+ /**
+ * The x offset within the top-left cell. Range is from 0 to 1023.
+ */
+ public void setDx1( short field_3_dx1 )
+ {
+ this.field_3_dx1 = field_3_dx1;
+ }
+
+ /**
+ * The row number for the top-left corner of the shape.
+ */
+ public short getRow1()
+ {
+ return field_4_row1;
+ }
+
+ /**
+ * The row number for the top-left corner of the shape.
+ */
+ public void setRow1( short field_4_row1 )
+ {
+ this.field_4_row1 = field_4_row1;
+ }
+
+ /**
+ * The y offset within the top-left corner of the current shape.
+ */
+ public short getDy1()
+ {
+ return field_5_dy1;
+ }
+
+ /**
+ * The y offset within the top-left corner of the current shape.
+ */
+ public void setDy1( short field_5_dy1 )
+ {
+ this.field_5_dy1 = field_5_dy1;
+ }
+
+ /**
+ * The column of the bottom right corner of this shape.
+ */
+ public short getCol2()
+ {
+ return field_6_col2;
+ }
+
+ /**
+ * The column of the bottom right corner of this shape.
+ */
+ public void setCol2( short field_6_col2 )
+ {
+ this.field_6_col2 = field_6_col2;
+ }
+
+ /**
+ * The x offset withing the cell for the bottom-right corner of this shape.
+ */
+ public short getDx2()
+ {
+ return field_7_dx2;
+ }
+
+ /**
+ * The x offset withing the cell for the bottom-right corner of this shape.
+ */
+ public void setDx2( short field_7_dx2 )
+ {
+ this.field_7_dx2 = field_7_dx2;
+ }
+
+ /**
+ * The row number for the bottom-right corner of the current shape.
+ */
+ public short getRow2()
+ {
+ return field_8_row2;
+ }
+
+ /**
+ * The row number for the bottom-right corner of the current shape.
+ */
+ public void setRow2( short field_8_row2 )
+ {
+ this.field_8_row2 = field_8_row2;
+ }
+
+ /**
+ * The y offset withing the cell for the bottom-right corner of this shape.
+ */
+ public short getDy2()
+ {
+ return field_9_dy2;
+ }
+
+ /**
+ * The y offset withing the cell for the bottom-right corner of this shape.
+ */
+ public void setDy2( short field_9_dy2 )
+ {
+ this.field_9_dy2 = field_9_dy2;
+ }
+
+ /**
+ * Any remaining data in the record
+ */
+ public byte[] getRemainingData()
+ {
+ return remainingData;
+ }
+
+ /**
+ * Any remaining data in the record
+ */
+ public void setRemainingData( byte[] remainingData )
+ {
+ this.remainingData = remainingData;
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/EscherClientDataRecord.java b/src/java/org/apache/poi/ddf/EscherClientDataRecord.java
new file mode 100644
index 0000000000..d568996f47
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherClientDataRecord.java
@@ -0,0 +1,130 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * The EscherClientDataRecord is used to store client specific data about the position of a
+ * shape within a container.
+ *
+ * @author Glen Stampoultzis
+ */
+public class EscherClientDataRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF011;
+ public static final String RECORD_DESCRIPTION = "MsofbtClientData";
+
+ private byte[] remainingData;
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ remainingData = new byte[bytesRemaining];
+ System.arraycopy( data, pos, remainingData, 0, bytesRemaining );
+ return 8 + bytesRemaining;
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ if (remainingData == null) remainingData = new byte[0];
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ LittleEndian.putInt( data, offset + 4, remainingData.length );
+ System.arraycopy( remainingData, 0, data, offset + 8, remainingData.length );
+ int pos = offset + 8 + remainingData.length;
+
+ listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
+ return pos - offset;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + (remainingData == null ? 0 : remainingData.length);
+ }
+
+ /**
+ * Returns the identifier of this record.
+ */
+ public short getRecordId()
+ {
+ return RECORD_ID;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "ClientData";
+ }
+
+ /**
+ * Returns the string representation of this record.
+ */
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+ String extraData;
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ try
+ {
+ HexDump.dump(this.remainingData, 0, b, 0);
+ extraData = b.toString();
+ }
+ catch ( Exception e )
+ {
+ extraData = "error";
+ }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
+ " Options: 0x" + HexDump.toHex(getOptions()) + nl +
+ " Extra Data:" + nl +
+ extraData;
+
+ }
+
+ /**
+ * Any data recording this record.
+ */
+ public byte[] getRemainingData()
+ {
+ return remainingData;
+ }
+
+ /**
+ * Any data recording this record.
+ */
+ public void setRemainingData( byte[] remainingData )
+ {
+ this.remainingData = remainingData;
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/EscherComplexProperty.java b/src/java/org/apache/poi/ddf/EscherComplexProperty.java
new file mode 100644
index 0000000000..4788a66c71
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherComplexProperty.java
@@ -0,0 +1,152 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.HexDump;
+
+import java.util.Arrays;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * A complex property differs from a simple property in that the data can not fit inside a 32 bit
+ * integer. See the specification for more detailed information regarding exactly what is
+ * stored here.
+ *
+ * @author Glen Stampoultzis
+ */
+public class EscherComplexProperty
+ extends EscherProperty
+{
+ byte[] complexData = new byte[0];
+
+ /**
+ * Create a complex property using the property id and a byte array containing the complex
+ * data value.
+ *
+ * @param id The id consists of the property number, a flag indicating whether this is a blip id and a flag
+ * indicating that this is a complex property.
+ * @param complexData The value of this property.
+ */
+ public EscherComplexProperty( short id, byte[] complexData )
+ {
+ super( id );
+ this.complexData = complexData;
+ }
+
+ /**
+ * Create a complex property using the property number, a flag to indicate whether this is a
+ * blip reference and the complex property data.
+ *
+ * @param propertyNumber The property number
+ * @param isBlipId Whether this is a blip id. Should be false.
+ * @param complexData The value of this complex property.
+ */
+ public EscherComplexProperty( short propertyNumber, boolean isBlipId, byte[] complexData )
+ {
+ super( propertyNumber, true, isBlipId );
+ this.complexData = complexData;
+ }
+
+ /**
+ * Serializes the simple part of this property. ie the first 6 bytes.
+ */
+ public int serializeSimplePart( byte[] data, int pos )
+ {
+ LittleEndian.putShort(data, pos, getId());
+ LittleEndian.putInt(data, pos + 2, complexData.length);
+ return 6;
+ }
+
+ /**
+ * Serializes the complex part of this property
+ *
+ * @param data The data array to serialize to
+ * @param pos The offset within data to start serializing to.
+ * @return The number of bytes serialized.
+ */
+ public int serializeComplexPart( byte[] data, int pos )
+ {
+ System.arraycopy(complexData, 0, data, pos, complexData.length);
+ return complexData.length;
+ }
+
+ /**
+ * Get the complex data value.
+ */
+ public byte[] getComplexData()
+ {
+ return complexData;
+ }
+
+ /**
+ * Determine whether this property is equal to another property.
+ *
+ * @param o The object to compare to.
+ * @return True if the objects are equal.
+ */
+ public boolean equals( Object o )
+ {
+ if ( this == o ) return true;
+ if ( !( o instanceof EscherComplexProperty ) ) return false;
+
+ final EscherComplexProperty escherComplexProperty = (EscherComplexProperty) o;
+
+ if ( !Arrays.equals( complexData, escherComplexProperty.complexData ) ) return false;
+
+ return true;
+ }
+
+ /**
+ * Caclulates the number of bytes required to serialize this property.
+ *
+ * @return Number of bytes
+ */
+ public int getPropertySize()
+ {
+ return 6 + complexData.length;
+ }
+
+ /**
+ * Calculates a hashcode for this property.
+ */
+ public int hashCode()
+ {
+ return getId() * 11;
+ }
+
+ /**
+ * Retrieves the string representation for this property.
+ */
+ public String toString()
+ {
+ String dataStr;
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ try
+ {
+ HexDump.dump( this.complexData, 0, b, 0 );
+ dataStr = b.toString();
+ }
+ catch ( Exception e )
+ {
+ dataStr = e.toString();
+ }
+ finally
+ {
+ try
+ {
+ b.close();
+ }
+ catch ( IOException e )
+ {
+ e.printStackTrace();
+ }
+ }
+
+ return "propNum: " + getPropertyNumber()
+ + ", propName: " + EscherProperties.getPropertyName( getPropertyNumber() )
+ + ", complex: " + isComplex()
+ + ", blipId: " + isBlipId()
+ + ", data: " + System.getProperty("line.separator") + dataStr;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherContainerRecord.java b/src/java/org/apache/poi/ddf/EscherContainerRecord.java
new file mode 100644
index 0000000000..41c7a7ec9e
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherContainerRecord.java
@@ -0,0 +1,168 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.io.PrintWriter;
+
+/**
+ * Escher container records store other escher records as children.
+ * The container records themselves never store any information beyond
+ * the standard header used by all escher records. This one record is
+ * used to represent many different types of records.
+ *
+ * @author Glen Stampoultzis
+ */
+public class EscherContainerRecord extends EscherRecord
+{
+ public static final short DGG_CONTAINER = (short)0xF000;
+ public static final short BSTORE_CONTAINER = (short)0xF001;
+ public static final short DG_CONTAINER = (short)0xF002;
+ public static final short SPGR_CONTAINER = (short)0xF003;
+ public static final short SP_CONTAINER = (short)0xF004;
+ public static final short SOLVER_CONTAINER = (short)0xF005;
+
+ private List childRecords = new ArrayList();
+
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int bytesWritten = 8;
+ offset += 8;
+ while ( bytesRemaining > 0 && offset < data.length )
+ {
+ EscherRecord child = recordFactory.createRecord(data, offset);
+ int childBytesWritten = child.fillFields( data, offset, recordFactory );
+ bytesWritten += childBytesWritten;
+ offset += childBytesWritten;
+ bytesRemaining -= childBytesWritten;
+ getChildRecords().add( child );
+ if (offset >= data.length && bytesRemaining > 0)
+ {
+ System.out.println("WARNING: " + bytesRemaining + " bytes remaining but no space left");
+ }
+ }
+ return bytesWritten;
+ }
+
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ LittleEndian.putShort(data, offset, getOptions());
+ LittleEndian.putShort(data, offset+2, getRecordId());
+ int remainingBytes = 0;
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ remainingBytes += r.getRecordSize();
+ }
+ LittleEndian.putInt(data, offset+4, remainingBytes);
+ int pos = offset+8;
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ pos += r.serialize(pos, data, listener );
+ }
+
+ listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
+ return pos - offset;
+ }
+
+ public int getRecordSize()
+ {
+ int childRecordsSize = 0;
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ childRecordsSize += r.getRecordSize();
+ }
+ return 8 + childRecordsSize;
+ }
+
+ public List getChildRecords()
+ {
+ return childRecords;
+ }
+
+ public void setChildRecords( List childRecords )
+ {
+ this.childRecords = childRecords;
+ }
+
+ public String getRecordName()
+ {
+ switch ((short)getRecordId())
+ {
+ case DGG_CONTAINER:
+ return "DggContainer";
+ case BSTORE_CONTAINER:
+ return "BStoreContainer";
+ case DG_CONTAINER:
+ return "DgContainer";
+ case SPGR_CONTAINER:
+ return "SpgrContainer";
+ case SP_CONTAINER:
+ return "SpContainer";
+ case SOLVER_CONTAINER:
+ return "SolverContainer";
+ default:
+ return "Container 0x" + HexDump.toHex(getRecordId());
+ }
+ }
+
+ public void display( PrintWriter w, int indent )
+ {
+ super.display( w, indent );
+ for ( Iterator iterator = childRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord escherRecord = (EscherRecord) iterator.next();
+ escherRecord.display( w, indent + 1 );
+ }
+ }
+
+ public void addChildRecord( EscherRecord record )
+ {
+ this.childRecords.add( record );
+ }
+
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separator" );
+
+ StringBuffer children = new StringBuffer();
+ if ( getChildRecords().size() > 0 )
+ {
+ children.append( " children: " + nl );
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord record = (EscherRecord) iterator.next();
+ children.append( record.toString() );
+// children.append( nl );
+ }
+ }
+
+ return getClass().getName() + " (" + getRecordName() + "):" + nl +
+ " isContainer: " + isContainerRecord() + nl +
+ " options: 0x" + HexDump.toHex( getOptions() ) + nl +
+ " recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
+ " numchildren: " + getChildRecords().size() + nl +
+ children.toString();
+
+ }
+
+ public EscherSpRecord getChildById( short recordId )
+ {
+ for ( Iterator iterator = childRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord escherRecord = (EscherRecord) iterator.next();
+ if (escherRecord.getRecordId() == recordId)
+ return (EscherSpRecord) escherRecord;
+ }
+ return null;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherDgRecord.java b/src/java/org/apache/poi/ddf/EscherDgRecord.java
new file mode 100644
index 0000000000..049bf383a6
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherDgRecord.java
@@ -0,0 +1,163 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * This record simply holds the number of shapes in the drawing group and the
+ * last shape id used for this drawing group.
+ *
+ * @author Glen Stampoultzis
+ */
+public class EscherDgRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF008;
+ public static final String RECORD_DESCRIPTION = "MsofbtDg";
+
+ private int field_1_numShapes;
+ private int field_2_lastMSOSPID;
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ int size = 0;
+ field_1_numShapes = LittleEndian.getInt( data, pos + size ); size += 4;
+ field_2_lastMSOSPID = LittleEndian.getInt( data, pos + size ); size += 4;
+// bytesRemaining -= size;
+// remainingData = new byte[bytesRemaining];
+// System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
+ return getRecordSize();
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ LittleEndian.putInt( data, offset + 4, 8 );
+ LittleEndian.putInt( data, offset + 8, field_1_numShapes );
+ LittleEndian.putInt( data, offset + 12, field_2_lastMSOSPID );
+// System.arraycopy( remainingData, 0, data, offset + 26, remainingData.length );
+// int pos = offset + 8 + 18 + remainingData.length;
+
+ listener.afterRecordSerialize( offset + 16, getRecordId(), getRecordSize(), this );
+ return getRecordSize();
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 8;
+ }
+
+ public short getRecordId()
+ {
+ return RECORD_ID;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "Dg";
+ }
+
+ /**
+ * Returns the string representation of this record.
+ */
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+// String extraData;
+// ByteArrayOutputStream b = new ByteArrayOutputStream();
+// try
+// {
+// HexDump.dump(this.remainingData, 0, b, 0);
+// extraData = b.toString();
+// }
+// catch ( Exception e )
+// {
+// extraData = "error";
+// }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
+ " Options: 0x" + HexDump.toHex(getOptions()) + nl +
+ " NumShapes: " + field_1_numShapes + nl +
+ " LastMSOSPID: " + field_2_lastMSOSPID + nl;
+
+ }
+
+ /**
+ * The number of shapes in this drawing group.
+ */
+ public int getNumShapes()
+ {
+ return field_1_numShapes;
+ }
+
+ /**
+ * The number of shapes in this drawing group.
+ */
+ public void setNumShapes( int field_1_numShapes )
+ {
+ this.field_1_numShapes = field_1_numShapes;
+ }
+
+ /**
+ * The last shape id used in this drawing group.
+ */
+ public int getLastMSOSPID()
+ {
+ return field_2_lastMSOSPID;
+ }
+
+ /**
+ * The last shape id used in this drawing group.
+ */
+ public void setLastMSOSPID( int field_2_lastMSOSPID )
+ {
+ this.field_2_lastMSOSPID = field_2_lastMSOSPID;
+ }
+
+ /**
+ * Gets the drawing group id for this record. This is encoded in the
+ * instance part of the option record.
+ *
+ * @return a drawing group id.
+ */
+ public short getDrawingGroupId()
+ {
+ return (short) ( getOptions() >> 4 );
+ }
+
+ public void incrementShapeCount()
+ {
+ this.field_1_numShapes++;
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/EscherDggRecord.java b/src/java/org/apache/poi/ddf/EscherDggRecord.java
new file mode 100644
index 0000000000..1adcf82fc4
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherDggRecord.java
@@ -0,0 +1,241 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hssf.record.RecordFormatException;
+
+import java.lang.reflect.Array;
+import java.util.*;
+
+/**
+ * This record defines the drawing groups used for a particular sheet.
+ */
+public class EscherDggRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF006;
+ public static final String RECORD_DESCRIPTION = "MsofbtDgg";
+
+ private int field_1_shapeIdMax;
+// private int field_2_numIdClusters; // for some reason the number of clusters is actually the real number + 1
+ private int field_3_numShapesSaved;
+ private int field_4_drawingsSaved;
+ private FileIdCluster[] field_5_fileIdClusters;
+
+ public static class FileIdCluster
+ {
+ public FileIdCluster( int drawingGroupId, int numShapeIdsUsed )
+ {
+ this.field_1_drawingGroupId = drawingGroupId;
+ this.field_2_numShapeIdsUsed = numShapeIdsUsed;
+ }
+
+ private int field_1_drawingGroupId;
+ private int field_2_numShapeIdsUsed;
+
+ public int getDrawingGroupId()
+ {
+ return field_1_drawingGroupId;
+ }
+
+ public int getNumShapeIdsUsed()
+ {
+ return field_2_numShapeIdsUsed;
+ }
+
+ public void incrementShapeId( )
+ {
+ this.field_2_numShapeIdsUsed++;
+ }
+ }
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ int size = 0;
+ field_1_shapeIdMax = LittleEndian.getInt( data, pos + size );size+=4;
+ int field_2_numIdClusters = LittleEndian.getInt( data, pos + size );size+=4;
+ field_3_numShapesSaved = LittleEndian.getInt( data, pos + size );size+=4;
+ field_4_drawingsSaved = LittleEndian.getInt( data, pos + size );size+=4;
+ field_5_fileIdClusters = new FileIdCluster[field_2_numIdClusters-1];
+ for (int i = 0; i < field_2_numIdClusters-1; i++)
+ {
+ field_5_fileIdClusters[i] = new FileIdCluster(LittleEndian.getInt( data, pos + size ), LittleEndian.getInt( data, pos + size + 4 ));
+ size += 8;
+ }
+ bytesRemaining -= size;
+ if (bytesRemaining != 0)
+ throw new RecordFormatException("Expecting no remaining data but got " + bytesRemaining + " byte(s).");
+ return 8 + size + bytesRemaining;
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ *
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ int pos = offset;
+ LittleEndian.putShort( data, pos, getOptions() ); pos += 2;
+ LittleEndian.putShort( data, pos, getRecordId() ); pos += 2;
+ int remainingBytes = getRecordSize() - 8;
+ LittleEndian.putInt( data, pos, remainingBytes ); pos += 4;
+ LittleEndian.putInt( data, pos, field_1_shapeIdMax ); pos += 4;
+ LittleEndian.putInt( data, pos, getNumIdClusters() ); pos += 4;
+ LittleEndian.putInt( data, pos, field_3_numShapesSaved ); pos += 4;
+ LittleEndian.putInt( data, pos, field_4_drawingsSaved ); pos += 4;
+ for ( int i = 0; i < field_5_fileIdClusters.length; i++ )
+ {
+ LittleEndian.putInt( data, pos, field_5_fileIdClusters[i].field_1_drawingGroupId ); pos += 4;
+ LittleEndian.putInt( data, pos, field_5_fileIdClusters[i].field_2_numShapeIdsUsed ); pos += 4;
+ }
+
+ listener.afterRecordSerialize( pos, getRecordId(), getRecordSize(), this );
+ return getRecordSize();
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 16 + (8 * field_5_fileIdClusters.length);
+ }
+
+ public short getRecordId()
+ {
+ return RECORD_ID;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "Dgg";
+ }
+
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+// String extraData;
+// ByteArrayOutputStream b = new ByteArrayOutputStream();
+// try
+// {
+// HexDump.dump(this.remainingData, 0, b, 0);
+// extraData = b.toString();
+// }
+// catch ( Exception e )
+// {
+// extraData = "error";
+// }
+ StringBuffer field_5_string = new StringBuffer();
+ for ( int i = 0; i < field_5_fileIdClusters.length; i++ )
+ {
+ field_5_string.append(" DrawingGroupId").append(i+1).append(": ");
+ field_5_string.append(field_5_fileIdClusters[i].field_1_drawingGroupId);
+ field_5_string.append(nl);
+ field_5_string.append(" NumShapeIdsUsed").append(i+1).append(": ");
+ field_5_string.append(field_5_fileIdClusters[i].field_2_numShapeIdsUsed);
+ field_5_string.append(nl);
+ }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
+ " Options: 0x" + HexDump.toHex(getOptions()) + nl +
+ " ShapeIdMax: " + field_1_shapeIdMax + nl +
+ " NumIdClusters: " + getNumIdClusters() + nl +
+ " NumShapesSaved: " + field_3_numShapesSaved + nl +
+ " DrawingsSaved: " + field_4_drawingsSaved + nl +
+ "" + field_5_string.toString();
+
+ }
+
+ public int getShapeIdMax()
+ {
+ return field_1_shapeIdMax;
+ }
+
+ /**
+ * The maximum is actually the next available. shape id.
+ */
+ public void setShapeIdMax( int field_1_shapeIdMax )
+ {
+ this.field_1_shapeIdMax = field_1_shapeIdMax;
+ }
+
+ public int getNumIdClusters()
+ {
+ return field_5_fileIdClusters.length + 1;
+ }
+
+ public int getNumShapesSaved()
+ {
+ return field_3_numShapesSaved;
+ }
+
+ public void setNumShapesSaved( int field_3_numShapesSaved )
+ {
+ this.field_3_numShapesSaved = field_3_numShapesSaved;
+ }
+
+ public int getDrawingsSaved()
+ {
+ return field_4_drawingsSaved;
+ }
+
+ public void setDrawingsSaved( int field_4_drawingsSaved )
+ {
+ this.field_4_drawingsSaved = field_4_drawingsSaved;
+ }
+
+ public FileIdCluster[] getFileIdClusters()
+ {
+ return field_5_fileIdClusters;
+ }
+
+ public void setFileIdClusters( FileIdCluster[] field_5_fileIdClusters )
+ {
+ this.field_5_fileIdClusters = field_5_fileIdClusters;
+ }
+
+ public void addCluster( int dgId, int numShapedUsed )
+ {
+ List clusters = new ArrayList(Arrays.asList(field_5_fileIdClusters));
+ clusters.add(new FileIdCluster(dgId, numShapedUsed));
+ Collections.sort(clusters, new Comparator()
+ {
+ public int compare( Object o1, Object o2 )
+ {
+ FileIdCluster f1 = (FileIdCluster) o1;
+ FileIdCluster f2 = (FileIdCluster) o2;
+ if (f1.getDrawingGroupId() == f2.getDrawingGroupId())
+ return 0;
+ if (f1.getDrawingGroupId() < f2.getDrawingGroupId())
+ return -1;
+ else
+ return +1;
+ }
+ } );
+ field_5_fileIdClusters = (FileIdCluster[]) clusters.toArray( new FileIdCluster[clusters.size()] );
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/EscherDump.java b/src/java/org/apache/poi/ddf/EscherDump.java
new file mode 100644
index 0000000000..7930486964
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherDump.java
@@ -0,0 +1,990 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+import org.apache.poi.util.LittleEndian;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.zip.InflaterInputStream;
+
+/**
+ * Used to dump the contents of escher records to a PrintStream.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherDump
+{
+
+ public EscherDump()
+ {
+ }
+
+ /**
+ * Decodes the escher stream from a byte array and dumps the results to
+ * a print stream.
+ *
+ * @param data The data array containing the escher records.
+ * @param offset The starting offset within the data array.
+ * @param size The number of bytes to read.
+ * @param out The output stream to write the results to.
+ *
+ */
+ public void dump( byte[] data, int offset, int size, PrintStream out ) throws IOException, LittleEndian.BufferUnderrunException
+ {
+ EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
+ int pos = offset;
+ while ( pos < offset + size )
+ {
+ EscherRecord r = recordFactory.createRecord(data, pos);
+ int bytesRead = r.fillFields(data, pos, recordFactory );
+ System.out.println( r.toString() );
+ pos += bytesRead;
+ }
+ }
+
+ /**
+ * This version of dump is a translation from the open office escher dump routine.
+ *
+ * @param maxLength The number of bytes to read
+ * @param in An input stream to read from.
+ * @param out An output stream to write to.
+ */
+ public void dumpOld( long maxLength, InputStream in, PrintStream out ) throws IOException, LittleEndian.BufferUnderrunException
+ {
+ long remainingBytes = maxLength;
+ short options; // 4 bits for the version and 12 bits for the instance
+ short recordId;
+ int recordBytesRemaining; // including enclosing records
+ StringBuffer stringBuf = new StringBuffer();
+ short nDumpSize;
+ String recordName;
+
+ boolean atEOF = false;
+
+ while ( !atEOF && ( remainingBytes > 0 ) )
+ {
+ stringBuf = new StringBuffer();
+ options = LittleEndian.readShort( in );
+ recordId = LittleEndian.readShort( in );
+ recordBytesRemaining = LittleEndian.readInt( in );
+
+ remainingBytes -= 2 + 2 + 4;
+
+ switch ( recordId )
+ {
+ case (short) 0xF000:
+ recordName = "MsofbtDggContainer";
+ break;
+ case (short) 0xF006:
+ recordName = "MsofbtDgg";
+ break;
+ case (short) 0xF016:
+ recordName = "MsofbtCLSID";
+ break;
+ case (short) 0xF00B:
+ recordName = "MsofbtOPT";
+ break;
+ case (short) 0xF11A:
+ recordName = "MsofbtColorMRU";
+ break;
+ case (short) 0xF11E:
+ recordName = "MsofbtSplitMenuColors";
+ break;
+ case (short) 0xF001:
+ recordName = "MsofbtBstoreContainer";
+ break;
+ case (short) 0xF007:
+ recordName = "MsofbtBSE";
+ break;
+ case (short) 0xF002:
+ recordName = "MsofbtDgContainer";
+ break;
+ case (short) 0xF008:
+ recordName = "MsofbtDg";
+ break;
+ case (short) 0xF118:
+ recordName = "MsofbtRegroupItem";
+ break;
+ case (short) 0xF120:
+ recordName = "MsofbtColorScheme";
+ break;
+ case (short) 0xF003:
+ recordName = "MsofbtSpgrContainer";
+ break;
+ case (short) 0xF004:
+ recordName = "MsofbtSpContainer";
+ break;
+ case (short) 0xF009:
+ recordName = "MsofbtSpgr";
+ break;
+ case (short) 0xF00A:
+ recordName = "MsofbtSp";
+ break;
+ case (short) 0xF00C:
+ recordName = "MsofbtTextbox";
+ break;
+ case (short) 0xF00D:
+ recordName = "MsofbtClientTextbox";
+ break;
+ case (short) 0xF00E:
+ recordName = "MsofbtAnchor";
+ break;
+ case (short) 0xF00F:
+ recordName = "MsofbtChildAnchor";
+ break;
+ case (short) 0xF010:
+ recordName = "MsofbtClientAnchor";
+ break;
+ case (short) 0xF011:
+ recordName = "MsofbtClientData";
+ break;
+ case (short) 0xF11F:
+ recordName = "MsofbtOleObject";
+ break;
+ case (short) 0xF11D:
+ recordName = "MsofbtDeletedPspl";
+ break;
+ case (short) 0xF005:
+ recordName = "MsofbtSolverContainer";
+ break;
+ case (short) 0xF012:
+ recordName = "MsofbtConnectorRule";
+ break;
+ case (short) 0xF013:
+ recordName = "MsofbtAlignRule";
+ break;
+ case (short) 0xF014:
+ recordName = "MsofbtArcRule";
+ break;
+ case (short) 0xF015:
+ recordName = "MsofbtClientRule";
+ break;
+ case (short) 0xF017:
+ recordName = "MsofbtCalloutRule";
+ break;
+ case (short) 0xF119:
+ recordName = "MsofbtSelection";
+ break;
+ case (short) 0xF122:
+ recordName = "MsofbtUDefProp";
+ break;
+ default:
+ if ( recordId >= (short) 0xF018 && recordId <= (short) 0xF117 )
+ recordName = "MsofbtBLIP";
+ else if ( ( options & (short) 0x000F ) == (short) 0x000F )
+ recordName = "UNKNOWN container";
+ else
+ recordName = "UNKNOWN ID";
+ }
+
+ stringBuf.append( " " );
+ stringBuf.append( HexDump.toHex( recordId ) );
+ stringBuf.append( " " ).append( recordName ).append( " [" );
+ stringBuf.append( HexDump.toHex( options ) );
+ stringBuf.append( ',' );
+ stringBuf.append( HexDump.toHex( recordBytesRemaining ) );
+ stringBuf.append( "] instance: " );
+ stringBuf.append( HexDump.toHex( ( (short) ( options >> 4 ) ) ) );
+ out.println( stringBuf.toString() );
+
+
+ if ( recordId == (short) 0xF007 && 36 <= remainingBytes && 36 <= recordBytesRemaining )
+ { // BSE, FBSE
+ // ULONG nP = pIn->GetRecPos();
+
+ byte n8;
+ // short n16;
+ // int n32;
+
+ stringBuf = new StringBuffer( " btWin32: " );
+ n8 = (byte) in.read();
+ stringBuf.append( HexDump.toHex( n8 ) );
+ stringBuf.append( getBlipType( n8 ) );
+ stringBuf.append( " btMacOS: " );
+ n8 = (byte) in.read();
+ stringBuf.append( HexDump.toHex( n8 ) );
+ stringBuf.append( getBlipType( n8 ) );
+ out.println( stringBuf.toString() );
+
+ out.println( " rgbUid:" );
+ HexDump.dump( in, out, 0, 16 );
+
+ out.print( " tag: " );
+ outHex( 2, in, out );
+ out.println();
+ out.print( " size: " );
+ outHex( 4, in, out );
+ out.println();
+ out.print( " cRef: " );
+ outHex( 4, in, out );
+ out.println();
+ out.print( " offs: " );
+ outHex( 4, in, out );
+ out.println();
+ out.print( " usage: " );
+ outHex( 1, in, out );
+ out.println();
+ out.print( " cbName: " );
+ outHex( 1, in, out );
+ out.println();
+ out.print( " unused2: " );
+ outHex( 1, in, out );
+ out.println();
+ out.print( " unused3: " );
+ outHex( 1, in, out );
+ out.println();
+
+ // subtract the number of bytes we've read
+ remainingBytes -= 36;
+ //n -= pIn->GetRecPos() - nP;
+ recordBytesRemaining = 0; // loop to MsofbtBLIP
+ }
+ else if ( recordId == (short) 0xF010 && 0x12 <= remainingBytes && 0x12 <= recordBytesRemaining )
+ { // ClientAnchor
+ //ULONG nP = pIn->GetRecPos();
+ // short n16;
+
+ out.print( " Flag: " );
+ outHex( 2, in, out );
+ out.println();
+ out.print( " Col1: " );
+ outHex( 2, in, out );
+ out.print( " dX1: " );
+ outHex( 2, in, out );
+ out.print( " Row1: " );
+ outHex( 2, in, out );
+ out.print( " dY1: " );
+ outHex( 2, in, out );
+ out.println();
+ out.print( " Col2: " );
+ outHex( 2, in, out );
+ out.print( " dX2: " );
+ outHex( 2, in, out );
+ out.print( " Row2: " );
+ outHex( 2, in, out );
+ out.print( " dY2: " );
+ outHex( 2, in, out );
+ out.println();
+
+ remainingBytes -= 18;
+ recordBytesRemaining -= 18;
+
+ }
+ else if ( recordId == (short) 0xF00B || recordId == (short) 0xF122 )
+ { // OPT
+ int nComplex = 0;
+ out.println( " PROPID VALUE" );
+ while ( recordBytesRemaining >= 6 + nComplex && remainingBytes >= 6 + nComplex )
+ {
+ short n16;
+ int n32;
+ n16 = LittleEndian.readShort( in );
+ n32 = LittleEndian.readInt( in );
+
+ recordBytesRemaining -= 6;
+ remainingBytes -= 6;
+ out.print( " " );
+ out.print( HexDump.toHex( n16 ) );
+ out.print( " (" );
+ int propertyId = n16 & (short) 0x3FFF;
+ out.print( " " + propertyId );
+ if ( ( n16 & (short) 0x8000 ) == 0 )
+ {
+ if ( ( n16 & (short) 0x4000 ) != 0 )
+ out.print( ", fBlipID" );
+ out.print( ") " );
+
+ out.print( HexDump.toHex( n32 ) );
+
+ if ( ( n16 & (short) 0x4000 ) == 0 )
+ {
+ out.print( " (" );
+ out.print( dec1616( n32 ) );
+ out.print( ')' );
+ out.print( " {" + propName( (short)propertyId ) + "}" );
+ }
+ out.println();
+ }
+ else
+ {
+ out.print( ", fComplex) " );
+ out.print( HexDump.toHex( n32 ) );
+ out.print( " - Complex prop len" );
+ out.println( " {" + propName( (short)propertyId ) + "}" );
+
+ nComplex += n32;
+ }
+
+ }
+ // complex property data
+ while ( ( nComplex & remainingBytes ) > 0 )
+ {
+ nDumpSize = ( nComplex > (int) remainingBytes ) ? (short) remainingBytes : (short) nComplex;
+ HexDump.dump( in, out, 0, nDumpSize );
+ nComplex -= nDumpSize;
+ recordBytesRemaining -= nDumpSize;
+ remainingBytes -= nDumpSize;
+ }
+ }
+ else if ( recordId == (short) 0xF012 )
+ {
+ out.print( " Connector rule: " );
+ out.print( LittleEndian.readInt( in ) );
+ out.print( " ShapeID A: " );
+ out.print( LittleEndian.readInt( in ) );
+ out.print( " ShapeID B: " );
+ out.print( LittleEndian.readInt( in ) );
+ out.print( " ShapeID connector: " );
+ out.print( LittleEndian.readInt( in ) );
+ out.print( " Connect pt A: " );
+ out.print( LittleEndian.readInt( in ) );
+ out.print( " Connect pt B: " );
+ out.println( LittleEndian.readInt( in ) );
+
+ recordBytesRemaining -= 24;
+ remainingBytes -= 24;
+ }
+ else if ( recordId >= (short) 0xF018 && recordId < (short) 0xF117 )
+ {
+ out.println( " Secondary UID: " );
+ HexDump.dump( in, out, 0, 16 );
+ out.println( " Cache of size: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
+ out.println( " Boundary top: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
+ out.println( " Boundary left: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
+ out.println( " Boundary width: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
+ out.println( " Boundary height: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
+ out.println( " X: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
+ out.println( " Y: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
+ out.println( " Cache of saved size: " + HexDump.toHex( LittleEndian.readInt( in ) ) );
+ out.println( " Compression Flag: " + HexDump.toHex( (byte) in.read() ) );
+ out.println( " Filter: " + HexDump.toHex( (byte) in.read() ) );
+ out.println( " Data (after decompression): " );
+
+ recordBytesRemaining -= 34 + 16;
+ remainingBytes -= 34 + 16;
+
+ nDumpSize = ( recordBytesRemaining > (int) remainingBytes ) ? (short) remainingBytes : (short) recordBytesRemaining;
+
+
+ byte[] buf = new byte[nDumpSize];
+ int read = in.read( buf );
+ while ( read != -1 && read < nDumpSize )
+ read += in.read( buf, read, buf.length );
+ ByteArrayInputStream bin = new ByteArrayInputStream( buf );
+
+ InputStream in1 = new InflaterInputStream( bin );
+ int bytesToDump = -1;
+ HexDump.dump( in1, out, 0, bytesToDump );
+
+ recordBytesRemaining -= nDumpSize;
+ remainingBytes -= nDumpSize;
+
+ }
+
+ boolean isContainer = ( options & (short) 0x000F ) == (short) 0x000F;
+ if ( isContainer && remainingBytes >= 0 )
+ { // Container
+ if ( recordBytesRemaining <= (int) remainingBytes )
+ out.println( " completed within" );
+ else
+ out.println( " continued elsewhere" );
+ }
+ else if ( remainingBytes >= 0 )
+ // -> 0x0000 ... 0x0FFF
+ {
+ nDumpSize = ( recordBytesRemaining > (int) remainingBytes ) ? (short) remainingBytes : (short) recordBytesRemaining;
+
+ if ( nDumpSize != 0 )
+ {
+ HexDump.dump( in, out, 0, nDumpSize );
+ remainingBytes -= nDumpSize;
+ }
+ }
+ else
+ out.println( " >> OVERRUN <<" );
+ }
+
+ }
+
+ /**
+ * Returns a property name given a property id. This is used only by the
+ * old escher dump routine.
+ *
+ * @param propertyId The property number for the name
+ * @return A descriptive name.
+ */
+ private String propName( short propertyId )
+ {
+ class PropName {
+ public PropName( int id, String name )
+ {
+ this.id = id;
+ this.name = name;
+ }
+
+ int id;
+ String name;
+ }
+
+ final PropName[] props = new PropName[] {
+ new PropName(4, "transform.rotation"),
+ new PropName(119, "protection.lockrotation"),
+ new PropName(120, "protection.lockaspectratio"),
+ new PropName(121, "protection.lockposition"),
+ new PropName(122, "protection.lockagainstselect"),
+ new PropName(123, "protection.lockcropping"),
+ new PropName(124, "protection.lockvertices"),
+ new PropName(125, "protection.locktext"),
+ new PropName(126, "protection.lockadjusthandles"),
+ new PropName(127, "protection.lockagainstgrouping"),
+ new PropName(128, "text.textid"),
+ new PropName(129, "text.textleft"),
+ new PropName(130, "text.texttop"),
+ new PropName(131, "text.textright"),
+ new PropName(132, "text.textbottom"),
+ new PropName(133, "text.wraptext"),
+ new PropName(134, "text.scaletext"),
+ new PropName(135, "text.anchortext"),
+ new PropName(136, "text.textflow"),
+ new PropName(137, "text.fontrotation"),
+ new PropName(138, "text.idofnextshape"),
+ new PropName(139, "text.bidir"),
+ new PropName(187, "text.singleclickselects"),
+ new PropName(188, "text.usehostmargins"),
+ new PropName(189, "text.rotatetextwithshape"),
+ new PropName(190, "text.sizeshapetofittext"),
+ new PropName(191, "text.sizetexttofitshape"),
+ new PropName(192, "geotext.unicode"),
+ new PropName(193, "geotext.rtftext"),
+ new PropName(194, "geotext.alignmentoncurve"),
+ new PropName(195, "geotext.defaultpointsize"),
+ new PropName(196, "geotext.textspacing"),
+ new PropName(197, "geotext.fontfamilyname"),
+ new PropName(240, "geotext.reverseroworder"),
+ new PropName(241, "geotext.hastexteffect"),
+ new PropName(242, "geotext.rotatecharacters"),
+ new PropName(243, "geotext.kerncharacters"),
+ new PropName(244, "geotext.tightortrack"),
+ new PropName(245, "geotext.stretchtofitshape"),
+ new PropName(246, "geotext.charboundingbox"),
+ new PropName(247, "geotext.scaletextonpath"),
+ new PropName(248, "geotext.stretchcharheight"),
+ new PropName(249, "geotext.nomeasurealongpath"),
+ new PropName(250, "geotext.boldfont"),
+ new PropName(251, "geotext.italicfont"),
+ new PropName(252, "geotext.underlinefont"),
+ new PropName(253, "geotext.shadowfont"),
+ new PropName(254, "geotext.smallcapsfont"),
+ new PropName(255, "geotext.strikethroughfont"),
+ new PropName(256, "blip.cropfromtop"),
+ new PropName(257, "blip.cropfrombottom"),
+ new PropName(258, "blip.cropfromleft"),
+ new PropName(259, "blip.cropfromright"),
+ new PropName(260, "blip.bliptodisplay"),
+ new PropName(261, "blip.blipfilename"),
+ new PropName(262, "blip.blipflags"),
+ new PropName(263, "blip.transparentcolor"),
+ new PropName(264, "blip.contrastsetting"),
+ new PropName(265, "blip.brightnesssetting"),
+ new PropName(266, "blip.gamma"),
+ new PropName(267, "blip.pictureid"),
+ new PropName(268, "blip.doublemod"),
+ new PropName(269, "blip.picturefillmod"),
+ new PropName(270, "blip.pictureline"),
+ new PropName(271, "blip.printblip"),
+ new PropName(272, "blip.printblipfilename"),
+ new PropName(273, "blip.printflags"),
+ new PropName(316, "blip.nohittestpicture"),
+ new PropName(317, "blip.picturegray"),
+ new PropName(318, "blip.picturebilevel"),
+ new PropName(319, "blip.pictureactive"),
+ new PropName(320, "geometry.left"),
+ new PropName(321, "geometry.top"),
+ new PropName(322, "geometry.right"),
+ new PropName(323, "geometry.bottom"),
+ new PropName(324, "geometry.shapepath"),
+ new PropName(325, "geometry.vertices"),
+ new PropName(326, "geometry.segmentinfo"),
+ new PropName(327, "geometry.adjustvalue"),
+ new PropName(328, "geometry.adjust2value"),
+ new PropName(329, "geometry.adjust3value"),
+ new PropName(330, "geometry.adjust4value"),
+ new PropName(331, "geometry.adjust5value"),
+ new PropName(332, "geometry.adjust6value"),
+ new PropName(333, "geometry.adjust7value"),
+ new PropName(334, "geometry.adjust8value"),
+ new PropName(335, "geometry.adjust9value"),
+ new PropName(336, "geometry.adjust10value"),
+ new PropName(378, "geometry.shadowOK"),
+ new PropName(379, "geometry.3dok"),
+ new PropName(380, "geometry.lineok"),
+ new PropName(381, "geometry.geotextok"),
+ new PropName(382, "geometry.fillshadeshapeok"),
+ new PropName(383, "geometry.fillok"),
+ new PropName(384, "fill.filltype"),
+ new PropName(385, "fill.fillcolor"),
+ new PropName(386, "fill.fillopacity"),
+ new PropName(387, "fill.fillbackcolor"),
+ new PropName(388, "fill.backopacity"),
+ new PropName(389, "fill.crmod"),
+ new PropName(390, "fill.patterntexture"),
+ new PropName(391, "fill.blipfilename"),
+ new PropName(392, "fill.blipflags"),
+ new PropName(393, "fill.width"),
+ new PropName(394, "fill.height"),
+ new PropName(395, "fill.angle"),
+ new PropName(396, "fill.focus"),
+ new PropName(397, "fill.toleft"),
+ new PropName(398, "fill.totop"),
+ new PropName(399, "fill.toright"),
+ new PropName(400, "fill.tobottom"),
+ new PropName(401, "fill.rectleft"),
+ new PropName(402, "fill.recttop"),
+ new PropName(403, "fill.rectright"),
+ new PropName(404, "fill.rectbottom"),
+ new PropName(405, "fill.dztype"),
+ new PropName(406, "fill.shadepreset"),
+ new PropName(407, "fill.shadecolors"),
+ new PropName(408, "fill.originx"),
+ new PropName(409, "fill.originy"),
+ new PropName(410, "fill.shapeoriginx"),
+ new PropName(411, "fill.shapeoriginy"),
+ new PropName(412, "fill.shadetype"),
+ new PropName(443, "fill.filled"),
+ new PropName(444, "fill.hittestfill"),
+ new PropName(445, "fill.shape"),
+ new PropName(446, "fill.userect"),
+ new PropName(447, "fill.nofillhittest"),
+ new PropName(448, "linestyle.color"),
+ new PropName(449, "linestyle.opacity"),
+ new PropName(450, "linestyle.backcolor"),
+ new PropName(451, "linestyle.crmod"),
+ new PropName(452, "linestyle.linetype"),
+ new PropName(453, "linestyle.fillblip"),
+ new PropName(454, "linestyle.fillblipname"),
+ new PropName(455, "linestyle.fillblipflags"),
+ new PropName(456, "linestyle.fillwidth"),
+ new PropName(457, "linestyle.fillheight"),
+ new PropName(458, "linestyle.filldztype"),
+ new PropName(459, "linestyle.linewidth"),
+ new PropName(460, "linestyle.linemiterlimit"),
+ new PropName(461, "linestyle.linestyle"),
+ new PropName(462, "linestyle.linedashing"),
+ new PropName(463, "linestyle.linedashstyle"),
+ new PropName(464, "linestyle.linestartarrowhead"),
+ new PropName(465, "linestyle.lineendarrowhead"),
+ new PropName(466, "linestyle.linestartarrowwidth"),
+ new PropName(467, "linestyle.lineestartarrowlength"),
+ new PropName(468, "linestyle.lineendarrowwidth"),
+ new PropName(469, "linestyle.lineendarrowlength"),
+ new PropName(470, "linestyle.linejoinstyle"),
+ new PropName(471, "linestyle.lineendcapstyle"),
+ new PropName(507, "linestyle.arrowheadsok"),
+ new PropName(508, "linestyle.anyline"),
+ new PropName(509, "linestyle.hitlinetest"),
+ new PropName(510, "linestyle.linefillshape"),
+ new PropName(511, "linestyle.nolinedrawdash"),
+ new PropName(512, "shadowstyle.type"),
+ new PropName(513, "shadowstyle.color"),
+ new PropName(514, "shadowstyle.highlight"),
+ new PropName(515, "shadowstyle.crmod"),
+ new PropName(516, "shadowstyle.opacity"),
+ new PropName(517, "shadowstyle.offsetx"),
+ new PropName(518, "shadowstyle.offsety"),
+ new PropName(519, "shadowstyle.secondoffsetx"),
+ new PropName(520, "shadowstyle.secondoffsety"),
+ new PropName(521, "shadowstyle.scalextox"),
+ new PropName(522, "shadowstyle.scaleytox"),
+ new PropName(523, "shadowstyle.scalextoy"),
+ new PropName(524, "shadowstyle.scaleytoy"),
+ new PropName(525, "shadowstyle.perspectivex"),
+ new PropName(526, "shadowstyle.perspectivey"),
+ new PropName(527, "shadowstyle.weight"),
+ new PropName(528, "shadowstyle.originx"),
+ new PropName(529, "shadowstyle.originy"),
+ new PropName(574, "shadowstyle.shadow"),
+ new PropName(575, "shadowstyle.shadowobsured"),
+ new PropName(576, "perspective.type"),
+ new PropName(577, "perspective.offsetx"),
+ new PropName(578, "perspective.offsety"),
+ new PropName(579, "perspective.scalextox"),
+ new PropName(580, "perspective.scaleytox"),
+ new PropName(581, "perspective.scalextoy"),
+ new PropName(582, "perspective.scaleytox"),
+ new PropName(583, "perspective.perspectivex"),
+ new PropName(584, "perspective.perspectivey"),
+ new PropName(585, "perspective.weight"),
+ new PropName(586, "perspective.originx"),
+ new PropName(587, "perspective.originy"),
+ new PropName(639, "perspective.perspectiveon"),
+ new PropName(640, "3d.specularamount"),
+ new PropName(661, "3d.diffuseamount"),
+ new PropName(662, "3d.shininess"),
+ new PropName(663, "3d.edgethickness"),
+ new PropName(664, "3d.extrudeforward"),
+ new PropName(665, "3d.extrudebackward"),
+ new PropName(666, "3d.extrudeplane"),
+ new PropName(667, "3d.extrusioncolor"),
+ new PropName(648, "3d.crmod"),
+ new PropName(700, "3d.3deffect"),
+ new PropName(701, "3d.metallic"),
+ new PropName(702, "3d.useextrusioncolor"),
+ new PropName(703, "3d.lightface"),
+ new PropName(704, "3dstyle.yrotationangle"),
+ new PropName(705, "3dstyle.xrotationangle"),
+ new PropName(706, "3dstyle.rotationaxisx"),
+ new PropName(707, "3dstyle.rotationaxisy"),
+ new PropName(708, "3dstyle.rotationaxisz"),
+ new PropName(709, "3dstyle.rotationangle"),
+ new PropName(710, "3dstyle.rotationcenterx"),
+ new PropName(711, "3dstyle.rotationcentery"),
+ new PropName(712, "3dstyle.rotationcenterz"),
+ new PropName(713, "3dstyle.rendermode"),
+ new PropName(714, "3dstyle.tolerance"),
+ new PropName(715, "3dstyle.xviewpoint"),
+ new PropName(716, "3dstyle.yviewpoint"),
+ new PropName(717, "3dstyle.zviewpoint"),
+ new PropName(718, "3dstyle.originx"),
+ new PropName(719, "3dstyle.originy"),
+ new PropName(720, "3dstyle.skewangle"),
+ new PropName(721, "3dstyle.skewamount"),
+ new PropName(722, "3dstyle.ambientintensity"),
+ new PropName(723, "3dstyle.keyx"),
+ new PropName(724, "3dstyle.keyy"),
+ new PropName(725, "3dstyle.keyz"),
+ new PropName(726, "3dstyle.keyintensity"),
+ new PropName(727, "3dstyle.fillx"),
+ new PropName(728, "3dstyle.filly"),
+ new PropName(729, "3dstyle.fillz"),
+ new PropName(730, "3dstyle.fillintensity"),
+ new PropName(763, "3dstyle.constrainrotation"),
+ new PropName(764, "3dstyle.rotationcenterauto"),
+ new PropName(765, "3dstyle.parallel"),
+ new PropName(766, "3dstyle.keyharsh"),
+ new PropName(767, "3dstyle.fillharsh"),
+ new PropName(769, "shape.master"),
+ new PropName(771, "shape.connectorstyle"),
+ new PropName(772, "shape.blackandwhitesettings"),
+ new PropName(773, "shape.wmodepurebw"),
+ new PropName(774, "shape.wmodebw"),
+ new PropName(826, "shape.oleicon"),
+ new PropName(827, "shape.preferrelativeresize"),
+ new PropName(828, "shape.lockshapetype"),
+ new PropName(830, "shape.deleteattachedobject"),
+ new PropName(831, "shape.backgroundshape"),
+ new PropName(832, "callout.callouttype"),
+ new PropName(833, "callout.xycalloutgap"),
+ new PropName(834, "callout.calloutangle"),
+ new PropName(835, "callout.calloutdroptype"),
+ new PropName(836, "callout.calloutdropspecified"),
+ new PropName(837, "callout.calloutlengthspecified"),
+ new PropName(889, "callout.iscallout"),
+ new PropName(890, "callout.calloutaccentbar"),
+ new PropName(891, "callout.callouttextborder"),
+ new PropName(892, "callout.calloutminusx"),
+ new PropName(893, "callout.calloutminusy"),
+ new PropName(894, "callout.dropauto"),
+ new PropName(895, "callout.lengthspecified"),
+ new PropName(896, "groupshape.shapename"),
+ new PropName(897, "groupshape.description"),
+ new PropName(898, "groupshape.hyperlink"),
+ new PropName(899, "groupshape.wrappolygonvertices"),
+ new PropName(900, "groupshape.wrapdistleft"),
+ new PropName(901, "groupshape.wrapdisttop"),
+ new PropName(902, "groupshape.wrapdistright"),
+ new PropName(903, "groupshape.wrapdistbottom"),
+ new PropName(904, "groupshape.regroupid"),
+ new PropName(953, "groupshape.editedwrap"),
+ new PropName(954, "groupshape.behinddocument"),
+ new PropName(955, "groupshape.ondblclicknotify"),
+ new PropName(956, "groupshape.isbutton"),
+ new PropName(957, "groupshape.1dadjustment"),
+ new PropName(958, "groupshape.hidden"),
+ new PropName(959, "groupshape.print"),
+ };
+
+ for ( int i = 0; i < props.length; i++ )
+ {
+ if (props[i].id == propertyId)
+ {
+ return props[i].name;
+ }
+ }
+
+ return "unknown property";
+ }
+
+ /**
+ * Returns the blip description given a blip id.
+ *
+ * @param b blip id
+ * @return A description.
+ */
+ private String getBlipType( byte b )
+ {
+ switch ( b )
+ {
+ case 0:
+ return " ERROR";
+ case 1:
+ return " UNKNOWN";
+ case 2:
+ return " EMF";
+ case 3:
+ return " WMF";
+ case 4:
+ return " PICT";
+ case 5:
+ return " JPEG";
+ case 6:
+ return " PNG";
+ case 7:
+ return " DIB";
+ default:
+ if ( b < 32 )
+ return " NotKnown";
+ else
+ return " Client";
+ }
+ }
+
+ /**
+ * Straight conversion from OO. Converts a type of float.
+ */
+ private String dec1616( int n32 )
+ {
+ String result = "";
+ result += (short) ( n32 >> 16 );
+ result += '.';
+ result += (short) ( n32 & (short) 0xFFFF );
+ return result;
+ }
+
+ /**
+ * Dumps out a hex value by reading from a input stream.
+ *
+ * @param bytes How many bytes this hex value consists of.
+ * @param in The stream to read the hex value from.
+ * @param out The stream to write the nicely formatted hex value to.
+ */
+ private void outHex( int bytes, InputStream in, PrintStream out ) throws IOException, LittleEndian.BufferUnderrunException
+ {
+ switch ( bytes )
+ {
+ case 1:
+ out.print( HexDump.toHex( (byte) in.read() ) );
+ break;
+ case 2:
+ out.print( HexDump.toHex( LittleEndian.readShort( in ) ) );
+ break;
+ case 4:
+ out.print( HexDump.toHex( LittleEndian.readInt( in ) ) );
+ break;
+ default:
+ throw new IOException( "Unable to output variable of that width" );
+ }
+ }
+
+ /**
+ * A simple test stub.
+ */
+ public static void main( String[] args ) throws IOException
+ {
+ String dump =
+ "0F 00 00 F0 89 07 00 00 00 00 06 F0 18 00 00 00 " +
+ "05 04 00 00 02 00 00 00 05 00 00 00 01 00 00 00 " +
+ "01 00 00 00 05 00 00 00 4F 00 01 F0 2F 07 00 00 " +
+ "42 00 07 F0 B7 01 00 00 03 04 3F 14 AE 6B 0F 65 " +
+ "B0 48 BF 5E 94 63 80 E8 91 73 FF 00 93 01 00 00 " +
+ "01 00 00 00 00 00 00 00 00 00 FF FF 20 54 1C F0 " +
+ "8B 01 00 00 3F 14 AE 6B 0F 65 B0 48 BF 5E 94 63 " +
+ "80 E8 91 73 92 0E 00 00 00 00 00 00 00 00 00 00 " +
+ "D1 07 00 00 DD 05 00 00 4A AD 6F 00 8A C5 53 00 " +
+ "59 01 00 00 00 FE 78 9C E3 9B C4 00 04 AC 77 D9 " +
+ "2F 32 08 32 FD E7 61 F8 FF 0F C8 FD 05 C5 30 19 " +
+ "10 90 63 90 FA 0F 06 0C 8C 0C 5C 70 19 43 30 EB " +
+ "0E FB 05 86 85 0C DB 18 58 80 72 8C 70 16 0B 83 " +
+ "05 56 51 29 88 C9 60 D9 69 0C 6C 20 26 23 03 C8 " +
+ "74 B0 A8 0E 03 07 FB 45 56 C7 A2 CC C4 1C 06 66 " +
+ "A0 0D 2C 40 39 5E 86 4C 06 3D A0 4E 10 D0 60 D9 " +
+ "C8 58 CC E8 CF B0 80 61 3A 8A 7E 0D C6 23 AC 4F " +
+ "E0 E2 98 B6 12 2B 06 73 9D 12 E3 52 56 59 F6 08 " +
+ "8A CC 52 66 A3 50 FF 96 2B 94 E9 DF 4C A1 FE 2D " +
+ "3A 03 AB 9F 81 C2 F0 A3 54 BF 0F 85 EE A7 54 FF " +
+ "40 FB 7F A0 E3 9F D2 F4 4F 71 FE 19 58 FF 2B 31 " +
+ "7F 67 36 3B 25 4F 99 1B 4E 53 A6 5F 89 25 95 E9 " +
+ "C4 00 C7 83 12 F3 1F 26 35 4A D3 D2 47 0E 0A C3 " +
+ "41 8E C9 8A 52 37 DC 15 A1 D0 0D BC 4C 06 0C 2B " +
+ "28 2C 13 28 D4 EF 43 61 5A A0 58 3F 85 71 E0 4B " +
+ "69 9E 64 65 FE 39 C0 E5 22 30 1D 30 27 0E 74 3A " +
+ "18 60 FD 4A CC B1 2C 13 7D 07 36 2D 2A 31 85 B2 " +
+ "6A 0D 74 1D 1D 22 4D 99 FE 60 0A F5 9B EC 1C 58 " +
+ "FD 67 06 56 3F 38 0D 84 3C A5 30 0E 28 D3 AF C4 " +
+ "A4 CA FA 44 7A 0D 65 6E 60 7F 4D A1 1B 24 58 F7 " +
+ "49 AF A5 CC 0D CC DF 19 FE 03 00 F0 B1 25 4D 42 " +
+ "00 07 F0 E1 01 00 00 03 04 39 50 BE 98 B0 6F 57 " +
+ "24 31 70 5D 23 2F 9F 10 66 FF 00 BD 01 00 00 01 " +
+ "00 00 00 00 00 00 00 00 00 FF FF 20 54 1C F0 B5 " +
+ "01 00 00 39 50 BE 98 B0 6F 57 24 31 70 5D 23 2F " +
+ "9F 10 66 DA 03 00 00 00 00 00 00 00 00 00 00 D1 " +
+ "07 00 00 DD 05 00 00 4A AD 6F 00 8A C5 53 00 83 " +
+ "01 00 00 00 FE 78 9C A5 52 BF 4B 42 51 14 3E F7 " +
+ "DC 77 7A 16 45 48 8B 3C 48 A8 16 15 0D 6C 88 D0 " +
+ "04 C3 40 A3 32 1C 84 96 08 21 04 A1 C5 5C A2 35 " +
+ "82 C0 35 6A AB 1C 6A 6B A8 24 5A 83 68 08 84 84 " +
+ "96 A2 86 A0 7F C2 86 5E E7 5E F5 41 E4 10 BC 03 " +
+ "1F E7 FB F1 CE B9 F7 F1 9E 7C 05 2E 7A 37 9B E0 " +
+ "45 7B 10 EC 6F 96 5F 1D 74 13 55 7E B0 6C 5D 20 " +
+ "60 C0 49 A2 9A BD 99 4F 50 83 1B 30 38 13 0E 33 " +
+ "60 A6 A7 6B B5 37 EB F4 10 FA 14 15 A0 B6 6B 37 " +
+ "0C 1E B3 49 73 5B A5 C2 26 48 3E C1 E0 6C 08 4A " +
+ "30 C9 93 AA 02 B8 20 13 62 05 4E E1 E8 D7 7C C0 " +
+ "B8 14 95 5E BE B8 A7 CF 1E BE 55 2C 56 B9 78 DF " +
+ "08 7E 88 4C 27 FF 7B DB FF 7A DD B7 1A 17 67 34 " +
+ "6A AE BA DA 35 D1 E7 72 BE FE EC 6E FE DA E5 7C " +
+ "3D EC 7A DE 03 FD 50 06 0B 23 F2 0E F3 B2 A5 11 " +
+ "91 0D 4C B5 B5 F3 BF 94 C1 8F 24 F7 D9 6F 60 94 " +
+ "3B C9 9A F3 1C 6B E7 BB F0 2E 49 B2 25 2B C6 B1 " +
+ "EE 69 EE 15 63 4F 71 7D CE 85 CC C8 35 B9 C3 28 " +
+ "28 CE D0 5C 67 79 F2 4A A2 14 23 A4 38 43 73 9D " +
+ "2D 69 2F C1 08 31 9F C5 5C 9B EB 7B C5 69 19 B3 " +
+ "B4 81 F3 DC E3 B4 8E 8B CC B3 94 53 5A E7 41 2A " +
+ "63 9A AA 38 C5 3D 48 BB EC 57 59 6F 2B AD 73 1F " +
+ "1D 60 92 AE 70 8C BB 8F CE 31 C1 3C 49 27 4A EB " +
+ "DC A4 5B 8C D1 0B 0E 73 37 E9 11 A7 99 C7 E8 41 " +
+ "69 B0 7F 00 96 F2 A7 E8 42 00 07 F0 B4 01 00 00 " +
+ "03 04 1A BA F9 D6 A9 B9 3A 03 08 61 E9 90 FF 7B " +
+ "9E E6 FF 00 90 01 00 00 01 00 00 00 00 00 00 00 " +
+ "00 00 FF FF 20 54 1C F0 88 01 00 00 1A BA F9 D6 " +
+ "A9 B9 3A 03 08 61 E9 90 FF 7B 9E E6 12 0E 00 00 " +
+ "00 00 00 00 00 00 00 00 D1 07 00 00 DD 05 00 00 " +
+ "4A AD 6F 00 8A C5 53 00 56 01 00 00 00 FE 78 9C " +
+ "E3 13 62 00 02 D6 BB EC 17 19 04 99 FE F3 30 FC " +
+ "FF 07 E4 FE 82 62 98 0C 08 C8 31 48 FD 07 03 06 " +
+ "46 06 2E B8 8C 21 98 75 87 FD 02 C3 42 86 6D 0C " +
+ "2C 40 39 46 38 8B 85 C1 02 AB A8 14 C4 64 B0 EC " +
+ "34 06 36 10 93 91 01 64 3A 58 54 87 81 83 FD 22 " +
+ "AB 63 51 66 62 0E 03 33 D0 06 16 A0 1C 2F 43 26 " +
+ "83 1E 50 27 08 68 B0 6C 64 2C 66 F4 67 58 C0 30 " +
+ "1D 45 BF 06 E3 11 D6 27 70 71 4C 5B 89 15 83 B9 " +
+ "4E 89 71 29 AB 2C 7B 04 45 66 29 B3 51 A8 7F CB " +
+ "15 CA F4 6F A6 50 FF 16 9D 81 D5 CF 40 61 F8 51 " +
+ "AA DF 87 42 F7 53 AA 7F A0 FD 3F D0 F1 4F 69 FA " +
+ "A7 38 FF 0C AC FF 95 98 BF 33 9B 9D 92 A7 CC 0D " +
+ "A7 29 D3 AF C4 92 CA 74 62 80 E3 41 89 F9 0F 93 " +
+ "1A A5 69 E9 23 07 85 E1 20 C7 64 45 A9 1B EE 8A " +
+ "50 E8 06 5E 26 03 86 15 14 96 09 14 EA F7 A1 30 " +
+ "2D 50 AC 9F C2 38 F0 A5 34 4F B2 32 FF 1C E0 72 " +
+ "11 98 0E 98 13 07 38 1D 28 31 C7 B2 4C F4 1D D8 " +
+ "B4 A0 C4 14 CA AA 35 D0 75 64 88 34 65 FA 83 29 " +
+ "D4 6F B2 73 60 F5 9F A1 54 FF 0E CA D3 40 C8 53 " +
+ "0A E3 E0 09 85 6E 50 65 7D 22 BD 86 32 37 B0 BF " +
+ "A6 D0 0D 12 AC FB A4 D7 52 E6 06 E6 EF 0C FF 01 " +
+ "97 1D 12 C7 42 00 07 F0 C3 01 00 00 03 04 BA 4C " +
+ "B6 23 BA 8B 27 BE C8 55 59 86 24 9F 89 D4 FF 00 " +
+ "9F 01 00 00 01 00 00 00 00 00 00 00 00 00 FF FF " +
+ "20 54 1C F0 97 01 00 00 BA 4C B6 23 BA 8B 27 BE " +
+ "C8 55 59 86 24 9F 89 D4 AE 0E 00 00 00 00 00 00 " +
+ "00 00 00 00 D1 07 00 00 DD 05 00 00 4A AD 6F 00 " +
+ "8A C5 53 00 65 01 00 00 00 FE 78 9C E3 5B C7 00 " +
+ "04 AC 77 D9 2F 32 08 32 FD E7 61 F8 FF 0F C8 FD " +
+ "05 C5 30 19 10 90 63 90 FA 0F 06 0C 8C 0C 5C 70 " +
+ "19 43 30 EB 0E FB 05 86 85 0C DB 18 58 80 72 8C " +
+ "70 16 0B 83 05 56 51 29 88 C9 60 D9 69 0C 6C 20 " +
+ "26 23 03 C8 74 B0 A8 0E 03 07 FB 45 56 C7 A2 CC " +
+ "C4 1C 06 66 A0 0D 2C 40 39 5E 86 4C 06 3D A0 4E " +
+ "10 D0 60 99 C6 B8 98 D1 9F 61 01 C3 74 14 FD 1A " +
+ "8C 2B D8 84 B1 88 4B A5 A5 75 03 01 50 DF 59 46 " +
+ "77 46 0F A8 3C A6 AB 88 15 83 B9 5E 89 B1 8B D5 " +
+ "97 2D 82 22 B3 94 29 D5 BF E5 CA C0 EA DF AC 43 " +
+ "A1 FD 14 EA 67 A0 30 FC 28 D5 EF 43 A1 FB 7D 87 " +
+ "B8 FF 07 3A FE 07 3A FD 53 EA 7E 0A C3 4F 89 F9 " +
+ "0E 73 EA 69 79 CA DC 70 8A 32 FD 4A 2C 5E 4C DF " +
+ "87 7A 3C BC E0 A5 30 1E 3E 31 C5 33 AC A0 30 2F " +
+ "52 A8 DF 87 C2 30 A4 54 3F A5 65 19 85 65 A9 12 " +
+ "D3 2B 16 0D 8A CB 13 4A F3 E3 27 E6 09 03 9D 0E " +
+ "06 58 BF 12 B3 13 CB C1 01 4E 8B 4A 4C 56 AC 91 " +
+ "03 5D 37 86 48 53 A6 3F 98 42 FD 26 3B 07 56 FF " +
+ "99 1D 14 EA A7 CC 7E 70 1A 08 79 42 61 1C 3C A5 " +
+ "D0 0D 9C 6C C2 32 6B 29 73 03 DB 6B CA DC C0 F8 " +
+ "97 F5 AD CC 1A CA DC C0 F4 83 32 37 B0 A4 30 CE " +
+ "FC C7 48 99 1B FE 33 32 FC 07 00 6C CC 2E 23 33 " +
+ "00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 " +
+ "00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 " +
+ "00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 " +
+ "10 ";
+
+ // Decode the stream to bytes
+ byte[] bytes = HexRead.readData( new ByteArrayInputStream( dump.getBytes() ), -1 );
+ // Create a new instance of the escher dumper
+ EscherDump dumper = new EscherDump();
+ // Dump the contents of scher to screen.
+// dumper.dumpOld( bytes.length, new ByteArrayInputStream( bytes ), System.out );
+ dumper.dump(bytes, 0, bytes.length, System.out);
+
+ }
+
+ public void dump( int recordSize, byte[] data, PrintStream out ) throws IOException, LittleEndian.BufferUnderrunException
+ {
+// ByteArrayInputStream is = new ByteArrayInputStream( data );
+// dump( recordSize, is, out );
+ dump( data, 0, recordSize, System.out );
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/EscherOptRecord.java b/src/java/org/apache/poi/ddf/EscherOptRecord.java
new file mode 100644
index 0000000000..4efab8ba3d
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherOptRecord.java
@@ -0,0 +1,174 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.HexDump;
+
+import java.util.*;
+import java.io.IOException;
+
+/**
+ * The opt record is used to store property values for a shape. It is the key to determining
+ * the attributes of a shape. Properties can be of two types: simple or complex. Simple types
+ * are fixed length. Complex properties are variable length.
+ *
+ * @author Glen Stampoultzis
+ */
+public class EscherOptRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF00B;
+ public static final String RECORD_DESCRIPTION = "msofbtOPT";
+
+ private List properties = new ArrayList();
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+
+ EscherPropertyFactory f = new EscherPropertyFactory();
+ properties = f.createProperties( data, pos, getInstance() );
+ return bytesRemaining + 8;
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ *
+ * @return The number of bytes written.
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ LittleEndian.putInt( data, offset + 4, getPropertiesSize() );
+ int pos = offset + 8;
+ for ( Iterator iterator = properties.iterator(); iterator.hasNext(); )
+ {
+ EscherProperty escherProperty = (EscherProperty) iterator.next();
+ pos += escherProperty.serializeSimplePart( data, pos );
+ }
+ for ( Iterator iterator = properties.iterator(); iterator.hasNext(); )
+ {
+ EscherProperty escherProperty = (EscherProperty) iterator.next();
+ pos += escherProperty.serializeComplexPart( data, pos );
+ }
+ listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
+ return pos - offset;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + getPropertiesSize();
+ }
+
+ /**
+ * Automatically recalculate the correct option
+ */
+ public short getOptions()
+ {
+ setOptions( (short) ( ( properties.size() << 4 ) | 0x3 ) );
+ return super.getOptions();
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "Opt";
+ }
+
+ private int getPropertiesSize()
+ {
+ int totalSize = 0;
+ for ( Iterator iterator = properties.iterator(); iterator.hasNext(); )
+ {
+ EscherProperty escherProperty = (EscherProperty) iterator.next();
+ totalSize += escherProperty.getPropertySize();
+ }
+ return totalSize;
+ }
+
+ /**
+ * Retrieve the string representation of this record.
+ */
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separator" );
+ StringBuffer propertiesBuf = new StringBuffer();
+ for ( Iterator iterator = properties.iterator(); iterator.hasNext(); )
+ propertiesBuf.append( " "
+ + iterator.next().toString()
+ + nl );
+
+ return "org.apache.poi.ddf.EscherOptRecord:" + nl +
+ " isContainer: " + isContainerRecord() + nl +
+ " options: 0x" + HexDump.toHex( getOptions() ) + nl +
+ " recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
+ " numchildren: " + getChildRecords().size() + nl +
+ " properties:" + nl +
+ propertiesBuf.toString();
+ }
+
+ /**
+ * The list of properties stored by this record.
+ */
+ public List getEscherProperties()
+ {
+ return properties;
+ }
+
+ /**
+ * The list of properties stored by this record.
+ */
+ public EscherProperty getEscherProperty( int index )
+ {
+ return (EscherProperty) properties.get( index );
+ }
+
+ /**
+ * Add a property to this record.
+ */
+ public void addEscherProperty( EscherProperty prop )
+ {
+ properties.add( prop );
+ }
+
+ /**
+ * Records should be sorted by property number before being stored.
+ */
+ public void sortProperties()
+ {
+ Collections.sort( properties, new Comparator()
+ {
+ public int compare( Object o1, Object o2 )
+ {
+ EscherProperty p1 = (EscherProperty) o1;
+ EscherProperty p2 = (EscherProperty) o2;
+ return new Short( p1.getPropertyNumber() ).compareTo( new Short( p2.getPropertyNumber() ) );
+ }
+ } );
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherProperties.java b/src/java/org/apache/poi/ddf/EscherProperties.java
new file mode 100644
index 0000000000..ea1c8745c5
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherProperties.java
@@ -0,0 +1,607 @@
+package org.apache.poi.ddf;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides a list of all known escher properties including the description and
+ * type.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherProperties
+{
+
+ // Property constants
+ public static final short TRANSFORM__ROTATION = 4;
+ public static final short PROTECTION__LOCKROTATION = 119;
+ public static final short PROTECTION__LOCKASPECTRATIO = 120;
+ public static final short PROTECTION__LOCKPOSITION = 121;
+ public static final short PROTECTION__LOCKAGAINSTSELECT = 122;
+ public static final short PROTECTION__LOCKCROPPING = 123;
+ public static final short PROTECTION__LOCKVERTICES = 124;
+ public static final short PROTECTION__LOCKTEXT = 125;
+ public static final short PROTECTION__LOCKADJUSTHANDLES = 126;
+ public static final short PROTECTION__LOCKAGAINSTGROUPING = 127;
+ public static final short TEXT__TEXTID = 128;
+ public static final short TEXT__TEXTLEFT = 129;
+ public static final short TEXT__TEXTTOP = 130;
+ public static final short TEXT__TEXTRIGHT = 131;
+ public static final short TEXT__TEXTBOTTOM = 132;
+ public static final short TEXT__WRAPTEXT = 133;
+ public static final short TEXT__SCALETEXT = 134;
+ public static final short TEXT__ANCHORTEXT = 135;
+ public static final short TEXT__TEXTFLOW = 136;
+ public static final short TEXT__FONTROTATION = 137;
+ public static final short TEXT__IDOFNEXTSHAPE = 138;
+ public static final short TEXT__BIDIR = 139;
+ public static final short TEXT__SINGLECLICKSELECTS = 187;
+ public static final short TEXT__USEHOSTMARGINS = 188;
+ public static final short TEXT__ROTATETEXTWITHSHAPE = 189;
+ public static final short TEXT__SIZESHAPETOFITTEXT = 190;
+ public static final short TEXT__SIZE_TEXT_TO_FIT_SHAPE = 191 ;
+ public static final short GEOTEXT__UNICODE = 192;
+ public static final short GEOTEXT__RTFTEXT = 193;
+ public static final short GEOTEXT__ALIGNMENTONCURVE = 194;
+ public static final short GEOTEXT__DEFAULTPOINTSIZE = 195;
+ public static final short GEOTEXT__TEXTSPACING = 196;
+ public static final short GEOTEXT__FONTFAMILYNAME = 197;
+ public static final short GEOTEXT__REVERSEROWORDER = 240;
+ public static final short GEOTEXT__HASTEXTEFFECT = 241;
+ public static final short GEOTEXT__ROTATECHARACTERS = 242;
+ public static final short GEOTEXT__KERNCHARACTERS = 243;
+ public static final short GEOTEXT__TIGHTORTRACK = 244;
+ public static final short GEOTEXT__STRETCHTOFITSHAPE = 245;
+ public static final short GEOTEXT__CHARBOUNDINGBOX = 246;
+ public static final short GEOTEXT__SCALETEXTONPATH = 247;
+ public static final short GEOTEXT__STRETCHCHARHEIGHT = 248;
+ public static final short GEOTEXT__NOMEASUREALONGPATH = 249;
+ public static final short GEOTEXT__BOLDFONT = 250;
+ public static final short GEOTEXT__ITALICFONT = 251;
+ public static final short GEOTEXT__UNDERLINEFONT = 252;
+ public static final short GEOTEXT__SHADOWFONT = 253;
+ public static final short GEOTEXT__SMALLCAPSFONT = 254;
+ public static final short GEOTEXT__STRIKETHROUGHFONT = 255;
+ public static final short BLIP__CROPFROMTOP = 256;
+ public static final short BLIP__CROPFROMBOTTOM = 257;
+ public static final short BLIP__CROPFROMLEFT = 258;
+ public static final short BLIP__CROPFROMRIGHT = 259;
+ public static final short BLIP__BLIPTODISPLAY = 260;
+ public static final short BLIP__BLIPFILENAME = 261;
+ public static final short BLIP__BLIPFLAGS = 262;
+ public static final short BLIP__TRANSPARENTCOLOR = 263;
+ public static final short BLIP__CONTRASTSETTING = 264;
+ public static final short BLIP__BRIGHTNESSSETTING = 265;
+ public static final short BLIP__GAMMA = 266;
+ public static final short BLIP__PICTUREID = 267;
+ public static final short BLIP__DOUBLEMOD = 268;
+ public static final short BLIP__PICTUREFILLMOD = 269;
+ public static final short BLIP__PICTURELINE = 270;
+ public static final short BLIP__PRINTBLIP = 271;
+ public static final short BLIP__PRINTBLIPFILENAME = 272;
+ public static final short BLIP__PRINTFLAGS = 273;
+ public static final short BLIP__NOHITTESTPICTURE = 316;
+ public static final short BLIP__PICTUREGRAY = 317;
+ public static final short BLIP__PICTUREBILEVEL = 318;
+ public static final short BLIP__PICTUREACTIVE = 319;
+ public static final short GEOMETRY__LEFT = 320;
+ public static final short GEOMETRY__TOP = 321;
+ public static final short GEOMETRY__RIGHT = 322;
+ public static final short GEOMETRY__BOTTOM = 323;
+ public static final short GEOMETRY__SHAPEPATH = 324;
+ public static final short GEOMETRY__VERTICES = 325;
+ public static final short GEOMETRY__SEGMENTINFO = 326;
+ public static final short GEOMETRY__ADJUSTVALUE = 327;
+ public static final short GEOMETRY__ADJUST2VALUE = 328;
+ public static final short GEOMETRY__ADJUST3VALUE = 329;
+ public static final short GEOMETRY__ADJUST4VALUE = 330;
+ public static final short GEOMETRY__ADJUST5VALUE = 331;
+ public static final short GEOMETRY__ADJUST6VALUE = 332;
+ public static final short GEOMETRY__ADJUST7VALUE = 333;
+ public static final short GEOMETRY__ADJUST8VALUE = 334;
+ public static final short GEOMETRY__ADJUST9VALUE = 335;
+ public static final short GEOMETRY__ADJUST10VALUE = 336;
+ public static final short GEOMETRY__SHADOWok = 378;
+ public static final short GEOMETRY__3DOK = 379;
+ public static final short GEOMETRY__LINEOK = 380;
+ public static final short GEOMETRY__GEOTEXTOK = 381;
+ public static final short GEOMETRY__FILLSHADESHAPEOK = 382;
+ public static final short GEOMETRY__FILLOK = 383;
+ public static final short FILL__FILLTYPE = 384;
+ public static final short FILL__FILLCOLOR = 385 ;
+ public static final short FILL__FILLOPACITY = 386;
+ public static final short FILL__FILLBACKCOLOR = 387;
+ public static final short FILL__BACKOPACITY = 388;
+ public static final short FILL__CRMOD = 389;
+ public static final short FILL__PATTERNTEXTURE = 390;
+ public static final short FILL__BLIPFILENAME = 391;
+ public static final short FILL__BLIPFLAGS = 392;
+ public static final short FILL__WIDTH = 393;
+ public static final short FILL__HEIGHT = 394;
+ public static final short FILL__ANGLE = 395;
+ public static final short FILL__FOCUS = 396;
+ public static final short FILL__TOLEFT = 397;
+ public static final short FILL__TOTOP = 398;
+ public static final short FILL__TORIGHT = 399;
+ public static final short FILL__TOBOTTOM = 400;
+ public static final short FILL__RECTLEFT = 401;
+ public static final short FILL__RECTTOP = 402;
+ public static final short FILL__RECTRIGHT = 403;
+ public static final short FILL__RECTBOTTOM = 404;
+ public static final short FILL__DZTYPE = 405;
+ public static final short FILL__SHADEPRESET = 406;
+ public static final short FILL__SHADECOLORS = 407;
+ public static final short FILL__ORIGINX = 408;
+ public static final short FILL__ORIGINY = 409;
+ public static final short FILL__SHAPEORIGINX = 410;
+ public static final short FILL__SHAPEORIGINY = 411;
+ public static final short FILL__SHADETYPE = 412;
+ public static final short FILL__FILLED = 443;
+ public static final short FILL__HITTESTFILL = 444;
+ public static final short FILL__SHAPE = 445;
+ public static final short FILL__USERECT = 446;
+ public static final short FILL__NOFILLHITTEST = 447;
+ public static final short LINESTYLE__COLOR = 448 ;
+ public static final short LINESTYLE__OPACITY = 449;
+ public static final short LINESTYLE__BACKCOLOR = 450;
+ public static final short LINESTYLE__CRMOD = 451;
+ public static final short LINESTYLE__LINETYPE = 452;
+ public static final short LINESTYLE__FILLBLIP = 453;
+ public static final short LINESTYLE__FILLBLIPNAME = 454;
+ public static final short LINESTYLE__FILLBLIPFLAGS = 455;
+ public static final short LINESTYLE__FILLWIDTH = 456;
+ public static final short LINESTYLE__FILLHEIGHT = 457;
+ public static final short LINESTYLE__FILLDZTYPE = 458;
+ public static final short LINESTYLE__LINEWIDTH = 459;
+ public static final short LINESTYLE__LINEMITERLIMIT = 460;
+ public static final short LINESTYLE__LINESTYLE = 461;
+ public static final short LINESTYLE__LINEDASHING = 462;
+ public static final short LINESTYLE__LINEDASHSTYLE = 463;
+ public static final short LINESTYLE__LINESTARTARROWHEAD = 464;
+ public static final short LINESTYLE__LINEENDARROWHEAD = 465;
+ public static final short LINESTYLE__LINESTARTARROWWIDTH = 466;
+ public static final short LINESTYLE__LINEESTARTARROWLENGTH = 467;
+ public static final short LINESTYLE__LINEENDARROWWIDTH = 468;
+ public static final short LINESTYLE__LINEENDARROWLENGTH = 469;
+ public static final short LINESTYLE__LINEJOINSTYLE = 470;
+ public static final short LINESTYLE__LINEENDCAPSTYLE = 471;
+ public static final short LINESTYLE__ARROWHEADSOK = 507;
+ public static final short LINESTYLE__ANYLINE = 508;
+ public static final short LINESTYLE__HITLINETEST = 509;
+ public static final short LINESTYLE__LINEFILLSHAPE = 510;
+ public static final short LINESTYLE__NOLINEDRAWDASH = 511;
+ public static final short SHADOWSTYLE__TYPE = 512;
+ public static final short SHADOWSTYLE__COLOR = 513;
+ public static final short SHADOWSTYLE__HIGHLIGHT = 514;
+ public static final short SHADOWSTYLE__CRMOD = 515;
+ public static final short SHADOWSTYLE__OPACITY = 516;
+ public static final short SHADOWSTYLE__OFFSETX = 517;
+ public static final short SHADOWSTYLE__OFFSETY = 518;
+ public static final short SHADOWSTYLE__SECONDOFFSETX = 519;
+ public static final short SHADOWSTYLE__SECONDOFFSETY = 520;
+ public static final short SHADOWSTYLE__SCALEXTOX = 521;
+ public static final short SHADOWSTYLE__SCALEYTOX = 522;
+ public static final short SHADOWSTYLE__SCALEXTOY = 523;
+ public static final short SHADOWSTYLE__SCALEYTOY = 524;
+ public static final short SHADOWSTYLE__PERSPECTIVEX = 525;
+ public static final short SHADOWSTYLE__PERSPECTIVEY = 526;
+ public static final short SHADOWSTYLE__WEIGHT = 527;
+ public static final short SHADOWSTYLE__ORIGINX = 528;
+ public static final short SHADOWSTYLE__ORIGINY = 529;
+ public static final short SHADOWSTYLE__SHADOW = 574;
+ public static final short SHADOWSTYLE__SHADOWOBSURED = 575;
+ public static final short PERSPECTIVE__TYPE = 576;
+ public static final short PERSPECTIVE__OFFSETX = 577;
+ public static final short PERSPECTIVE__OFFSETY = 578;
+ public static final short PERSPECTIVE__SCALEXTOX = 579;
+ public static final short PERSPECTIVE__SCALEYTOX = 580;
+ public static final short PERSPECTIVE__SCALEXTOY = 581;
+ public static final short PERSPECTIVE__SCALEYTOY = 582;
+ public static final short PERSPECTIVE__PERSPECTIVEX = 583;
+ public static final short PERSPECTIVE__PERSPECTIVEY = 584;
+ public static final short PERSPECTIVE__WEIGHT = 585;
+ public static final short PERSPECTIVE__ORIGINX = 586;
+ public static final short PERSPECTIVE__ORIGINY = 587;
+ public static final short PERSPECTIVE__PERSPECTIVEON = 639;
+ public static final short THREED__SPECULARAMOUNT = 640;
+ public static final short THREED__DIFFUSEAMOUNT = 661;
+ public static final short THREED__SHININESS = 662;
+ public static final short THREED__EDGETHICKNESS = 663;
+ public static final short THREED__EXTRUDEFORWARD = 664;
+ public static final short THREED__EXTRUDEBACKWARD = 665;
+ public static final short THREED__EXTRUDEPLANE = 666;
+ public static final short THREED__EXTRUSIONCOLOR = 667;
+ public static final short THREED__CRMOD = 648;
+ public static final short THREED__3DEFFECT = 700;
+ public static final short THREED__METALLIC = 701;
+ public static final short THREED__USEEXTRUSIONCOLOR = 702;
+ public static final short THREED__LIGHTFACE = 703;
+ public static final short THREEDSTYLE__YROTATIONANGLE = 704;
+ public static final short THREEDSTYLE__XROTATIONANGLE = 705;
+ public static final short THREEDSTYLE__ROTATIONAXISX = 706;
+ public static final short THREEDSTYLE__ROTATIONAXISY = 707;
+ public static final short THREEDSTYLE__ROTATIONAXISZ = 708;
+ public static final short THREEDSTYLE__ROTATIONANGLE = 709;
+ public static final short THREEDSTYLE__ROTATIONCENTERX = 710;
+ public static final short THREEDSTYLE__ROTATIONCENTERY = 711;
+ public static final short THREEDSTYLE__ROTATIONCENTERZ = 712;
+ public static final short THREEDSTYLE__RENDERMODE = 713;
+ public static final short THREEDSTYLE__TOLERANCE = 714;
+ public static final short THREEDSTYLE__XVIEWPOINT = 715;
+ public static final short THREEDSTYLE__YVIEWPOINT = 716;
+ public static final short THREEDSTYLE__ZVIEWPOINT = 717;
+ public static final short THREEDSTYLE__ORIGINX = 718;
+ public static final short THREEDSTYLE__ORIGINY = 719;
+ public static final short THREEDSTYLE__SKEWANGLE = 720;
+ public static final short THREEDSTYLE__SKEWAMOUNT = 721;
+ public static final short THREEDSTYLE__AMBIENTINTENSITY = 722;
+ public static final short THREEDSTYLE__KEYX = 723;
+ public static final short THREEDSTYLE__KEYY = 724;
+ public static final short THREEDSTYLE__KEYZ = 725;
+ public static final short THREEDSTYLE__KEYINTENSITY = 726;
+ public static final short THREEDSTYLE__FILLX = 727;
+ public static final short THREEDSTYLE__FILLY = 728;
+ public static final short THREEDSTYLE__FILLZ = 729;
+ public static final short THREEDSTYLE__FILLINTENSITY = 730;
+ public static final short THREEDSTYLE__CONSTRAINROTATION = 763;
+ public static final short THREEDSTYLE__ROTATIONCENTERAUTO = 764;
+ public static final short THREEDSTYLE__PARALLEL = 765;
+ public static final short THREEDSTYLE__KEYHARSH = 766;
+ public static final short THREEDSTYLE__FILLHARSH = 767;
+ public static final short SHAPE__MASTER = 769;
+ public static final short SHAPE__CONNECTORSTYLE = 771;
+ public static final short SHAPE__BLACKANDWHITESETTINGS = 772;
+ public static final short SHAPE__WMODEPUREBW = 773;
+ public static final short SHAPE__WMODEBW = 774;
+ public static final short SHAPE__OLEICON = 826;
+ public static final short SHAPE__PREFERRELATIVERESIZE = 827;
+ public static final short SHAPE__LOCKSHAPETYPE = 828;
+ public static final short SHAPE__DELETEATTACHEDOBJECT = 830;
+ public static final short SHAPE__BACKGROUNDSHAPE = 831;
+ public static final short CALLOUT__CALLOUTTYPE = 832;
+ public static final short CALLOUT__XYCALLOUTGAP = 833;
+ public static final short CALLOUT__CALLOUTANGLE = 834;
+ public static final short CALLOUT__CALLOUTDROPTYPE = 835;
+ public static final short CALLOUT__CALLOUTDROPSPECIFIED = 836;
+ public static final short CALLOUT__CALLOUTLENGTHSPECIFIED = 837;
+ public static final short CALLOUT__ISCALLOUT = 889;
+ public static final short CALLOUT__CALLOUTACCENTBAR = 890;
+ public static final short CALLOUT__CALLOUTTEXTBORDER = 891;
+ public static final short CALLOUT__CALLOUTMINUSX = 892;
+ public static final short CALLOUT__CALLOUTMINUSY = 893;
+ public static final short CALLOUT__DROPAUTO = 894;
+ public static final short CALLOUT__LENGTHSPECIFIED = 895;
+ public static final short GROUPSHAPE__SHAPENAME = 896;
+ public static final short GROUPSHAPE__DESCRIPTION = 897;
+ public static final short GROUPSHAPE__HYPERLINK = 898;
+ public static final short GROUPSHAPE__WRAPPOLYGONVERTICES = 899;
+ public static final short GROUPSHAPE__WRAPDISTLEFT = 900;
+ public static final short GROUPSHAPE__WRAPDISTTOP = 901;
+ public static final short GROUPSHAPE__WRAPDISTRIGHT = 902;
+ public static final short GROUPSHAPE__WRAPDISTBOTTOM = 903;
+ public static final short GROUPSHAPE__REGROUPID = 904;
+ public static final short GROUPSHAPE__EDITEDWRAP = 953;
+ public static final short GROUPSHAPE__BEHINDDOCUMENT = 954;
+ public static final short GROUPSHAPE__ONDBLCLICKNOTIFY = 955;
+ public static final short GROUPSHAPE__ISBUTTON = 956;
+ public static final short GROUPSHAPE__1DADJUSTMENT = 957;
+ public static final short GROUPSHAPE__HIDDEN = 958;
+ public static final short GROUPSHAPE__PRINT = 959;
+
+
+ private static Map properties;
+
+ private static void initProps()
+ {
+ if ( properties == null )
+ {
+ properties = new HashMap();
+ addProp( TRANSFORM__ROTATION, data( "transform.rotation" ) );
+ addProp( PROTECTION__LOCKROTATION , data( "protection.lockrotation" ) );
+ addProp( PROTECTION__LOCKASPECTRATIO , data( "protection.lockaspectratio" ) );
+ addProp( PROTECTION__LOCKPOSITION , data( "protection.lockposition" ) );
+ addProp( PROTECTION__LOCKAGAINSTSELECT , data( "protection.lockagainstselect" ) );
+ addProp( PROTECTION__LOCKCROPPING , data( "protection.lockcropping" ) );
+ addProp( PROTECTION__LOCKVERTICES , data( "protection.lockvertices" ) );
+ addProp( PROTECTION__LOCKTEXT , data( "protection.locktext" ) );
+ addProp( PROTECTION__LOCKADJUSTHANDLES , data( "protection.lockadjusthandles" ) );
+ addProp( PROTECTION__LOCKAGAINSTGROUPING , data( "protection.lockagainstgrouping", EscherPropertyMetaData.TYPE_BOOLEAN ) );
+ addProp( TEXT__TEXTID , data( "text.textid" ) );
+ addProp( TEXT__TEXTLEFT , data( "text.textleft" ) );
+ addProp( TEXT__TEXTTOP , data( "text.texttop" ) );
+ addProp( TEXT__TEXTRIGHT , data( "text.textright" ) );
+ addProp( TEXT__TEXTBOTTOM , data( "text.textbottom" ) );
+ addProp( TEXT__WRAPTEXT , data( "text.wraptext" ) );
+ addProp( TEXT__SCALETEXT , data( "text.scaletext" ) );
+ addProp( TEXT__ANCHORTEXT , data( "text.anchortext" ) );
+ addProp( TEXT__TEXTFLOW , data( "text.textflow" ) );
+ addProp( TEXT__FONTROTATION , data( "text.fontrotation" ) );
+ addProp( TEXT__IDOFNEXTSHAPE , data( "text.idofnextshape" ) );
+ addProp( TEXT__BIDIR , data( "text.bidir" ) );
+ addProp( TEXT__SINGLECLICKSELECTS , data( "text.singleclickselects" ) );
+ addProp( TEXT__USEHOSTMARGINS , data( "text.usehostmargins" ) );
+ addProp( TEXT__ROTATETEXTWITHSHAPE , data( "text.rotatetextwithshape" ) );
+ addProp( TEXT__SIZESHAPETOFITTEXT , data( "text.sizeshapetofittext" ) );
+ addProp( TEXT__SIZE_TEXT_TO_FIT_SHAPE, data( "text.sizetexttofitshape", EscherPropertyMetaData.TYPE_BOOLEAN ) );
+ addProp( GEOTEXT__UNICODE , data( "geotext.unicode" ) );
+ addProp( GEOTEXT__RTFTEXT , data( "geotext.rtftext" ) );
+ addProp( GEOTEXT__ALIGNMENTONCURVE , data( "geotext.alignmentoncurve" ) );
+ addProp( GEOTEXT__DEFAULTPOINTSIZE , data( "geotext.defaultpointsize" ) );
+ addProp( GEOTEXT__TEXTSPACING , data( "geotext.textspacing" ) );
+ addProp( GEOTEXT__FONTFAMILYNAME , data( "geotext.fontfamilyname" ) );
+ addProp( GEOTEXT__REVERSEROWORDER , data( "geotext.reverseroworder" ) );
+ addProp( GEOTEXT__HASTEXTEFFECT , data( "geotext.hastexteffect" ) );
+ addProp( GEOTEXT__ROTATECHARACTERS , data( "geotext.rotatecharacters" ) );
+ addProp( GEOTEXT__KERNCHARACTERS , data( "geotext.kerncharacters" ) );
+ addProp( GEOTEXT__TIGHTORTRACK , data( "geotext.tightortrack" ) );
+ addProp( GEOTEXT__STRETCHTOFITSHAPE , data( "geotext.stretchtofitshape" ) );
+ addProp( GEOTEXT__CHARBOUNDINGBOX , data( "geotext.charboundingbox" ) );
+ addProp( GEOTEXT__SCALETEXTONPATH , data( "geotext.scaletextonpath" ) );
+ addProp( GEOTEXT__STRETCHCHARHEIGHT , data( "geotext.stretchcharheight" ) );
+ addProp( GEOTEXT__NOMEASUREALONGPATH , data( "geotext.nomeasurealongpath" ) );
+ addProp( GEOTEXT__BOLDFONT , data( "geotext.boldfont" ) );
+ addProp( GEOTEXT__ITALICFONT , data( "geotext.italicfont" ) );
+ addProp( GEOTEXT__UNDERLINEFONT , data( "geotext.underlinefont" ) );
+ addProp( GEOTEXT__SHADOWFONT , data( "geotext.shadowfont" ) );
+ addProp( GEOTEXT__SMALLCAPSFONT , data( "geotext.smallcapsfont" ) );
+ addProp( GEOTEXT__STRIKETHROUGHFONT , data( "geotext.strikethroughfont" ) );
+ addProp( BLIP__CROPFROMTOP , data( "blip.cropfromtop" ) );
+ addProp( BLIP__CROPFROMBOTTOM , data( "blip.cropfrombottom" ) );
+ addProp( BLIP__CROPFROMLEFT , data( "blip.cropfromleft" ) );
+ addProp( BLIP__CROPFROMRIGHT , data( "blip.cropfromright" ) );
+ addProp( BLIP__BLIPTODISPLAY , data( "blip.bliptodisplay" ) );
+ addProp( BLIP__BLIPFILENAME , data( "blip.blipfilename" ) );
+ addProp( BLIP__BLIPFLAGS , data( "blip.blipflags" ) );
+ addProp( BLIP__TRANSPARENTCOLOR , data( "blip.transparentcolor" ) );
+ addProp( BLIP__CONTRASTSETTING , data( "blip.contrastsetting" ) );
+ addProp( BLIP__BRIGHTNESSSETTING , data( "blip.brightnesssetting" ) );
+ addProp( BLIP__GAMMA , data( "blip.gamma" ) );
+ addProp( BLIP__PICTUREID , data( "blip.pictureid" ) );
+ addProp( BLIP__DOUBLEMOD , data( "blip.doublemod" ) );
+ addProp( BLIP__PICTUREFILLMOD , data( "blip.picturefillmod" ) );
+ addProp( BLIP__PICTURELINE , data( "blip.pictureline" ) );
+ addProp( BLIP__PRINTBLIP , data( "blip.printblip" ) );
+ addProp( BLIP__PRINTBLIPFILENAME , data( "blip.printblipfilename" ) );
+ addProp( BLIP__PRINTFLAGS , data( "blip.printflags" ) );
+ addProp( BLIP__NOHITTESTPICTURE , data( "blip.nohittestpicture" ) );
+ addProp( BLIP__PICTUREGRAY , data( "blip.picturegray" ) );
+ addProp( BLIP__PICTUREBILEVEL , data( "blip.picturebilevel" ) );
+ addProp( BLIP__PICTUREACTIVE , data( "blip.pictureactive" ) );
+ addProp( GEOMETRY__LEFT , data( "geometry.left" ) );
+ addProp( GEOMETRY__TOP , data( "geometry.top" ) );
+ addProp( GEOMETRY__RIGHT , data( "geometry.right" ) );
+ addProp( GEOMETRY__BOTTOM , data( "geometry.bottom" ) );
+ addProp( GEOMETRY__SHAPEPATH , data( "geometry.shapepath", EscherPropertyMetaData.TYPE_SHAPEPATH ) );
+ addProp( GEOMETRY__VERTICES , data( "geometry.vertices" , EscherPropertyMetaData.TYPE_ARRAY ) );
+ addProp( GEOMETRY__SEGMENTINFO , data( "geometry.segmentinfo", EscherPropertyMetaData.TYPE_ARRAY ) );
+ addProp( GEOMETRY__ADJUSTVALUE , data( "geometry.adjustvalue" ) );
+ addProp( GEOMETRY__ADJUST2VALUE , data( "geometry.adjust2value" ) );
+ addProp( GEOMETRY__ADJUST3VALUE , data( "geometry.adjust3value" ) );
+ addProp( GEOMETRY__ADJUST4VALUE , data( "geometry.adjust4value" ) );
+ addProp( GEOMETRY__ADJUST5VALUE , data( "geometry.adjust5value" ) );
+ addProp( GEOMETRY__ADJUST6VALUE , data( "geometry.adjust6value" ) );
+ addProp( GEOMETRY__ADJUST7VALUE , data( "geometry.adjust7value" ) );
+ addProp( GEOMETRY__ADJUST8VALUE , data( "geometry.adjust8value" ) );
+ addProp( GEOMETRY__ADJUST9VALUE , data( "geometry.adjust9value" ) );
+ addProp( GEOMETRY__ADJUST10VALUE , data( "geometry.adjust10value" ) );
+ addProp( GEOMETRY__SHADOWok , data( "geometry.shadowOK" ) );
+ addProp( GEOMETRY__3DOK , data( "geometry.3dok" ) );
+ addProp( GEOMETRY__LINEOK , data( "geometry.lineok" ) );
+ addProp( GEOMETRY__GEOTEXTOK , data( "geometry.geotextok" ) );
+ addProp( GEOMETRY__FILLSHADESHAPEOK , data( "geometry.fillshadeshapeok" ) );
+ addProp( GEOMETRY__FILLOK , data( "geometry.fillok", EscherPropertyMetaData.TYPE_BOOLEAN ) );
+ addProp( FILL__FILLTYPE , data( "fill.filltype" ) );
+ addProp( FILL__FILLCOLOR, data( "fill.fillcolor", EscherPropertyMetaData.TYPE_RGB ) );
+ addProp( FILL__FILLOPACITY , data( "fill.fillopacity" ) );
+ addProp( FILL__FILLBACKCOLOR , data( "fill.fillbackcolor", EscherPropertyMetaData.TYPE_RGB ) );
+ addProp( FILL__BACKOPACITY , data( "fill.backopacity" ) );
+ addProp( FILL__CRMOD , data( "fill.crmod" ) );
+ addProp( FILL__PATTERNTEXTURE , data( "fill.patterntexture" ) );
+ addProp( FILL__BLIPFILENAME , data( "fill.blipfilename" ) );
+ addProp( FILL__BLIPFLAGS, data( "fill.blipflags" ) );
+ addProp( FILL__WIDTH , data( "fill.width" ) );
+ addProp( FILL__HEIGHT , data( "fill.height" ) );
+ addProp( FILL__ANGLE , data( "fill.angle" ) );
+ addProp( FILL__FOCUS , data( "fill.focus" ) );
+ addProp( FILL__TOLEFT , data( "fill.toleft" ) );
+ addProp( FILL__TOTOP , data( "fill.totop" ) );
+ addProp( FILL__TORIGHT , data( "fill.toright" ) );
+ addProp( FILL__TOBOTTOM , data( "fill.tobottom" ) );
+ addProp( FILL__RECTLEFT , data( "fill.rectleft" ) );
+ addProp( FILL__RECTTOP , data( "fill.recttop" ) );
+ addProp( FILL__RECTRIGHT , data( "fill.rectright" ) );
+ addProp( FILL__RECTBOTTOM , data( "fill.rectbottom" ) );
+ addProp( FILL__DZTYPE , data( "fill.dztype" ) );
+ addProp( FILL__SHADEPRESET , data( "fill.shadepreset" ) );
+ addProp( FILL__SHADECOLORS , data( "fill.shadecolors", EscherPropertyMetaData.TYPE_ARRAY ) );
+ addProp( FILL__ORIGINX , data( "fill.originx" ) );
+ addProp( FILL__ORIGINY , data( "fill.originy" ) );
+ addProp( FILL__SHAPEORIGINX , data( "fill.shapeoriginx" ) );
+ addProp( FILL__SHAPEORIGINY , data( "fill.shapeoriginy" ) );
+ addProp( FILL__SHADETYPE , data( "fill.shadetype" ) );
+ addProp( FILL__FILLED , data( "fill.filled" ) );
+ addProp( FILL__HITTESTFILL , data( "fill.hittestfill" ) );
+ addProp( FILL__SHAPE , data( "fill.shape" ) );
+ addProp( FILL__USERECT , data( "fill.userect" ) );
+ addProp( FILL__NOFILLHITTEST , data( "fill.nofillhittest", EscherPropertyMetaData.TYPE_BOOLEAN ) );
+ addProp( LINESTYLE__COLOR, data( "linestyle.color", EscherPropertyMetaData.TYPE_RGB ) );
+ addProp( LINESTYLE__OPACITY , data( "linestyle.opacity" ) );
+ addProp( LINESTYLE__BACKCOLOR , data( "linestyle.backcolor", EscherPropertyMetaData.TYPE_RGB ) );
+ addProp( LINESTYLE__CRMOD , data( "linestyle.crmod" ) );
+ addProp( LINESTYLE__LINETYPE , data( "linestyle.linetype" ) );
+ addProp( LINESTYLE__FILLBLIP , data( "linestyle.fillblip" ) );
+ addProp( LINESTYLE__FILLBLIPNAME , data( "linestyle.fillblipname" ) );
+ addProp( LINESTYLE__FILLBLIPFLAGS , data( "linestyle.fillblipflags" ) );
+ addProp( LINESTYLE__FILLWIDTH , data( "linestyle.fillwidth" ) );
+ addProp( LINESTYLE__FILLHEIGHT , data( "linestyle.fillheight" ) );
+ addProp( LINESTYLE__FILLDZTYPE , data( "linestyle.filldztype" ) );
+ addProp( LINESTYLE__LINEWIDTH , data( "linestyle.linewidth" ) );
+ addProp( LINESTYLE__LINEMITERLIMIT , data( "linestyle.linemiterlimit" ) );
+ addProp( LINESTYLE__LINESTYLE , data( "linestyle.linestyle" ) );
+ addProp( LINESTYLE__LINEDASHING , data( "linestyle.linedashing" ) );
+ addProp( LINESTYLE__LINEDASHSTYLE , data( "linestyle.linedashstyle", EscherPropertyMetaData.TYPE_ARRAY ) );
+ addProp( LINESTYLE__LINESTARTARROWHEAD , data( "linestyle.linestartarrowhead" ) );
+ addProp( LINESTYLE__LINEENDARROWHEAD , data( "linestyle.lineendarrowhead" ) );
+ addProp( LINESTYLE__LINESTARTARROWWIDTH , data( "linestyle.linestartarrowwidth" ) );
+ addProp( LINESTYLE__LINEESTARTARROWLENGTH , data( "linestyle.lineestartarrowlength" ) );
+ addProp( LINESTYLE__LINEENDARROWWIDTH , data( "linestyle.lineendarrowwidth" ) );
+ addProp( LINESTYLE__LINEENDARROWLENGTH , data( "linestyle.lineendarrowlength" ) );
+ addProp( LINESTYLE__LINEJOINSTYLE , data( "linestyle.linejoinstyle" ) );
+ addProp( LINESTYLE__LINEENDCAPSTYLE , data( "linestyle.lineendcapstyle" ) );
+ addProp( LINESTYLE__ARROWHEADSOK , data( "linestyle.arrowheadsok" ) );
+ addProp( LINESTYLE__ANYLINE , data( "linestyle.anyline" ) );
+ addProp( LINESTYLE__HITLINETEST , data( "linestyle.hitlinetest" ) );
+ addProp( LINESTYLE__LINEFILLSHAPE , data( "linestyle.linefillshape" ) );
+ addProp( LINESTYLE__NOLINEDRAWDASH , data( "linestyle.nolinedrawdash", EscherPropertyMetaData.TYPE_BOOLEAN ) );
+ addProp( SHADOWSTYLE__TYPE , data( "shadowstyle.type" ) );
+ addProp( SHADOWSTYLE__COLOR , data( "shadowstyle.color", EscherPropertyMetaData.TYPE_RGB ) );
+ addProp( SHADOWSTYLE__HIGHLIGHT , data( "shadowstyle.highlight" ) );
+ addProp( SHADOWSTYLE__CRMOD , data( "shadowstyle.crmod" ) );
+ addProp( SHADOWSTYLE__OPACITY , data( "shadowstyle.opacity" ) );
+ addProp( SHADOWSTYLE__OFFSETX , data( "shadowstyle.offsetx" ) );
+ addProp( SHADOWSTYLE__OFFSETY , data( "shadowstyle.offsety" ) );
+ addProp( SHADOWSTYLE__SECONDOFFSETX , data( "shadowstyle.secondoffsetx" ) );
+ addProp( SHADOWSTYLE__SECONDOFFSETY , data( "shadowstyle.secondoffsety" ) );
+ addProp( SHADOWSTYLE__SCALEXTOX , data( "shadowstyle.scalextox" ) );
+ addProp( SHADOWSTYLE__SCALEYTOX , data( "shadowstyle.scaleytox" ) );
+ addProp( SHADOWSTYLE__SCALEXTOY , data( "shadowstyle.scalextoy" ) );
+ addProp( SHADOWSTYLE__SCALEYTOY , data( "shadowstyle.scaleytoy" ) );
+ addProp( SHADOWSTYLE__PERSPECTIVEX , data( "shadowstyle.perspectivex" ) );
+ addProp( SHADOWSTYLE__PERSPECTIVEY , data( "shadowstyle.perspectivey" ) );
+ addProp( SHADOWSTYLE__WEIGHT , data( "shadowstyle.weight" ) );
+ addProp( SHADOWSTYLE__ORIGINX , data( "shadowstyle.originx" ) );
+ addProp( SHADOWSTYLE__ORIGINY , data( "shadowstyle.originy" ) );
+ addProp( SHADOWSTYLE__SHADOW , data( "shadowstyle.shadow" ) );
+ addProp( SHADOWSTYLE__SHADOWOBSURED , data( "shadowstyle.shadowobsured" ) );
+ addProp( PERSPECTIVE__TYPE , data( "perspective.type" ) );
+ addProp( PERSPECTIVE__OFFSETX , data( "perspective.offsetx" ) );
+ addProp( PERSPECTIVE__OFFSETY , data( "perspective.offsety" ) );
+ addProp( PERSPECTIVE__SCALEXTOX , data( "perspective.scalextox" ) );
+ addProp( PERSPECTIVE__SCALEYTOX , data( "perspective.scaleytox" ) );
+ addProp( PERSPECTIVE__SCALEXTOY , data( "perspective.scalextoy" ) );
+ addProp( PERSPECTIVE__SCALEYTOY , data( "perspective.scaleytoy" ) );
+ addProp( PERSPECTIVE__PERSPECTIVEX , data( "perspective.perspectivex" ) );
+ addProp( PERSPECTIVE__PERSPECTIVEY , data( "perspective.perspectivey" ) );
+ addProp( PERSPECTIVE__WEIGHT , data( "perspective.weight" ) );
+ addProp( PERSPECTIVE__ORIGINX , data( "perspective.originx" ) );
+ addProp( PERSPECTIVE__ORIGINY , data( "perspective.originy" ) );
+ addProp( PERSPECTIVE__PERSPECTIVEON , data( "perspective.perspectiveon" ) );
+ addProp( THREED__SPECULARAMOUNT , data( "3d.specularamount" ) );
+ addProp( THREED__DIFFUSEAMOUNT , data( "3d.diffuseamount" ) );
+ addProp( THREED__SHININESS , data( "3d.shininess" ) );
+ addProp( THREED__EDGETHICKNESS , data( "3d.edgethickness" ) );
+ addProp( THREED__EXTRUDEFORWARD , data( "3d.extrudeforward" ) );
+ addProp( THREED__EXTRUDEBACKWARD , data( "3d.extrudebackward" ) );
+ addProp( THREED__EXTRUDEPLANE , data( "3d.extrudeplane" ) );
+ addProp( THREED__EXTRUSIONCOLOR , data( "3d.extrusioncolor", EscherPropertyMetaData.TYPE_RGB ) );
+ addProp( THREED__CRMOD , data( "3d.crmod" ) );
+ addProp( THREED__3DEFFECT , data( "3d.3deffect" ) );
+ addProp( THREED__METALLIC , data( "3d.metallic" ) );
+ addProp( THREED__USEEXTRUSIONCOLOR , data( "3d.useextrusioncolor", EscherPropertyMetaData.TYPE_RGB ) );
+ addProp( THREED__LIGHTFACE , data( "3d.lightface" ) );
+ addProp( THREEDSTYLE__YROTATIONANGLE , data( "3dstyle.yrotationangle" ) );
+ addProp( THREEDSTYLE__XROTATIONANGLE , data( "3dstyle.xrotationangle" ) );
+ addProp( THREEDSTYLE__ROTATIONAXISX , data( "3dstyle.rotationaxisx" ) );
+ addProp( THREEDSTYLE__ROTATIONAXISY , data( "3dstyle.rotationaxisy" ) );
+ addProp( THREEDSTYLE__ROTATIONAXISZ , data( "3dstyle.rotationaxisz" ) );
+ addProp( THREEDSTYLE__ROTATIONANGLE , data( "3dstyle.rotationangle" ) );
+ addProp( THREEDSTYLE__ROTATIONCENTERX , data( "3dstyle.rotationcenterx" ) );
+ addProp( THREEDSTYLE__ROTATIONCENTERY , data( "3dstyle.rotationcentery" ) );
+ addProp( THREEDSTYLE__ROTATIONCENTERZ , data( "3dstyle.rotationcenterz" ) );
+ addProp( THREEDSTYLE__RENDERMODE , data( "3dstyle.rendermode" ) );
+ addProp( THREEDSTYLE__TOLERANCE , data( "3dstyle.tolerance" ) );
+ addProp( THREEDSTYLE__XVIEWPOINT , data( "3dstyle.xviewpoint" ) );
+ addProp( THREEDSTYLE__YVIEWPOINT , data( "3dstyle.yviewpoint" ) );
+ addProp( THREEDSTYLE__ZVIEWPOINT , data( "3dstyle.zviewpoint" ) );
+ addProp( THREEDSTYLE__ORIGINX , data( "3dstyle.originx" ) );
+ addProp( THREEDSTYLE__ORIGINY , data( "3dstyle.originy" ) );
+ addProp( THREEDSTYLE__SKEWANGLE , data( "3dstyle.skewangle" ) );
+ addProp( THREEDSTYLE__SKEWAMOUNT , data( "3dstyle.skewamount" ) );
+ addProp( THREEDSTYLE__AMBIENTINTENSITY , data( "3dstyle.ambientintensity" ) );
+ addProp( THREEDSTYLE__KEYX , data( "3dstyle.keyx" ) );
+ addProp( THREEDSTYLE__KEYY , data( "3dstyle.keyy" ) );
+ addProp( THREEDSTYLE__KEYZ , data( "3dstyle.keyz" ) );
+ addProp( THREEDSTYLE__KEYINTENSITY , data( "3dstyle.keyintensity" ) );
+ addProp( THREEDSTYLE__FILLX , data( "3dstyle.fillx" ) );
+ addProp( THREEDSTYLE__FILLY , data( "3dstyle.filly" ) );
+ addProp( THREEDSTYLE__FILLZ , data( "3dstyle.fillz" ) );
+ addProp( THREEDSTYLE__FILLINTENSITY , data( "3dstyle.fillintensity" ) );
+ addProp( THREEDSTYLE__CONSTRAINROTATION , data( "3dstyle.constrainrotation" ) );
+ addProp( THREEDSTYLE__ROTATIONCENTERAUTO , data( "3dstyle.rotationcenterauto" ) );
+ addProp( THREEDSTYLE__PARALLEL , data( "3dstyle.parallel" ) );
+ addProp( THREEDSTYLE__KEYHARSH , data( "3dstyle.keyharsh" ) );
+ addProp( THREEDSTYLE__FILLHARSH , data( "3dstyle.fillharsh" ) );
+ addProp( SHAPE__MASTER , data( "shape.master" ) );
+ addProp( SHAPE__CONNECTORSTYLE , data( "shape.connectorstyle" ) );
+ addProp( SHAPE__BLACKANDWHITESETTINGS , data( "shape.blackandwhitesettings" ) );
+ addProp( SHAPE__WMODEPUREBW , data( "shape.wmodepurebw" ) );
+ addProp( SHAPE__WMODEBW , data( "shape.wmodebw" ) );
+ addProp( SHAPE__OLEICON , data( "shape.oleicon" ) );
+ addProp( SHAPE__PREFERRELATIVERESIZE , data( "shape.preferrelativeresize" ) );
+ addProp( SHAPE__LOCKSHAPETYPE , data( "shape.lockshapetype" ) );
+ addProp( SHAPE__DELETEATTACHEDOBJECT , data( "shape.deleteattachedobject" ) );
+ addProp( SHAPE__BACKGROUNDSHAPE , data( "shape.backgroundshape" ) );
+ addProp( CALLOUT__CALLOUTTYPE , data( "callout.callouttype" ) );
+ addProp( CALLOUT__XYCALLOUTGAP , data( "callout.xycalloutgap" ) );
+ addProp( CALLOUT__CALLOUTANGLE , data( "callout.calloutangle" ) );
+ addProp( CALLOUT__CALLOUTDROPTYPE , data( "callout.calloutdroptype" ) );
+ addProp( CALLOUT__CALLOUTDROPSPECIFIED , data( "callout.calloutdropspecified" ) );
+ addProp( CALLOUT__CALLOUTLENGTHSPECIFIED , data( "callout.calloutlengthspecified" ) );
+ addProp( CALLOUT__ISCALLOUT , data( "callout.iscallout" ) );
+ addProp( CALLOUT__CALLOUTACCENTBAR , data( "callout.calloutaccentbar" ) );
+ addProp( CALLOUT__CALLOUTTEXTBORDER , data( "callout.callouttextborder" ) );
+ addProp( CALLOUT__CALLOUTMINUSX , data( "callout.calloutminusx" ) );
+ addProp( CALLOUT__CALLOUTMINUSY , data( "callout.calloutminusy" ) );
+ addProp( CALLOUT__DROPAUTO , data( "callout.dropauto" ) );
+ addProp( CALLOUT__LENGTHSPECIFIED , data( "callout.lengthspecified" ) );
+ addProp( GROUPSHAPE__SHAPENAME , data( "groupshape.shapename" ) );
+ addProp( GROUPSHAPE__DESCRIPTION , data( "groupshape.description" ) );
+ addProp( GROUPSHAPE__HYPERLINK , data( "groupshape.hyperlink" ) );
+ addProp( GROUPSHAPE__WRAPPOLYGONVERTICES , data( "groupshape.wrappolygonvertices", EscherPropertyMetaData.TYPE_ARRAY ) );
+ addProp( GROUPSHAPE__WRAPDISTLEFT , data( "groupshape.wrapdistleft" ) );
+ addProp( GROUPSHAPE__WRAPDISTTOP , data( "groupshape.wrapdisttop" ) );
+ addProp( GROUPSHAPE__WRAPDISTRIGHT , data( "groupshape.wrapdistright" ) );
+ addProp( GROUPSHAPE__WRAPDISTBOTTOM , data( "groupshape.wrapdistbottom" ) );
+ addProp( GROUPSHAPE__REGROUPID , data( "groupshape.regroupid" ) );
+ addProp( GROUPSHAPE__EDITEDWRAP , data( "groupshape.editedwrap" ) );
+ addProp( GROUPSHAPE__BEHINDDOCUMENT , data( "groupshape.behinddocument" ) );
+ addProp( GROUPSHAPE__ONDBLCLICKNOTIFY , data( "groupshape.ondblclicknotify" ) );
+ addProp( GROUPSHAPE__ISBUTTON , data( "groupshape.isbutton" ) );
+ addProp( GROUPSHAPE__1DADJUSTMENT , data( "groupshape.1dadjustment" ) );
+ addProp( GROUPSHAPE__HIDDEN , data( "groupshape.hidden" ) );
+ addProp( GROUPSHAPE__PRINT , data( "groupshape.print", EscherPropertyMetaData.TYPE_BOOLEAN ) );
+ }
+ }
+
+ private static void addProp( int s, EscherPropertyMetaData data )
+ {
+ properties.put( new Short( (short) s ), data );
+ }
+
+ private static EscherPropertyMetaData data( String propName, byte type )
+ {
+ return new EscherPropertyMetaData( propName, type );
+ }
+
+ private static EscherPropertyMetaData data( String propName )
+ {
+ return new EscherPropertyMetaData( propName );
+ }
+
+ public static String getPropertyName( short propertyId )
+ {
+ initProps();
+ EscherPropertyMetaData o = (EscherPropertyMetaData) properties.get( new Short( propertyId ) );
+ return o == null ? "unknown" : o.getDescription();
+ }
+
+ public static byte getPropertyType( short propertyId )
+ {
+ initProps();
+ EscherPropertyMetaData escherPropertyMetaData = (EscherPropertyMetaData) properties.get( new Short( propertyId ) );
+ return escherPropertyMetaData == null ? 0 : escherPropertyMetaData.getType();
+ }
+}
+
+
+
diff --git a/src/java/org/apache/poi/ddf/EscherProperty.java b/src/java/org/apache/poi/ddf/EscherProperty.java
new file mode 100644
index 0000000000..787ccf2f8d
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherProperty.java
@@ -0,0 +1,78 @@
+package org.apache.poi.ddf;
+
+/**
+ * This is the abstract base class for all escher properties.
+ *
+ * @see EscherOptRecord
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+abstract public class EscherProperty
+{
+ private short id;
+
+ /**
+ * The id is distinct from the actual property number. The id includes the property number the blip id
+ * flag and an indicator whether the property is complex or not.
+ */
+ public EscherProperty( short id )
+ {
+ this.id = id;
+ }
+
+ /**
+ * Constructs a new escher property. The three parameters are combined to form a property
+ * id.
+ */
+ public EscherProperty( short propertyNumber, boolean isComplex, boolean isBlipId )
+ {
+ this.id = (short)(propertyNumber +
+ (isComplex ? 0x8000 : 0x0) +
+ (isBlipId ? 0x4000 : 0x0));
+ }
+
+ public short getId()
+ {
+ return id;
+ }
+
+ public short getPropertyNumber()
+ {
+ return (short) ( id & (short) 0x3FFF );
+ }
+
+ public boolean isComplex()
+ {
+ return ( id & (short) 0x8000 ) != 0;
+ }
+
+ public boolean isBlipId()
+ {
+ return ( id & (short) 0x4000 ) != 0;
+ }
+
+ public String getName()
+ {
+ return EscherProperties.getPropertyName(id);
+ }
+
+ /**
+ * Most properties are just 6 bytes in length. Override this if we're
+ * dealing with complex properties.
+ */
+ public int getPropertySize()
+ {
+ return 6;
+ }
+
+ /**
+ * Escher properties consist of a simple fixed length part and a complex variable length part.
+ * The fixed length part is serialized first.
+ */
+ abstract public int serializeSimplePart( byte[] data, int pos );
+ /**
+ * Escher properties consist of a simple fixed length part and a complex variable length part.
+ * The fixed length part is serialized first.
+ */
+ abstract public int serializeComplexPart( byte[] data, int pos );
+}
diff --git a/src/java/org/apache/poi/ddf/EscherPropertyFactory.java b/src/java/org/apache/poi/ddf/EscherPropertyFactory.java
new file mode 100644
index 0000000000..cc6b7ba7a8
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherPropertyFactory.java
@@ -0,0 +1,91 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hssf.record.RecordFormatException;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Generates a property given a reference into the byte array storing that property.
+ *
+ * @author Glen Stampoultzis
+ */
+public class EscherPropertyFactory
+{
+ /**
+ * Create new properties from a byte array.
+ *
+ * @param data The byte array containing the property
+ * @param offset The starting offset into the byte array
+ * @return The new properties
+ */
+ public List createProperties( byte[] data, int offset, short numProperties )
+ {
+ List results = new ArrayList();
+
+ int pos = offset;
+ int complexBytes = 0;
+// while ( bytesRemaining >= 6 )
+ for (int i = 0; i < numProperties; i++)
+ {
+ short propId;
+ int propData;
+ propId = LittleEndian.getShort( data, pos );
+ propData = LittleEndian.getInt( data, pos + 2 );
+ short propNumber = (short) ( propId & (short) 0x3FFF );
+ boolean isComplex = ( propId & (short) 0x8000 ) != 0;
+ boolean isBlipId = ( propId & (short) 0x4000 ) != 0;
+ if ( isComplex )
+ complexBytes = propData;
+ else
+ complexBytes = 0;
+ byte propertyType = EscherProperties.getPropertyType( (short) propNumber );
+ if ( propertyType == EscherPropertyMetaData.TYPE_BOOLEAN )
+ results.add( new EscherBoolProperty( propNumber, propData ) );
+ else if ( propertyType == EscherPropertyMetaData.TYPE_RGB )
+ results.add( new EscherRGBProperty( propNumber, propData ) );
+ else if ( propertyType == EscherPropertyMetaData.TYPE_SHAPEPATH )
+ results.add( new EscherShapePathProperty( propNumber, propData ) );
+ else
+ {
+ if ( !isComplex )
+ results.add( new EscherSimpleProperty( propNumber, propData ) );
+ else
+ {
+ if ( propertyType == EscherPropertyMetaData.TYPE_ARRAY)
+ results.add( new EscherArrayProperty( propId, new byte[propData]) );
+ else
+ results.add( new EscherComplexProperty( propId, new byte[propData]) );
+
+ }
+ }
+ pos += 6;
+// bytesRemaining -= 6 + complexBytes;
+ }
+
+ // Get complex data
+ for ( Iterator iterator = results.iterator(); iterator.hasNext(); )
+ {
+ EscherProperty p = (EscherProperty) iterator.next();
+ if (p instanceof EscherComplexProperty)
+ {
+ if (p instanceof EscherArrayProperty)
+ {
+ pos += ((EscherArrayProperty)p).setArrayData(data, pos);
+ }
+ else
+ {
+ byte[] complexData = ((EscherComplexProperty)p).getComplexData();
+ System.arraycopy(data, pos, complexData, 0, complexData.length);
+ pos += complexData.length;
+ }
+ }
+ }
+
+ return results;
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherPropertyMetaData.java b/src/java/org/apache/poi/ddf/EscherPropertyMetaData.java
new file mode 100644
index 0000000000..97576064e7
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherPropertyMetaData.java
@@ -0,0 +1,51 @@
+package org.apache.poi.ddf;
+
+/**
+ * This class stores the type and description of an escher property.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherPropertyMetaData
+{
+ // Escher property types.
+ public final static byte TYPE_UNKNOWN = (byte) 0;
+ public final static byte TYPE_BOOLEAN = (byte) 1;
+ public final static byte TYPE_RGB = (byte) 2;
+ public final static byte TYPE_SHAPEPATH = (byte) 3;
+ public final static byte TYPE_SIMPLE = (byte)4;
+ public final static byte TYPE_ARRAY = (byte)5;;
+
+ private String description;
+ private byte type;
+
+
+ /**
+ * @param description The description of the escher property.
+ */
+ public EscherPropertyMetaData( String description )
+ {
+ this.description = description;
+ }
+
+ /**
+ *
+ * @param description The description of the escher property.
+ * @param type The type of the property.
+ */
+ public EscherPropertyMetaData( String description, byte type )
+ {
+ this.description = description;
+ this.type = type;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public byte getType()
+ {
+ return type;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherRGBProperty.java b/src/java/org/apache/poi/ddf/EscherRGBProperty.java
new file mode 100644
index 0000000000..1b8c9d2952
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherRGBProperty.java
@@ -0,0 +1,37 @@
+package org.apache.poi.ddf;
+
+/**
+ * A color property.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherRGBProperty
+ extends EscherSimpleProperty
+{
+
+ public EscherRGBProperty( short propertyNumber, int rgbColor )
+ {
+ super( propertyNumber, false, false, rgbColor );
+ }
+
+ public int getRgbColor()
+ {
+ return propertyValue;
+ }
+
+ public byte getRed()
+ {
+ return (byte) ( propertyValue & 0xFF );
+ }
+
+ public byte getGreen()
+ {
+ return (byte) ( (propertyValue >> 8) & 0xFF );
+ }
+
+ public byte getBlue()
+ {
+ return (byte) ( (propertyValue >> 16) & 0xFF );
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherRecord.java b/src/java/org/apache/poi/ddf/EscherRecord.java
new file mode 100644
index 0000000000..0001bbfec0
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherRecord.java
@@ -0,0 +1,273 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.LittleEndian;
+
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The base abstract record from which all escher records are defined. Subclasses will need
+ * to define methods for serialization/deserialization and for determining the record size.
+ *
+ * @author Glen Stampoultzis
+ */
+abstract public class EscherRecord
+{
+ private short options;
+ private short recordId;
+
+ /**
+ * Create a new instance
+ */
+ public EscherRecord()
+ {
+ }
+
+ /**
+ * Delegates to fillFields(byte[], int, EscherRecordFactory)
+ *
+ * @see #fillFields(byte[], int, org.apache.poi.ddf.EscherRecordFactory)
+ */
+ protected int fillFields( byte[] data, EscherRecordFactory f )
+ {
+ return fillFields( data, 0, f );
+ }
+
+ /**
+ * The contract of this method is to deserialize an escher record including
+ * it's children.
+ *
+ * @param data The byte array containing the serialized escher
+ * records.
+ * @param offset The offset into the byte array.
+ * @param recordFactory A factory for creating new escher records.
+ * @return The number of bytes written.
+ */
+ public abstract int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory );
+
+ /**
+ * Reads the 8 byte header information and populates the <code>options</code>
+ * and <code>recordId</code> records.
+ *
+ * @param data the byte array to read from
+ * @param offset the offset to start reading from
+ * @return the number of bytes remaining in this record. This
+ * may include the children if this is a container.
+ */
+ protected int readHeader( byte[] data, int offset )
+ {
+ EscherRecordHeader header = EscherRecordHeader.readHeader(data, offset);
+ options = header.getOptions();
+ recordId = header.getRecordId();
+ return header.getRemainingBytes();
+ }
+
+ /**
+ * Determine whether this is a container record by inspecting the option
+ * field.
+ * @return true is this is a container field.
+ */
+ public boolean isContainerRecord()
+ {
+ return (options & (short)0x000f) == (short)0x000f;
+ }
+
+ /**
+ * @return The options field for this record. All records have one.
+ */
+ public short getOptions()
+ {
+ return options;
+ }
+
+ /**
+ * Set the options this this record. Container records should have the
+ * last nibble set to 0xF.
+ */
+ public void setOptions( short options )
+ {
+ this.options = options;
+ }
+
+ /**
+ * Serializes to a new byte array. This is done by delegating to
+ * serialize(int, byte[]);
+ *
+ * @return the serialized record.
+ * @see #serialize(int, byte[])
+ */
+ public byte[] serialize()
+ {
+ byte[] retval = new byte[getRecordSize()];
+
+ serialize( 0, retval );
+ return retval;
+ }
+
+ /**
+ * Serializes to an existing byte array without serialization listener.
+ * This is done by delegating to serialize(int, byte[], EscherSerializationListener).
+ *
+ * @param offset the offset within the data byte array.
+ * @param data the data array to serialize to.
+ * @return The number of bytes written.
+ *
+ * @see #serialize(int, byte[], org.apache.poi.ddf.EscherSerializationListener)
+ */
+ public int serialize( int offset, byte[] data)
+ {
+ return serialize( offset, data, new NullEscherSerializationListener() );
+ }
+
+ /**
+ * Serializes the record to an existing byte array.
+ *
+ * @param offset the offset within the byte array
+ * @param data the data array to serialize to
+ * @param listener a listener for begin and end serialization events. This
+ * is useful because the serialization is
+ * hierarchical/recursive and sometimes you need to be able
+ * break into that.
+ * @return the number of bytes written.
+ */
+ public abstract int serialize( int offset, byte[] data, EscherSerializationListener listener );
+
+ /**
+ * Subclasses should effeciently return the number of bytes required to
+ * serialize the record.
+ *
+ * @return number of bytes
+ */
+ abstract public int getRecordSize();
+
+ /**
+ * Return the current record id.
+ *
+ * @return The 16 bit record id.
+ */
+ public short getRecordId()
+ {
+ return recordId;
+ }
+
+ /**
+ * Sets the record id for this record.
+ */
+ public void setRecordId( short recordId )
+ {
+ this.recordId = recordId;
+ }
+
+ /**
+ * @return Returns the children of this record. By default this will
+ * be an empty list. EscherCotainerRecord is the only record
+ * that may contain children.
+ *
+ * @see EscherContainerRecord
+ */
+ public List getChildRecords() { return Collections.EMPTY_LIST; }
+
+ /**
+ * Sets the child records for this record. By default this will throw
+ * an exception as only EscherContainerRecords may have children.
+ *
+ * @param childRecords Not used in base implementation.
+ */
+ public void setChildRecords( List childRecords ) { throw new IllegalArgumentException("This record does not support child records."); }
+
+ /**
+ * Escher records may need to be clonable in the future.
+ */
+ public Object clone()
+ {
+ throw new RuntimeException( "The class " + getClass().getName() + " needs to define a clone method" );
+ }
+
+ /**
+ * Returns the indexed child record.
+ */
+ public EscherRecord getChild( int index )
+ {
+ return (EscherRecord) getChildRecords().get(index);
+ }
+
+ /**
+ * The display methods allows escher variables to print the record names
+ * according to their hierarchy.
+ *
+ * @param w The print writer to output to.
+ * @param indent The current indent level.
+ */
+ public void display(PrintWriter w, int indent)
+ {
+ for (int i = 0; i < indent * 4; i++) w.print(' ');
+ w.println(getRecordName());
+ }
+
+ /**
+ * Subclasses should return the short name for this escher record.
+ */
+ public abstract String getRecordName();
+
+ /**
+ * Returns the instance part of the option record.
+ *
+ * @return The instance part of the record
+ */
+ public short getInstance()
+ {
+ return (short) ( options >> 4 );
+ }
+
+ /**
+ * This class reads the standard escher header.
+ */
+ static class EscherRecordHeader
+ {
+ private short options;
+ private short recordId;
+ private int remainingBytes;
+
+ private EscherRecordHeader()
+ {
+ }
+
+ public static EscherRecordHeader readHeader( byte[] data, int offset )
+ {
+ EscherRecordHeader header = new EscherRecordHeader();
+ header.options = LittleEndian.getShort(data, offset);
+ header.recordId = LittleEndian.getShort(data, offset + 2);
+ header.remainingBytes = LittleEndian.getInt( data, offset + 4 );
+ return header;
+ }
+
+
+ public short getOptions()
+ {
+ return options;
+ }
+
+ public short getRecordId()
+ {
+ return recordId;
+ }
+
+ public int getRemainingBytes()
+ {
+ return remainingBytes;
+ }
+
+ public String toString()
+ {
+ return "EscherRecordHeader{" +
+ "options=" + options +
+ ", recordId=" + recordId +
+ ", remainingBytes=" + remainingBytes +
+ "}";
+ }
+
+
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherRecordFactory.java b/src/java/org/apache/poi/ddf/EscherRecordFactory.java
new file mode 100644
index 0000000000..4c6e22c8a6
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherRecordFactory.java
@@ -0,0 +1,16 @@
+package org.apache.poi.ddf;
+
+/**
+ * The escher record factory interface allows for the creation of escher
+ * records from a pointer into a data array.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public interface EscherRecordFactory
+{
+ /**
+ * Create a new escher record from the data provided. Does not attempt
+ * to fill the contents of the record however.
+ */
+ EscherRecord createRecord( byte[] data, int offset );
+}
diff --git a/src/java/org/apache/poi/ddf/EscherSerializationListener.java b/src/java/org/apache/poi/ddf/EscherSerializationListener.java
new file mode 100644
index 0000000000..ddea267c7e
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherSerializationListener.java
@@ -0,0 +1,27 @@
+package org.apache.poi.ddf;
+
+/**
+ * Interface for listening to escher serialization events.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public interface EscherSerializationListener
+{
+ /**
+ * Fired before a given escher record is serialized.
+ *
+ * @param offset The position in the data array at which the record will be serialized.
+ * @param recordId The id of the record about to be serialized.
+ */
+ void beforeRecordSerialize(int offset, short recordId, EscherRecord record);
+
+ /**
+ * Fired after a record has been serialized.
+ *
+ * @param offset The position of the end of the serialized record + 1
+ * @param recordId The id of the record about to be serialized
+ * @param size The number of bytes written for this record. If it is a container
+ * record then this will include the size of any included records.
+ */
+ void afterRecordSerialize(int offset, short recordId, int size, EscherRecord record);
+}
diff --git a/src/java/org/apache/poi/ddf/EscherShapePathProperty.java b/src/java/org/apache/poi/ddf/EscherShapePathProperty.java
new file mode 100644
index 0000000000..df70e9c83f
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherShapePathProperty.java
@@ -0,0 +1,25 @@
+package org.apache.poi.ddf;
+
+/**
+ * Defines the constants for the various possible shape paths.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherShapePathProperty
+ extends EscherSimpleProperty
+{
+
+ public static final int LINE_OF_STRAIGHT_SEGMENTS = 0;
+ public static final int CLOSED_POLYGON = 1;
+ public static final int CURVES = 2;
+ public static final int CLOSED_CURVES = 3;
+ public static final int COMPLEX = 4;
+
+ public EscherShapePathProperty( short propertyNumber, int shapePath )
+ {
+ super( propertyNumber, false, false, shapePath );
+ }
+
+
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherSimpleProperty.java b/src/java/org/apache/poi/ddf/EscherSimpleProperty.java
new file mode 100644
index 0000000000..b61474efdd
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherSimpleProperty.java
@@ -0,0 +1,103 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.HexDump;
+
+/**
+ * A simple property is of fixed length and as a property number in addition
+ * to a 32-bit value. Properties that can't be stored in only 32-bits are
+ * stored as EscherComplexProperty objects.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherSimpleProperty extends EscherProperty
+{
+ protected int propertyValue;
+
+ /**
+ * The id is distinct from the actual property number. The id includes the property number the blip id
+ * flag and an indicator whether the property is complex or not.
+ */
+ public EscherSimpleProperty( short id, int propertyValue )
+ {
+ super( id );
+ this.propertyValue = propertyValue;
+ }
+
+ /**
+ * Constructs a new escher property. The three parameters are combined to form a property
+ * id.
+ */
+ public EscherSimpleProperty( short propertyNumber, boolean isComplex, boolean isBlipId, int propertyValue )
+ {
+ super( propertyNumber, isComplex, isBlipId );
+ this.propertyValue = propertyValue;
+ }
+
+ /**
+ * Serialize the simple part of the escher record.
+ *
+ * @return the number of bytes serialized.
+ */
+ public int serializeSimplePart( byte[] data, int offset )
+ {
+ LittleEndian.putShort(data, offset, getId());
+ LittleEndian.putInt(data, offset + 2, propertyValue);
+ return 6;
+ }
+
+ /**
+ * Escher properties consist of a simple fixed length part and a complex variable length part.
+ * The fixed length part is serialized first.
+ */
+ public int serializeComplexPart( byte[] data, int pos )
+ {
+ return 0;
+ }
+
+ /**
+ * @return Return the 32 bit value of this property.
+ */
+ public int getPropertyValue()
+ {
+ return propertyValue;
+ }
+
+ /**
+ * Returns true if one escher property is equal to another.
+ */
+ public boolean equals( Object o )
+ {
+ if ( this == o ) return true;
+ if ( !( o instanceof EscherSimpleProperty ) ) return false;
+
+ final EscherSimpleProperty escherSimpleProperty = (EscherSimpleProperty) o;
+
+ if ( propertyValue != escherSimpleProperty.propertyValue ) return false;
+ if ( getId() != escherSimpleProperty.getId() ) return false;
+
+ return true;
+ }
+
+ /**
+ * Returns a hashcode so that this object can be stored in collections that
+ * require the use of such things.
+ */
+ public int hashCode()
+ {
+ return propertyValue;
+ }
+
+ /**
+ * @return the string representation of this property.
+ */
+ public String toString()
+ {
+ return "propNum: " + getPropertyNumber()
+ + ", propName: " + EscherProperties.getPropertyName( getPropertyNumber() )
+ + ", complex: " + isComplex()
+ + ", blipId: " + isBlipId()
+ + ", value: " + propertyValue + " (0x" + HexDump.toHex(propertyValue) + ")";
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherSpRecord.java b/src/java/org/apache/poi/ddf/EscherSpRecord.java
new file mode 100644
index 0000000000..a5e2be9ff9
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherSpRecord.java
@@ -0,0 +1,201 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Together the the EscherOptRecord this record defines some of the basic
+ * properties of a shape.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherSpRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF00A;
+ public static final String RECORD_DESCRIPTION = "MsofbtSp";
+
+ public static final int FLAG_GROUP = 0x0001;
+ public static final int FLAG_CHILD = 0x0002;
+ public static final int FLAG_PATRIARCH = 0x0004;
+ public static final int FLAG_DELETED = 0x0008;
+ public static final int FLAG_OLESHAPE = 0x0010;
+ public static final int FLAG_HAVEMASTER = 0x0020;
+ public static final int FLAG_FLIPHORIZ = 0x0040;
+ public static final int FLAG_FLIPVERT = 0x0080;
+ public static final int FLAG_CONNECTOR = 0x0100;
+ public static final int FLAG_HAVEANCHOR = 0x0200;
+ public static final int FLAG_BACKGROUND = 0x0400;
+ public static final int FLAG_HASSHAPETYPE = 0x0800;
+
+ private int field_1_shapeId;
+ private int field_2_flags;
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ int size = 0;
+ field_1_shapeId = LittleEndian.getInt( data, pos + size ); size += 4;
+ field_2_flags = LittleEndian.getInt( data, pos + size ); size += 4;
+// bytesRemaining -= size;
+// remainingData = new byte[bytesRemaining];
+// System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
+ return getRecordSize();
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ *
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ int remainingBytes = 8;
+ LittleEndian.putInt( data, offset + 4, remainingBytes );
+ LittleEndian.putInt( data, offset + 8, field_1_shapeId );
+ LittleEndian.putInt( data, offset + 12, field_2_flags );
+// System.arraycopy( remainingData, 0, data, offset + 26, remainingData.length );
+// int pos = offset + 8 + 18 + remainingData.length;
+ listener.afterRecordSerialize( offset + getRecordSize(), getRecordId(), getRecordSize(), this );
+ return 8 + 8;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 8;
+ }
+
+ /**
+ * @return the 16 bit identifier for this record.
+ */
+ public short getRecordId()
+ {
+ return RECORD_ID;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "Sp";
+ }
+
+ /**
+ * @return the string representing this shape.
+ */
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
+ " Options: 0x" + HexDump.toHex(getOptions()) + nl +
+ " ShapeId: " + field_1_shapeId + nl +
+ " Flags: " + decodeFlags(field_2_flags) + " (0x" + HexDump.toHex(field_2_flags) + ")" + nl;
+
+ }
+
+ /**
+ * Converts the shape flags into a more descriptive name.
+ */
+ private String decodeFlags( int flags )
+ {
+ StringBuffer result = new StringBuffer();
+ result.append( ( flags & FLAG_GROUP ) != 0 ? "|GROUP" : "" );
+ result.append( ( flags & FLAG_CHILD ) != 0 ? "|CHILD" : "" );
+ result.append( ( flags & FLAG_PATRIARCH ) != 0 ? "|PATRIARCH" : "" );
+ result.append( ( flags & FLAG_DELETED ) != 0 ? "|DELETED" : "" );
+ result.append( ( flags & FLAG_OLESHAPE ) != 0 ? "|OLESHAPE" : "" );
+ result.append( ( flags & FLAG_HAVEMASTER ) != 0 ? "|HAVEMASTER" : "" );
+ result.append( ( flags & FLAG_FLIPHORIZ ) != 0 ? "|FLIPHORIZ" : "" );
+ result.append( ( flags & FLAG_FLIPVERT ) != 0 ? "|FLIPVERT" : "" );
+ result.append( ( flags & FLAG_CONNECTOR ) != 0 ? "|CONNECTOR" : "" );
+ result.append( ( flags & FLAG_HAVEANCHOR ) != 0 ? "|HAVEANCHOR" : "" );
+ result.append( ( flags & FLAG_BACKGROUND ) != 0 ? "|BACKGROUND" : "" );
+ result.append( ( flags & FLAG_HASSHAPETYPE ) != 0 ? "|HASSHAPETYPE" : "" );
+
+ result.deleteCharAt(0);
+ return result.toString();
+ }
+
+ /**
+ * @return A number that identifies this shape
+ */
+ public int getShapeId()
+ {
+ return field_1_shapeId;
+ }
+
+ /**
+ * Sets a number that identifies this shape.
+ */
+ public void setShapeId( int field_1_shapeId )
+ {
+ this.field_1_shapeId = field_1_shapeId;
+ }
+
+ /**
+ * The flags that apply to this shape.
+ *
+ * @see #FLAG_GROUP
+ * @see #FLAG_CHILD
+ * @see #FLAG_PATRIARCH
+ * @see #FLAG_DELETED
+ * @see #FLAG_OLESHAPE
+ * @see #FLAG_HAVEMASTER
+ * @see #FLAG_FLIPHORIZ
+ * @see #FLAG_FLIPVERT
+ * @see #FLAG_CONNECTOR
+ * @see #FLAG_HAVEANCHOR
+ * @see #FLAG_BACKGROUND
+ * @see #FLAG_HASSHAPETYPE
+ */
+ public int getFlags()
+ {
+ return field_2_flags;
+ }
+
+ /**
+ * The flags that apply to this shape.
+ *
+ * @see #FLAG_GROUP
+ * @see #FLAG_CHILD
+ * @see #FLAG_PATRIARCH
+ * @see #FLAG_DELETED
+ * @see #FLAG_OLESHAPE
+ * @see #FLAG_HAVEMASTER
+ * @see #FLAG_FLIPHORIZ
+ * @see #FLAG_FLIPVERT
+ * @see #FLAG_CONNECTOR
+ * @see #FLAG_HAVEANCHOR
+ * @see #FLAG_BACKGROUND
+ * @see #FLAG_HASSHAPETYPE
+ */
+ public void setFlags( int field_2_flags )
+ {
+ this.field_2_flags = field_2_flags;
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/EscherSpgrRecord.java b/src/java/org/apache/poi/ddf/EscherSpgrRecord.java
new file mode 100644
index 0000000000..7e86108e0d
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherSpgrRecord.java
@@ -0,0 +1,192 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hssf.record.RecordFormatException;
+
+/**
+ * The spgr record defines information about a shape group. Groups in escher
+ * are simply another form of shape that you can't physically see.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherSpgrRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF009;
+ public static final String RECORD_DESCRIPTION = "MsofbtSpgr";
+
+ private int field_1_rectX1;
+ private int field_2_rectY1;
+ private int field_3_rectX2;
+ private int field_4_rectY2;
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ int size = 0;
+ field_1_rectX1 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_2_rectY1 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_3_rectX2 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_4_rectY2 = LittleEndian.getInt( data, pos + size );size+=4;
+ bytesRemaining -= size;
+ if (bytesRemaining != 0) throw new RecordFormatException("Expected no remaining bytes but got " + bytesRemaining);
+// remainingData = new byte[bytesRemaining];
+// System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );
+ return 8 + size + bytesRemaining;
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ *
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+ LittleEndian.putShort( data, offset, getOptions() );
+ LittleEndian.putShort( data, offset + 2, getRecordId() );
+ int remainingBytes = 16;
+ LittleEndian.putInt( data, offset + 4, remainingBytes );
+ LittleEndian.putInt( data, offset + 8, field_1_rectX1 );
+ LittleEndian.putInt( data, offset + 12, field_2_rectY1 );
+ LittleEndian.putInt( data, offset + 16, field_3_rectX2 );
+ LittleEndian.putInt( data, offset + 20, field_4_rectY2 );
+// System.arraycopy( remainingData, 0, data, offset + 26, remainingData.length );
+// int pos = offset + 8 + 18 + remainingData.length;
+ listener.afterRecordSerialize( offset + getRecordSize(), getRecordId(), offset + getRecordSize(), this );
+ return 8 + 16;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 16;
+ }
+
+ /**
+ * The 16 bit identifier of this shape group record.
+ */
+ public short getRecordId()
+ {
+ return RECORD_ID;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "Spgr";
+ }
+
+ /**
+ * @return the string representation of this record.
+ */
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+// String extraData;
+// ByteArrayOutputStream b = new ByteArrayOutputStream();
+// try
+// {
+// HexDump.dump(this.remainingData, 0, b, 0);
+// extraData = b.toString();
+// }
+// catch ( Exception e )
+// {
+// extraData = "error";
+// }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
+ " Options: 0x" + HexDump.toHex(getOptions()) + nl +
+ " RectX: " + field_1_rectX1 + nl +
+ " RectY: " + field_2_rectY1 + nl +
+ " RectWidth: " + field_3_rectX2 + nl +
+ " RectHeight: " + field_4_rectY2 + nl;
+
+ }
+
+ /**
+ * The starting top-left coordinate of child records.
+ */
+ public int getRectX1()
+ {
+ return field_1_rectX1;
+ }
+
+ /**
+ * The starting top-left coordinate of child records.
+ */
+ public void setRectX1( int x1 )
+ {
+ this.field_1_rectX1 = x1;
+ }
+
+ /**
+ * The starting top-left coordinate of child records.
+ */
+ public int getRectY1()
+ {
+ return field_2_rectY1;
+ }
+
+ /**
+ * The starting top-left coordinate of child records.
+ */
+ public void setRectY1( int y1 )
+ {
+ this.field_2_rectY1 = y1;
+ }
+
+ /**
+ * The starting bottom-right coordinate of child records.
+ */
+ public int getRectX2()
+ {
+ return field_3_rectX2;
+ }
+
+ /**
+ * The starting bottom-right coordinate of child records.
+ */
+ public void setRectX2( int x2 )
+ {
+ this.field_3_rectX2 = x2;
+ }
+
+ /**
+ * The starting bottom-right coordinate of child records.
+ */
+ public int getRectY2()
+ {
+ return field_4_rectY2;
+ }
+
+ /**
+ * The starting bottom-right coordinate of child records.
+ */
+ public void setRectY2( int field_4_rectY2 )
+ {
+ this.field_4_rectY2 = field_4_rectY2;
+ }
+}
diff --git a/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java b/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java
new file mode 100644
index 0000000000..2790d4eacf
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java
@@ -0,0 +1,171 @@
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hssf.record.RecordFormatException;
+
+/**
+ * A list of the most recently used colours for the drawings contained in
+ * this document.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherSplitMenuColorsRecord
+ extends EscherRecord
+{
+ public static final short RECORD_ID = (short) 0xF11E;
+ public static final String RECORD_DESCRIPTION = "MsofbtSplitMenuColors";
+
+ private int field_1_color1;
+ private int field_2_color2;
+ private int field_3_color3;
+ private int field_4_color4;
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ int pos = offset + 8;
+ int size = 0;
+ field_1_color1 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_2_color2 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_3_color3 = LittleEndian.getInt( data, pos + size );size+=4;
+ field_4_color4 = LittleEndian.getInt( data, pos + size );size+=4;
+ bytesRemaining -= size;
+ if (bytesRemaining != 0)
+ throw new RecordFormatException("Expecting no remaining data but got " + bytesRemaining + " byte(s).");
+ return 8 + size + bytesRemaining;
+ }
+
+ /**
+ * This method serializes this escher record into a byte array.
+ *
+ * @param offset The offset into <code>data</code> to start writing the record data to.
+ * @param data The byte array to serialize to.
+ * @param listener A listener to retrieve start and end callbacks. Use a <code>NullEscherSerailizationListener</code> to ignore these events.
+ * @return The number of bytes written.
+ *
+ * @see NullEscherSerializationListener
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+// int field_2_numIdClusters = field_5_fileIdClusters.length + 1;
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ int pos = offset;
+ LittleEndian.putShort( data, pos, getOptions() ); pos += 2;
+ LittleEndian.putShort( data, pos, getRecordId() ); pos += 2;
+ int remainingBytes = getRecordSize() - 8;
+
+ LittleEndian.putInt( data, pos, remainingBytes ); pos += 4;
+ LittleEndian.putInt( data, pos, field_1_color1 ); pos += 4;
+ LittleEndian.putInt( data, pos, field_2_color2 ); pos += 4;
+ LittleEndian.putInt( data, pos, field_3_color3 ); pos += 4;
+ LittleEndian.putInt( data, pos, field_4_color4 ); pos += 4;
+ listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
+ return getRecordSize();
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + 4 * 4;
+ }
+
+ /**
+ * @return the 16 bit identifer for this record.
+ */
+ public short getRecordId()
+ {
+ return RECORD_ID;
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "SplitMenuColors";
+ }
+
+ /**
+ * @return a string representation of this record.
+ */
+ public String toString()
+ {
+ String nl = System.getProperty("line.separator");
+
+// String extraData;
+// ByteArrayOutputStream b = new ByteArrayOutputStream();
+// try
+// {
+// HexDump.dump(this.remainingData, 0, b, 0);
+// extraData = b.toString();
+// }
+// catch ( Exception e )
+// {
+// extraData = "error";
+// }
+ return getClass().getName() + ":" + nl +
+ " RecordId: 0x" + HexDump.toHex(RECORD_ID) + nl +
+ " Options: 0x" + HexDump.toHex(getOptions()) + nl +
+ " Color1: 0x" + HexDump.toHex(field_1_color1) + nl +
+ " Color2: 0x" + HexDump.toHex(field_2_color2) + nl +
+ " Color3: 0x" + HexDump.toHex(field_3_color3) + nl +
+ " Color4: 0x" + HexDump.toHex(field_4_color4) + nl +
+ "";
+
+ }
+
+ public int getColor1()
+ {
+ return field_1_color1;
+ }
+
+ public void setColor1( int field_1_color1 )
+ {
+ this.field_1_color1 = field_1_color1;
+ }
+
+ public int getColor2()
+ {
+ return field_2_color2;
+ }
+
+ public void setColor2( int field_2_color2 )
+ {
+ this.field_2_color2 = field_2_color2;
+ }
+
+ public int getColor3()
+ {
+ return field_3_color3;
+ }
+
+ public void setColor3( int field_3_color3 )
+ {
+ this.field_3_color3 = field_3_color3;
+ }
+
+ public int getColor4()
+ {
+ return field_4_color4;
+ }
+
+ public void setColor4( int field_4_color4 )
+ {
+ this.field_4_color4 = field_4_color4;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/EscherTextboxRecord.java b/src/java/org/apache/poi/ddf/EscherTextboxRecord.java
new file mode 100644
index 0000000000..34dc4fca29
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/EscherTextboxRecord.java
@@ -0,0 +1,215 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hssf.record.RecordFormatException;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Supports text boxes
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherTextboxRecord extends EscherRecord
+{
+ public static final short RECORD_ID = (short)0xF00D;
+ public static final String RECORD_DESCRIPTION = "msofbtClientTextbox";
+
+ private static final byte[] NO_BYTES = new byte[0];
+
+ /** The data for this record not including the the 8 byte header */
+ private byte[] thedata = NO_BYTES;
+
+ public EscherTextboxRecord()
+ {
+ }
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ if ( isContainerRecord() )
+ {
+ int bytesWritten = 0;
+ thedata = new byte[0];
+ offset += 8;
+ bytesWritten += 8;
+ while ( bytesRemaining > 0 )
+ {
+ EscherRecord child = recordFactory.createRecord( data, offset );
+ int childBytesWritten = child.fillFields( data, offset, recordFactory );
+ bytesWritten += childBytesWritten;
+ offset += childBytesWritten;
+ bytesRemaining -= childBytesWritten;
+ getChildRecords().add( child );
+ }
+ return bytesWritten;
+ }
+ else
+ {
+ thedata = new byte[bytesRemaining];
+ System.arraycopy( data, offset + 8, thedata, 0, bytesRemaining );
+ return bytesRemaining + 8;
+ }
+ }
+
+ /**
+ * Writes this record and any contained records to the supplied byte
+ * array.
+ *
+ * @return the number of bytes written.
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ LittleEndian.putShort(data, offset, getOptions());
+ LittleEndian.putShort(data, offset+2, getRecordId());
+ int remainingBytes = thedata.length;
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ remainingBytes += r.getRecordSize();
+ }
+ LittleEndian.putInt(data, offset+4, remainingBytes);
+ System.arraycopy(thedata, 0, data, offset+8, thedata.length);
+ int pos = offset+8+thedata.length;
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ pos += r.serialize(pos, data, listener );
+ }
+
+ listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
+ int size = pos - offset;
+ if (size != getRecordSize())
+ throw new RecordFormatException(size + " bytes written but getRecordSize() reports " + getRecordSize());
+ return size;
+ }
+
+ /**
+ * Returns any extra data associated with this record. In practice excel
+ * does not seem to put anything here.
+ */
+ public byte[] getData()
+ {
+ return thedata;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + thedata.length;
+ }
+
+ public Object clone()
+ {
+ // shallow clone
+ return super.clone();
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "ClientTextbox";
+ }
+
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separator" );
+
+ String theDumpHex = "";
+ try
+ {
+ if (thedata.length != 0)
+ {
+ theDumpHex = " Extra Data:" + nl;
+ theDumpHex += HexDump.dump(thedata, 0, 0);
+ }
+ }
+ catch ( Exception e )
+ {
+ theDumpHex = "Error!!";
+ }
+
+ return getClass().getName() + ":" + nl +
+ " isContainer: " + isContainerRecord() + nl +
+ " options: 0x" + HexDump.toHex( getOptions() ) + nl +
+ " recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
+ " numchildren: " + getChildRecords().size() + nl +
+ theDumpHex;
+ }
+
+}
+
+
+
diff --git a/src/java/org/apache/poi/ddf/NullEscherSerializationListener.java b/src/java/org/apache/poi/ddf/NullEscherSerializationListener.java
new file mode 100644
index 0000000000..10a1e798c4
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/NullEscherSerializationListener.java
@@ -0,0 +1,20 @@
+package org.apache.poi.ddf;
+
+/**
+ * Ignores all serialization events.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class NullEscherSerializationListener implements EscherSerializationListener
+{
+ public void beforeRecordSerialize( int offset, short recordId, EscherRecord record )
+ {
+ // do nothing
+ }
+
+ public void afterRecordSerialize( int offset, short recordId, int size, EscherRecord record )
+ {
+ // do nothing
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherBSERecord.java b/src/java/org/apache/poi/ddf/TestEscherBSERecord.java
new file mode 100644
index 0000000000..3778347d71
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherBSERecord.java
@@ -0,0 +1,87 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+import java.io.IOException;
+
+public class TestEscherBSERecord extends TestCase
+{
+ public void testFillFields() throws Exception
+ {
+ String data = "01 00 00 00 24 00 00 00 05 05 01 02 03 04 " +
+ " 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 00 00 00 " +
+ " 00 00 02 00 00 00 03 00 00 00 04 05 06 07";
+ EscherBSERecord r = new EscherBSERecord();
+ int bytesWritten = r.fillFields( HexRead.readFromString( data ), 0, new DefaultEscherRecordFactory() );
+ assertEquals( 44, bytesWritten );
+ assertEquals( (short) 0x0001, r.getOptions() );
+ assertEquals( EscherBSERecord.BT_JPEG, r.getBlipTypeWin32() );
+ assertEquals( EscherBSERecord.BT_JPEG, r.getBlipTypeMacOS() );
+ assertEquals( "[01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00, ]", HexDump.toHex( r.getUid() ) );
+ assertEquals( (short) 1, r.getTag() );
+ assertEquals( 2, r.getRef() );
+ assertEquals( 3, r.getOffset() );
+ assertEquals( (byte) 4, r.getUsage() );
+ assertEquals( (byte) 5, r.getName() );
+ assertEquals( (byte) 6, r.getUnused2() );
+ assertEquals( (byte) 7, r.getUnused3() );
+ assertEquals( 0, r.getRemainingData().length );
+ }
+
+ public void testSerialize() throws Exception
+ {
+ EscherBSERecord r = createRecord();
+
+ byte[] data = new byte[8 + 36];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 44, bytesWritten );
+ assertEquals( "[01, 00, 00, 00, 24, 00, 00, 00, 05, 05, 01, 02, 03, 04, " +
+ "05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00, 01, 00, 00, 00, " +
+ "00, 00, 02, 00, 00, 00, 03, 00, 00, 00, 04, 05, 06, 07, ]",
+ HexDump.toHex( data ) );
+
+ }
+
+ private EscherBSERecord createRecord() throws IOException
+ {
+ EscherBSERecord r = new EscherBSERecord();
+ r.setOptions( (short) 0x0001 );
+ r.setBlipTypeWin32( EscherBSERecord.BT_JPEG );
+ r.setBlipTypeMacOS( EscherBSERecord.BT_JPEG );
+ r.setUid( HexRead.readFromString( "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00" ) );
+ r.setTag( (short) 1 );
+ r.setRef( 2 );
+ r.setOffset( 3 );
+ r.setUsage( (byte) 4 );
+ r.setName( (byte) 5 );
+ r.setUnused2( (byte) 6 );
+ r.setUnused3( (byte) 7 );
+ r.setRemainingData( new byte[0] );
+ return r;
+
+ }
+
+ public void testToString() throws Exception
+ {
+ EscherBSERecord record = createRecord();
+ String nl = System.getProperty("line.separator");
+ assertEquals( "org.apache.poi.ddf.EscherBSERecord:" + nl +
+ " RecordId: 0xF007" + nl +
+ " Options: 0x0001" + nl +
+ " BlipTypeWin32: 5" + nl +
+ " BlipTypeMacOS: 5" + nl +
+ " SUID: [01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00, ]" + nl +
+ " Tag: 1" + nl +
+ " Size: 0" + nl +
+ " Ref: 2" + nl +
+ " Offset: 3" + nl +
+ " Usage: 4" + nl +
+ " Name: 5" + nl +
+ " Unused2: 6" + nl +
+ " Unused3: 7" + nl +
+ " Extra Data:" + nl, record.toString() );
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherBlipRecord.java b/src/java/org/apache/poi/ddf/TestEscherBlipRecord.java
new file mode 100644
index 0000000000..184dc53590
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherBlipRecord.java
@@ -0,0 +1,110 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherBlipRecord extends TestCase
+{
+ private String dataStr;
+ private byte[] data;
+
+ protected void setUp() throws Exception
+ {
+ dataStr = "2C 15 18 F0 34 00 00 00 01 01 01 01 01 01 01 01 " +
+ "01 01 01 01 01 01 01 01 06 00 00 00 03 00 00 00 " +
+ "01 00 00 00 04 00 00 00 02 00 00 00 0A 00 00 00 " +
+ "0B 00 00 00 05 00 00 00 08 07 01 02";
+ data = HexRead.readFromString(dataStr);
+ }
+
+ public void testSerialize() throws Exception
+ {
+ EscherBlipRecord r = new EscherBlipRecord();
+ r.setBoundaryLeft(1);
+ r.setBoundaryHeight(2);
+ r.setBoundaryTop(3);
+ r.setBoundaryWidth(4);
+ r.setCacheOfSavedSize(5);
+ r.setCacheOfSize(6);
+ r.setFilter((byte)7);
+ r.setCompressionFlag((byte)8);
+ r.setSecondaryUID(new byte[] { (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01,
+ (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01,
+ (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01,
+ (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, });
+ r.setWidth(10);
+ r.setHeight(11);
+ r.setRecordId(EscherBlipRecord.RECORD_ID_START);
+ r.setOptions((short)5420);
+ r.setData(new byte[] { (byte)0x01, (byte)0x02 } );
+
+ byte[] buf = new byte[r.getRecordSize()];
+ r.serialize(0, buf, new NullEscherSerializationListener() );
+
+ assertEquals("[2C, 15, 18, F0, 26, 00, 00, 00, " +
+ "01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, " +
+ "06, 00, 00, 00, " + // field_2_cacheOfSize
+ "03, 00, 00, 00, " + // field_3_boundaryTop
+ "01, 00, 00, 00, " + // field_4_boundaryLeft
+ "04, 00, 00, 00, " + // field_5_boundaryWidth
+ "02, 00, 00, 00, " + // field_6_boundaryHeight
+ "0A, 00, 00, 00, " + // field_7_x
+ "0B, 00, 00, 00, " + // field_8_y
+ "05, 00, 00, 00, " + // field_9_cacheOfSavedSize
+ "08, " + // field_10_compressionFlag
+ "07, " + // field_11_filter
+ "01, 02, ]", // field_12_data
+ HexDump.toHex(buf));
+ assertEquals(60, r.getRecordSize() );
+
+ }
+
+ public void testFillFields() throws Exception
+ {
+ EscherBlipRecord r = new EscherBlipRecord();
+ r.fillFields( data, 0, new DefaultEscherRecordFactory());
+
+ assertEquals( EscherBlipRecord.RECORD_ID_START, r.getRecordId() );
+ assertEquals( 1, r.getBoundaryLeft() );
+ assertEquals( 2, r.getBoundaryHeight() );
+ assertEquals( 3, r.getBoundaryTop() );
+ assertEquals( 4, r.getBoundaryWidth() );
+ assertEquals( 5, r.getCacheOfSavedSize() );
+ assertEquals( 6, r.getCacheOfSize() );
+ assertEquals( 7, r.getFilter() );
+ assertEquals( 8, r.getCompressionFlag() );
+ assertEquals( "[01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, ]", HexDump.toHex(r.getSecondaryUID() ) );
+ assertEquals( 10, r.getWidth() );
+ assertEquals( 11, r.getHeight() );
+ assertEquals( (short)5420, r.getOptions() );
+ assertEquals( "[01, 02, ]", HexDump.toHex( r.getData() ) );
+ }
+
+ public void testToString() throws Exception
+ {
+ EscherBlipRecord r = new EscherBlipRecord();
+ r.fillFields( data, 0, new DefaultEscherRecordFactory() );
+
+ String nl = System.getProperty("line.separator");
+
+ assertEquals( "org.apache.poi.ddf.EscherBlipRecord:" + nl +
+ " RecordId: 0xF018" + nl +
+ " Options: 0x152C" + nl +
+ " Secondary UID: [01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, ]" + nl +
+ " CacheOfSize: 6" + nl +
+ " BoundaryTop: 3" + nl +
+ " BoundaryLeft: 1" + nl +
+ " BoundaryWidth: 4" + nl +
+ " BoundaryHeight: 2" + nl +
+ " X: 10" + nl +
+ " Y: 11" + nl +
+ " CacheOfSavedSize: 5" + nl +
+ " CompressionFlag: 8" + nl +
+ " Filter: 7" + nl +
+ " Data:" + nl +
+ "00000000 01 02 .." + nl
+ , r.toString() );
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherBoolProperty.java b/src/java/org/apache/poi/ddf/TestEscherBoolProperty.java
new file mode 100644
index 0000000000..4f16ffddfd
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherBoolProperty.java
@@ -0,0 +1,13 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+
+public class TestEscherBoolProperty extends TestCase
+{
+ public void testToString() throws Exception
+ {
+ EscherBoolProperty p = new EscherBoolProperty((short)1, 1);
+ assertEquals("propNum: 1, propName: unknown, complex: false, blipId: false, value: 1 (0x00000001)", p.toString());
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherChildAnchorRecord.java b/src/java/org/apache/poi/ddf/TestEscherChildAnchorRecord.java
new file mode 100644
index 0000000000..6c58d449e3
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherChildAnchorRecord.java
@@ -0,0 +1,73 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherChildAnchorRecord extends TestCase
+{
+ public void testSerialize() throws Exception
+ {
+ EscherChildAnchorRecord r = createRecord();
+
+ byte[] data = new byte[8 + 16];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 24, bytesWritten );
+ assertEquals( "[01, 00, " +
+ "0F, F0, " +
+ "10, 00, 00, 00, " +
+ "01, 00, 00, 00, " +
+ "02, 00, 00, 00, " +
+ "03, 00, 00, 00, " +
+ "04, 00, 00, 00, ]", HexDump.toHex( data ) );
+ }
+
+ public void testFillFields() throws Exception
+ {
+ String hexData = "01 00 " +
+ "0F F0 " +
+ "10 00 00 00 " +
+ "01 00 00 00 " +
+ "02 00 00 00 " +
+ "03 00 00 00 " +
+ "04 00 00 00 ";
+
+ byte[] data = HexRead.readFromString( hexData );
+ EscherChildAnchorRecord r = new EscherChildAnchorRecord();
+ int bytesWritten = r.fillFields( data, new DefaultEscherRecordFactory() );
+
+ assertEquals( 24, bytesWritten );
+ assertEquals( 1, r.getDx1() );
+ assertEquals( 2, r.getDy1() );
+ assertEquals( 3, r.getDx2() );
+ assertEquals( 4, r.getDy2() );
+ assertEquals( (short) 0x0001, r.getOptions() );
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty( "line.separator" );
+
+ String expected = "org.apache.poi.ddf.EscherChildAnchorRecord:" + nl +
+ " RecordId: 0xF00F" + nl +
+ " Options: 0x0001" + nl +
+ " X1: 1" + nl +
+ " Y1: 2" + nl +
+ " X2: 3" + nl +
+ " Y2: 4" + nl;
+ assertEquals( expected, createRecord().toString() );
+ }
+
+ private EscherChildAnchorRecord createRecord()
+ {
+ EscherChildAnchorRecord r = new EscherChildAnchorRecord();
+ r.setRecordId( EscherChildAnchorRecord.RECORD_ID );
+ r.setOptions( (short) 0x0001 );
+ r.setDx1( 1 );
+ r.setDy1( 2 );
+ r.setDx2( 3 );
+ r.setDy2( 4 );
+ return r;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherClientAnchorRecord.java b/src/java/org/apache/poi/ddf/TestEscherClientAnchorRecord.java
new file mode 100644
index 0000000000..acd84625df
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherClientAnchorRecord.java
@@ -0,0 +1,91 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherClientAnchorRecord extends TestCase
+{
+ public void testSerialize() throws Exception
+ {
+ EscherClientAnchorRecord r = createRecord();
+
+ byte[] data = new byte[8 + 18 + 2];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 28, bytesWritten );
+ assertEquals( "[01, 00, " +
+ "10, F0, " +
+ "14, 00, 00, 00, " +
+ "4D, 00, 37, 00, 21, 00, 58, 00, " +
+ "0B, 00, 2C, 00, 16, 00, 63, 00, " +
+ "42, 00, " +
+ "FF, DD, ]", HexDump.toHex( data ) );
+ }
+
+ public void testFillFields() throws Exception
+ {
+ String hexData = "01 00 " +
+ "10 F0 " +
+ "14 00 00 00 " +
+ "4D 00 37 00 21 00 58 00 " +
+ "0B 00 2C 00 16 00 63 00 " +
+ "42 00 " +
+ "FF DD";
+ byte[] data = HexRead.readFromString( hexData );
+ EscherClientAnchorRecord r = new EscherClientAnchorRecord();
+ int bytesWritten = r.fillFields( data, new DefaultEscherRecordFactory() );
+
+ assertEquals( 28, bytesWritten );
+ assertEquals( (short) 55, r.getCol1() );
+ assertEquals( (short) 44, r.getCol2() );
+ assertEquals( (short) 33, r.getDx1() );
+ assertEquals( (short) 22, r.getDx2() );
+ assertEquals( (short) 11, r.getDy1() );
+ assertEquals( (short) 66, r.getDy2() );
+ assertEquals( (short) 77, r.getFlag() );
+ assertEquals( (short) 88, r.getRow1() );
+ assertEquals( (short) 99, r.getRow2() );
+ assertEquals( (short) 0x0001, r.getOptions() );
+ assertEquals( (byte) 0xFF, r.getRemainingData()[0] );
+ assertEquals( (byte) 0xDD, r.getRemainingData()[1] );
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty("line.separator");
+
+ String expected = "org.apache.poi.ddf.EscherClientAnchorRecord:" + nl +
+ " RecordId: 0xF010" + nl +
+ " Options: 0x0001" + nl +
+ " Flag: 77" + nl +
+ " Col1: 55" + nl +
+ " DX1: 33" + nl +
+ " Row1: 88" + nl +
+ " DY1: 11" + nl +
+ " Col2: 44" + nl +
+ " DX2: 22" + nl +
+ " Row2: 99" + nl +
+ " DY2: 66" + nl +
+ " Extra Data:" + nl +
+ "00000000 FF DD .." + nl;
+ assertEquals( expected, createRecord().toString() );
+ }
+
+ private EscherClientAnchorRecord createRecord()
+ {
+ EscherClientAnchorRecord r = new EscherClientAnchorRecord();
+ r.setCol1( (short) 55 );
+ r.setCol2( (short) 44 );
+ r.setDx1( (short) 33 );
+ r.setDx2( (short) 22 );
+ r.setDy1( (short) 11 );
+ r.setDy2( (short) 66 );
+ r.setFlag( (short) 77 );
+ r.setRow1( (short) 88 );
+ r.setRow2( (short) 99 );
+ r.setOptions( (short) 0x0001 );
+ r.setRemainingData( new byte[]{(byte) 0xFF, (byte) 0xDD} );
+ return r;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherClientDataRecord.java b/src/java/org/apache/poi/ddf/TestEscherClientDataRecord.java
new file mode 100644
index 0000000000..a91e36896a
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherClientDataRecord.java
@@ -0,0 +1,57 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherClientDataRecord extends TestCase
+{
+ public void testSerialize() throws Exception
+ {
+ EscherClientDataRecord r = createRecord();
+
+ byte[] data = new byte[8];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 8, bytesWritten );
+ assertEquals( "[02, 00, " +
+ "11, F0, " +
+ "00, 00, 00, 00, ]",
+ HexDump.toHex( data ) );
+ }
+
+ public void testFillFields() throws Exception
+ {
+ String hexData = "02 00 " +
+ "11 F0 " +
+ "00 00 00 00 ";
+ byte[] data = HexRead.readFromString( hexData );
+ EscherClientDataRecord r = new EscherClientDataRecord();
+ int bytesWritten = r.fillFields( data, new DefaultEscherRecordFactory() );
+
+ assertEquals( 8, bytesWritten );
+ assertEquals( (short)0xF011, r.getRecordId() );
+ assertEquals( "[]", HexDump.toHex(r.getRemainingData()) );
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty("line.separator");
+
+ String expected = "org.apache.poi.ddf.EscherClientDataRecord:" + nl +
+ " RecordId: 0xF011" + nl +
+ " Options: 0x0002" + nl +
+ " Extra Data:" + nl +
+ "" ;
+ assertEquals( expected, createRecord().toString() );
+ }
+
+ private EscherClientDataRecord createRecord()
+ {
+ EscherClientDataRecord r = new EscherClientDataRecord();
+ r.setOptions( (short) 0x0002 );
+ r.setRecordId( EscherClientDataRecord.RECORD_ID );
+ r.setRemainingData( new byte[] {} );
+ return r;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherContainerRecord.java b/src/java/org/apache/poi/ddf/TestEscherContainerRecord.java
new file mode 100644
index 0000000000..9d74a3ee3e
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherContainerRecord.java
@@ -0,0 +1,98 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexRead;
+import org.apache.poi.util.HexDump;
+
+public class TestEscherContainerRecord extends TestCase
+{
+ public void testFillFields() throws Exception
+ {
+ EscherRecordFactory f = new DefaultEscherRecordFactory();
+ byte[] data = HexRead.readFromString( "0F 02 11 F1 00 00 00 00" );
+ EscherRecord r = f.createRecord( data, 0 );
+ r.fillFields( data, 0, f );
+ assertTrue( r instanceof EscherContainerRecord );
+ assertEquals( (short) 0x020F, r.getOptions() );
+ assertEquals( (short) 0xF111, r.getRecordId() );
+
+ data = HexRead.readFromString( "0F 02 11 F1 08 00 00 00" +
+ " 02 00 22 F2 00 00 00 00" );
+ r = f.createRecord( data, 0 );
+ r.fillFields( data, 0, f );
+ EscherRecord c = r.getChild( 0 );
+ assertFalse( c instanceof EscherContainerRecord );
+ assertEquals( (short) 0x0002, c.getOptions() );
+ assertEquals( (short) 0xF222, c.getRecordId() );
+ }
+
+ public void testSerialize() throws Exception
+ {
+ UnknownEscherRecord r = new UnknownEscherRecord();
+ r.setOptions( (short) 0x123F );
+ r.setRecordId( (short) 0xF112 );
+ byte[] data = new byte[8];
+ r.serialize( 0, data, new NullEscherSerializationListener() );
+
+ assertEquals( "[3F, 12, 12, F1, 00, 00, 00, 00, ]", HexDump.toHex( data ) );
+
+ EscherRecord childRecord = new UnknownEscherRecord();
+ childRecord.setOptions( (short) 0x9999 );
+ childRecord.setRecordId( (short) 0xFF01 );
+ r.addChildRecord( childRecord );
+ data = new byte[16];
+ r.serialize( 0, data, new NullEscherSerializationListener() );
+
+ assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00, ]", HexDump.toHex( data ) );
+
+ }
+
+ public void testToString() throws Exception
+ {
+ EscherContainerRecord r = new EscherContainerRecord();
+ r.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ r.setOptions( (short) 0x000F );
+ String nl = System.getProperty( "line.separator" );
+ assertEquals( "org.apache.poi.ddf.EscherContainerRecord (SpContainer):" + nl +
+ " isContainer: true" + nl +
+ " options: 0x000F" + nl +
+ " recordId: 0xF004" + nl +
+ " numchildren: 0" + nl
+ , r.toString() );
+
+ EscherOptRecord r2 = new EscherOptRecord();
+ r2.setOptions( (short) 0x9876 );
+ r2.setRecordId( EscherOptRecord.RECORD_ID );
+
+ r.addChildRecord( r2 );
+ String expected = "org.apache.poi.ddf.EscherContainerRecord (SpContainer):" + nl +
+ " isContainer: true" + nl +
+ " options: 0x000F" + nl +
+ " recordId: 0xF004" + nl +
+ " numchildren: 1" + nl +
+ " children: " + nl +
+ "org.apache.poi.ddf.EscherOptRecord:" + nl +
+ " isContainer: false" + nl +
+ " options: 0x0003" + nl +
+ " recordId: 0xF00B" + nl +
+ " numchildren: 0" + nl +
+ " properties:" + nl;
+ assertEquals( expected, r.toString() );
+
+ }
+
+ public void testGetRecordSize() throws Exception
+ {
+ EscherContainerRecord r = new EscherContainerRecord();
+ r.addChildRecord(new EscherRecord()
+ {
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory ) { return 0; }
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener ) { return 0; }
+ public int getRecordSize() { return 10; }
+ public String getRecordName() { return ""; }
+ } );
+
+ assertEquals(18, r.getRecordSize());
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherDgRecord.java b/src/java/org/apache/poi/ddf/TestEscherDgRecord.java
new file mode 100644
index 0000000000..a7fa9bb52e
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherDgRecord.java
@@ -0,0 +1,62 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherDgRecord extends TestCase
+{
+ public void testSerialize() throws Exception
+ {
+ EscherDgRecord r = createRecord();
+
+ byte[] data = new byte[16];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 16, bytesWritten );
+ assertEquals( "[10, 00, " +
+ "08, F0, " +
+ "08, 00, 00, 00, " +
+ "02, 00, 00, 00, " + // num shapes in drawing
+ "01, 04, 00, 00, ]", // The last MSOSPID given to an SP in this DG
+ HexDump.toHex( data ) );
+ }
+
+ public void testFillFields() throws Exception
+ {
+ String hexData = "10 00 " +
+ "08 F0 " +
+ "08 00 00 00 " +
+ "02 00 00 00 " +
+ "01 04 00 00 ";
+ byte[] data = HexRead.readFromString( hexData );
+ EscherDgRecord r = new EscherDgRecord();
+ int bytesWritten = r.fillFields( data, new DefaultEscherRecordFactory() );
+
+ assertEquals( 16, bytesWritten );
+ assertEquals( 2, r.getNumShapes() );
+ assertEquals( 1025, r.getLastMSOSPID() );
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty("line.separator");
+
+ String expected = "org.apache.poi.ddf.EscherDgRecord:" + nl +
+ " RecordId: 0xF008" + nl +
+ " Options: 0x0010" + nl +
+ " NumShapes: 2" + nl +
+ " LastMSOSPID: 1025" + nl;
+ assertEquals( expected, createRecord().toString() );
+ }
+
+ private EscherDgRecord createRecord()
+ {
+ EscherDgRecord r = new EscherDgRecord();
+ r.setOptions( (short) 0x0010 );
+ r.setRecordId( EscherDgRecord.RECORD_ID );
+ r.setNumShapes(2);
+ r.setLastMSOSPID(1025);
+ return r;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherDggRecord.java b/src/java/org/apache/poi/ddf/TestEscherDggRecord.java
new file mode 100644
index 0000000000..b632ae9902
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherDggRecord.java
@@ -0,0 +1,89 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherDggRecord extends TestCase
+{
+ public void testSerialize() throws Exception
+ {
+ EscherDggRecord r = createRecord();
+
+ byte[] data = new byte[32];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 32, bytesWritten );
+ assertEquals( "[00, 00, " +
+ "06, F0, " +
+ "18, 00, 00, 00, " +
+ "02, 04, 00, 00, " +
+ "02, 00, 00, 00, " +
+ "02, 00, 00, 00, " +
+ "01, 00, 00, 00, " +
+ "01, 00, 00, 00, 02, 00, 00, 00, ]",
+ HexDump.toHex( data ) );
+ }
+
+ public void testFillFields() throws Exception
+ {
+ String hexData = "00 00 " +
+ "06 F0 " +
+ "18 00 00 00 " +
+ "02 04 00 00 " +
+ "02 00 00 00 " +
+ "02 00 00 00 " +
+ "01 00 00 00 " +
+ "01 00 00 00 02 00 00 00";
+ byte[] data = HexRead.readFromString( hexData );
+ EscherDggRecord r = new EscherDggRecord();
+ int bytesWritten = r.fillFields( data, new DefaultEscherRecordFactory() );
+
+ assertEquals( 32, bytesWritten );
+ assertEquals( 0x402, r.getShapeIdMax() );
+ assertEquals( 0x02, r.getNumIdClusters() );
+ assertEquals( 0x02, r.getNumShapesSaved() );
+ assertEquals( 0x01, r.getDrawingsSaved() );
+ assertEquals( 1, r.getFileIdClusters().length );
+ assertEquals( 0x01, r.getFileIdClusters()[0].getDrawingGroupId());
+ assertEquals( 0x02, r.getFileIdClusters()[0].getNumShapeIdsUsed());
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty("line.separator");
+
+ String expected = "org.apache.poi.ddf.EscherDggRecord:" + nl +
+ " RecordId: 0xF006" + nl +
+ " Options: 0x0000" + nl +
+ " ShapeIdMax: 1026" + nl +
+ " NumIdClusters: 2" + nl +
+ " NumShapesSaved: 2" + nl +
+ " DrawingsSaved: 1" + nl +
+ " DrawingGroupId1: 1" + nl +
+ " NumShapeIdsUsed1: 2" + nl;
+ assertEquals( expected, createRecord().toString() );
+ }
+
+ private EscherDggRecord createRecord()
+ {
+ EscherDggRecord r = new EscherDggRecord();
+ r.setOptions( (short) 0x0000 );
+ r.setRecordId( EscherDggRecord.RECORD_ID );
+ r.setShapeIdMax( 0x402 );
+ r.setNumShapesSaved( 0x02 );
+ r.setDrawingsSaved( 0x01 );
+ r.setFileIdClusters(new EscherDggRecord.FileIdCluster[] {
+ new EscherDggRecord.FileIdCluster( 1, 2 )
+ });
+ return r;
+ }
+
+ public void testGetRecordSize() throws Exception
+ {
+ EscherDggRecord r = new EscherDggRecord();
+ r.setFileIdClusters(new EscherDggRecord.FileIdCluster[] { new EscherDggRecord.FileIdCluster(0,0) } );
+ assertEquals(32,r.getRecordSize());
+
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherOptRecord.java b/src/java/org/apache/poi/ddf/TestEscherOptRecord.java
new file mode 100644
index 0000000000..dc5511a7b9
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherOptRecord.java
@@ -0,0 +1,140 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexRead;
+import org.apache.poi.util.HexDump;
+
+import java.io.IOException;
+
+public class TestEscherOptRecord extends TestCase
+{
+
+ public void testFillFields() throws Exception
+ {
+ checkFillFieldsSimple();
+ checkFillFieldsComplex();
+ }
+
+ private void checkFillFieldsComplex() throws IOException
+ {
+ String dataStr = "33 00 " +
+ "0B F0 " +
+ "14 00 00 00 " +
+ "BF 00 01 00 00 00 " +
+ "01 80 02 00 00 00 " +
+ "BF 00 01 00 00 00 " +
+ "01 02";
+
+ EscherOptRecord r = new EscherOptRecord();
+ r.fillFields( HexRead.readFromString( dataStr ), new DefaultEscherRecordFactory() );
+ assertEquals( (short) 0x0033, r.getOptions() );
+ assertEquals( (short) 0xF00B, r.getRecordId() );
+ assertEquals( 3, r.getEscherProperties().size() );
+ EscherBoolProperty prop1 = new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 1 );
+ EscherComplexProperty prop2 = new EscherComplexProperty( (short) 1, false, new byte[] { 0x01, 0x02 } );
+ EscherBoolProperty prop3 = new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 1 );
+ assertEquals( prop1, r.getEscherProperty( 0 ) );
+ assertEquals( prop2, r.getEscherProperty( 1 ) );
+ assertEquals( prop3, r.getEscherProperty( 2 ) );
+
+ }
+
+ private void checkFillFieldsSimple()
+ throws IOException
+ {
+ String dataStr = "33 00 " + // options
+ "0B F0 " + // recordid
+ "12 00 00 00 " + // remaining bytes
+ "BF 00 08 00 08 00 " +
+ "81 01 09 00 00 08 " +
+ "C0 01 40 00 00 08";
+
+ EscherOptRecord r = new EscherOptRecord();
+ r.fillFields( HexRead.readFromString( dataStr ), new DefaultEscherRecordFactory() );
+ assertEquals( (short) 0x0033, r.getOptions() );
+ assertEquals( (short) 0xF00B, r.getRecordId() );
+ assertEquals( 3, r.getEscherProperties().size() );
+ EscherBoolProperty prop1 = new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 524296 );
+ EscherRGBProperty prop2 = new EscherRGBProperty( EscherProperties.FILL__FILLCOLOR, 0x08000009 );
+ EscherRGBProperty prop3 = new EscherRGBProperty( EscherProperties.LINESTYLE__COLOR, 0x08000040 );
+ assertEquals( prop1, r.getEscherProperty( 0 ) );
+ assertEquals( prop2, r.getEscherProperty( 1 ) );
+ assertEquals( prop3, r.getEscherProperty( 2 ) );
+ }
+
+ public void testSerialize() throws Exception
+ {
+ checkSerializeSimple();
+ checkSerializeComplex();
+ }
+
+ private void checkSerializeComplex()
+ {
+ //Complex escher record
+ EscherOptRecord r = new EscherOptRecord();
+ r.setOptions( (short) 0x0033 );
+ r.setRecordId( (short) 0xF00B );
+ EscherBoolProperty prop1 = new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 1 );
+ EscherComplexProperty prop2 = new EscherComplexProperty( (short) 1, false, new byte[] { 0x01, 0x02 } );
+ EscherBoolProperty prop3 = new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 1 );
+ r.addEscherProperty( prop1 );
+ r.addEscherProperty( prop2 );
+ r.addEscherProperty( prop3 );
+
+ byte[] data = new byte[28];
+ int bytesWritten = r.serialize(0, data, new NullEscherSerializationListener() );
+ assertEquals( 28, bytesWritten );
+ String dataStr = "[33, 00, " +
+ "0B, F0, " +
+ "14, 00, 00, 00, " +
+ "BF, 00, 01, 00, 00, 00, " +
+ "01, 80, 02, 00, 00, 00, " +
+ "BF, 00, 01, 00, 00, 00, " +
+ "01, 02, ]";
+ assertEquals( dataStr, HexDump.toHex(data) );
+
+ }
+
+ private void checkSerializeSimple()
+ {
+ EscherOptRecord r = new EscherOptRecord();
+ r.setOptions( (short) 0x0033 );
+ r.setRecordId( (short) 0xF00B );
+ EscherBoolProperty prop1 = new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 1 );
+ EscherRGBProperty prop2 = new EscherRGBProperty( EscherProperties.FILL__FILLCOLOR, 0x08000009 );
+ EscherRGBProperty prop3 = new EscherRGBProperty( EscherProperties.LINESTYLE__COLOR, 0x08000040 );
+ r.addEscherProperty( prop1 );
+ r.addEscherProperty( prop2 );
+ r.addEscherProperty( prop3 );
+
+ byte[] data = new byte[26];
+ int bytesWritten = r.serialize(0, data, new NullEscherSerializationListener() );
+ String dataStr = "[33, 00, " +
+ "0B, F0, " +
+ "12, 00, 00, 00, " +
+ "BF, 00, 01, 00, 00, 00, " +
+ "81, 01, 09, 00, 00, 08, " +
+ "C0, 01, 40, 00, 00, 08, ]";
+ assertEquals( dataStr, HexDump.toHex(data) );
+ assertEquals( 26, bytesWritten );
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty("line.separator");
+ EscherOptRecord r = new EscherOptRecord();
+ r.setOptions((short)0x000F);
+ r.setRecordId(EscherOptRecord.RECORD_ID);
+ EscherProperty prop1 = new EscherBoolProperty((short)1, 1);
+ r.addEscherProperty(prop1);
+ String expected = "org.apache.poi.ddf.EscherOptRecord:" + nl +
+ " isContainer: true" + nl +
+ " options: 0x0013" + nl +
+ " recordId: 0x" + HexDump.toHex(EscherOptRecord.RECORD_ID) + nl +
+ " numchildren: 0" + nl +
+ " properties:" + nl +
+ " propNum: 1, propName: unknown, complex: false, blipId: false, value: 1 (0x00000001)" + nl;
+ assertEquals( expected, r.toString());
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherPropertyFactory.java b/src/java/org/apache/poi/ddf/TestEscherPropertyFactory.java
new file mode 100644
index 0000000000..3caa156ad6
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherPropertyFactory.java
@@ -0,0 +1,42 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+import org.apache.poi.util.HexRead;
+import org.apache.poi.util.HexDump;
+
+/**
+ * @author Glen Stampoultzis (glens @ superlinksoftware.com)
+ */
+public class TestEscherPropertyFactory extends TestCase
+{
+ public void testCreateProperties() throws Exception
+ {
+ String dataStr = "41 C1 " + // propid, complex ind
+ "03 00 00 00 " + // size of complex property
+ "01 00 " + // propid, complex ind
+ "00 00 00 00 " + // value
+ "41 C1 " + // propid, complex ind
+ "03 00 00 00 " + // size of complex property
+ "01 02 03 " +
+ "01 02 03 "
+ ;
+ byte[] data = HexRead.readFromString( dataStr );
+ EscherPropertyFactory f = new EscherPropertyFactory();
+ List props = f.createProperties( data, 0, (short)3 );
+ EscherComplexProperty p1 = (EscherComplexProperty) props.get( 0 );
+ assertEquals( (short)0xC141, p1.getId() );
+ assertEquals( "[01, 02, 03, ]", HexDump.toHex( p1.getComplexData() ) );
+
+ EscherComplexProperty p3 = (EscherComplexProperty) props.get( 2 );
+ assertEquals( (short)0xC141, p3.getId() );
+ assertEquals( "[01, 02, 03, ]", HexDump.toHex( p3.getComplexData() ) );
+
+
+ }
+
+
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherSpRecord.java b/src/java/org/apache/poi/ddf/TestEscherSpRecord.java
new file mode 100644
index 0000000000..448aee6540
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherSpRecord.java
@@ -0,0 +1,62 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherSpRecord extends TestCase
+{
+ public void testSerialize() throws Exception
+ {
+ EscherSpRecord r = createRecord();
+
+ byte[] data = new byte[16];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 16, bytesWritten );
+ assertEquals( "[02, 00, " +
+ "0A, F0, " +
+ "08, 00, 00, 00, " +
+ "00, 04, 00, 00, " +
+ "05, 00, 00, 00, ]",
+ HexDump.toHex( data ) );
+ }
+
+ public void testFillFields() throws Exception
+ {
+ String hexData = "02 00 " +
+ "0A F0 " +
+ "08 00 00 00 " +
+ "00 04 00 00 " +
+ "05 00 00 00 ";
+ byte[] data = HexRead.readFromString( hexData );
+ EscherSpRecord r = new EscherSpRecord();
+ int bytesWritten = r.fillFields( data, new DefaultEscherRecordFactory() );
+
+ assertEquals( 16, bytesWritten );
+ assertEquals( 0x0400, r.getShapeId() );
+ assertEquals( 0x05, r.getFlags() );
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty("line.separator");
+
+ String expected = "org.apache.poi.ddf.EscherSpRecord:" + nl +
+ " RecordId: 0xF00A" + nl +
+ " Options: 0x0002" + nl +
+ " ShapeId: 1024" + nl +
+ " Flags: GROUP|PATRIARCH (0x00000005)" + nl;
+ assertEquals( expected, createRecord().toString() );
+ }
+
+ private EscherSpRecord createRecord()
+ {
+ EscherSpRecord r = new EscherSpRecord();
+ r.setOptions( (short) 0x0002 );
+ r.setRecordId( EscherSpRecord.RECORD_ID );
+ r.setShapeId(0x0400);
+ r.setFlags(0x05);
+ return r;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherSpgrRecord.java b/src/java/org/apache/poi/ddf/TestEscherSpgrRecord.java
new file mode 100644
index 0000000000..3ec276ab97
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherSpgrRecord.java
@@ -0,0 +1,73 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherSpgrRecord extends TestCase
+{
+ public void testSerialize() throws Exception
+ {
+ EscherSpgrRecord r = createRecord();
+
+ byte[] data = new byte[24];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 24, bytesWritten );
+ assertEquals( "[10, 00, " +
+ "09, F0, " +
+ "10, 00, 00, 00, " +
+ "01, 00, 00, 00, " + // x
+ "02, 00, 00, 00, " + // y
+ "03, 00, 00, 00, " + // width
+ "04, 00, 00, 00, ]", // height
+ HexDump.toHex( data ) );
+ }
+
+ public void testFillFields() throws Exception
+ {
+ String hexData = "10 00 " +
+ "09 F0 " +
+ "10 00 00 00 " +
+ "01 00 00 00 " +
+ "02 00 00 00 " +
+ "03 00 00 00 " +
+ "04 00 00 00 ";
+ byte[] data = HexRead.readFromString( hexData );
+ EscherSpgrRecord r = new EscherSpgrRecord();
+ int bytesWritten = r.fillFields( data, new DefaultEscherRecordFactory() );
+
+ assertEquals( 24, bytesWritten );
+ assertEquals( 1, r.getRectX1() );
+ assertEquals( 2, r.getRectY1() );
+ assertEquals( 3, r.getRectX2() );
+ assertEquals( 4, r.getRectY2() );
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty("line.separator");
+
+ String expected = "org.apache.poi.ddf.EscherSpgrRecord:" + nl +
+ " RecordId: 0xF009" + nl +
+ " Options: 0x0010" + nl +
+ " RectX: 1" + nl +
+ " RectY: 2" + nl +
+ " RectWidth: 3" + nl +
+ " RectHeight: 4" + nl;
+ ;
+ assertEquals( expected, createRecord().toString() );
+ }
+
+ private EscherSpgrRecord createRecord()
+ {
+ EscherSpgrRecord r = new EscherSpgrRecord();
+ r.setOptions( (short) 0x0010 );
+ r.setRecordId( EscherSpgrRecord.RECORD_ID );
+ r.setRectX1(1);
+ r.setRectY1(2);
+ r.setRectX2(3);
+ r.setRectY2(4);
+ return r;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java b/src/java/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java
new file mode 100644
index 0000000000..2550800c2e
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java
@@ -0,0 +1,73 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+public class TestEscherSplitMenuColorsRecord extends TestCase
+{
+ public void testSerialize() throws Exception
+ {
+ EscherSplitMenuColorsRecord r = createRecord();
+
+ byte[] data = new byte[24];
+ int bytesWritten = r.serialize( 0, data, new NullEscherSerializationListener() );
+ assertEquals( 24, bytesWritten );
+ assertEquals( "[40, 00, " +
+ "1E, F1, " +
+ "10, 00, 00, 00, " +
+ "02, 04, 00, 00, " +
+ "02, 00, 00, 00, " +
+ "02, 00, 00, 00, " +
+ "01, 00, 00, 00, ]",
+ HexDump.toHex( data ) );
+ }
+
+ public void testFillFields() throws Exception
+ {
+ String hexData = "40 00 " +
+ "1E F1 " +
+ "10 00 00 00 " +
+ "02 04 00 00 " +
+ "02 00 00 00 " +
+ "02 00 00 00 " +
+ "01 00 00 00 ";
+ byte[] data = HexRead.readFromString( hexData );
+ EscherSplitMenuColorsRecord r = new EscherSplitMenuColorsRecord();
+ int bytesWritten = r.fillFields( data, new DefaultEscherRecordFactory() );
+
+ assertEquals( 24, bytesWritten );
+ assertEquals( 0x0402, r.getColor1() );
+ assertEquals( 0x02, r.getColor2() );
+ assertEquals( 0x02, r.getColor3() );
+ assertEquals( 0x01, r.getColor4() );
+ }
+
+ public void testToString() throws Exception
+ {
+ String nl = System.getProperty("line.separator");
+
+ String expected = "org.apache.poi.ddf.EscherSplitMenuColorsRecord:" + nl +
+ " RecordId: 0xF11E" + nl +
+ " Options: 0x0040" + nl +
+ " Color1: 0x00000402" + nl +
+ " Color2: 0x00000002" + nl +
+ " Color3: 0x00000002" + nl +
+ " Color4: 0x00000001" + nl +
+ "";
+ assertEquals( expected, createRecord().toString() );
+ }
+
+ private EscherSplitMenuColorsRecord createRecord()
+ {
+ EscherSplitMenuColorsRecord r = new EscherSplitMenuColorsRecord();
+ r.setOptions( (short) 0x0040 );
+ r.setRecordId( EscherSplitMenuColorsRecord.RECORD_ID );
+ r.setColor1( 0x402 );
+ r.setColor2( 0x2 );
+ r.setColor3( 0x2 );
+ r.setColor4( 0x1 );
+ return r;
+ }
+
+}
diff --git a/src/java/org/apache/poi/ddf/TestUnknownEscherRecord.java b/src/java/org/apache/poi/ddf/TestUnknownEscherRecord.java
new file mode 100644
index 0000000000..f61cfc6b4d
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/TestUnknownEscherRecord.java
@@ -0,0 +1,106 @@
+package org.apache.poi.ddf;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexRead;
+import org.apache.poi.util.HexDump;
+
+public class TestUnknownEscherRecord extends TestCase
+{
+ public void testFillFields() throws Exception
+ {
+ String testData =
+ "0F 02 " + // options
+ "11 F1 " + // record id
+ "00 00 00 00"; // remaining bytes
+
+ UnknownEscherRecord r = new UnknownEscherRecord();
+ EscherRecordFactory factory = new DefaultEscherRecordFactory();
+ r.fillFields( HexRead.readFromString( testData ), factory );
+
+ assertEquals( 0x020F, r.getOptions() );
+ assertEquals( (short) 0xF111, r.getRecordId() );
+ assertTrue( r.isContainerRecord() );
+ assertEquals( 8, r.getRecordSize() );
+ assertEquals( 0, r.getChildRecords().size() );
+ assertEquals( 0, r.getData().length );
+
+ testData =
+ "00 02 " + // options
+ "11 F1 " + // record id
+ "04 00 00 00 " + // remaining bytes
+ "01 02 03 04";
+
+ r = new UnknownEscherRecord();
+ r.fillFields( HexRead.readFromString( testData ), factory );
+
+ assertEquals( 0x0200, r.getOptions() );
+ assertEquals( (short) 0xF111, r.getRecordId() );
+ assertEquals( 12, r.getRecordSize() );
+ assertFalse( r.isContainerRecord() );
+ assertEquals( 0, r.getChildRecords().size() );
+ assertEquals( 4, r.getData().length );
+ assertEquals( 1, r.getData()[0] );
+ assertEquals( 2, r.getData()[1] );
+ assertEquals( 3, r.getData()[2] );
+ assertEquals( 4, r.getData()[3] );
+
+ testData =
+ "0F 02 " + // options
+ "11 F1 " + // record id
+ "08 00 00 00 " + // remaining bytes
+ "00 02 " + // options
+ "FF FF " + // record id
+ "00 00 00 00"; // remaining bytes
+
+ r = new UnknownEscherRecord();
+ r.fillFields( HexRead.readFromString( testData ), factory );
+
+ assertEquals( 0x020F, r.getOptions() );
+ assertEquals( (short) 0xF111, r.getRecordId() );
+ assertEquals( 8, r.getRecordSize() );
+ assertTrue( r.isContainerRecord() );
+ assertEquals( 1, r.getChildRecords().size() );
+ assertEquals( (short) 0xFFFF, r.getChild( 0 ).getRecordId() );
+
+ }
+
+ public void testSerialize() throws Exception
+ {
+ UnknownEscherRecord r = new UnknownEscherRecord();
+ r.setOptions( (short) 0x1234 );
+ r.setRecordId( (short) 0xF112 );
+ byte[] data = new byte[8];
+ r.serialize( 0, data, new NullEscherSerializationListener() );
+
+ assertEquals( "[34, 12, 12, F1, 00, 00, 00, 00, ]", HexDump.toHex( data ) );
+
+ EscherRecord childRecord = new UnknownEscherRecord();
+ childRecord.setOptions( (short) 0x9999 );
+ childRecord.setRecordId( (short) 0xFF01 );
+ r.addChildRecord( childRecord );
+ r.setOptions( (short) 0x123F );
+ data = new byte[16];
+ r.serialize( 0, data, new NullEscherSerializationListener() );
+
+ assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00, ]", HexDump.toHex( data ) );
+ }
+
+ public void testToString() throws Exception
+ {
+ UnknownEscherRecord r = new UnknownEscherRecord();
+ r.setOptions( (short) 0x1234 );
+ r.setRecordId( (short) 0xF112 );
+ byte[] data = new byte[8];
+ r.serialize( 0, data, new NullEscherSerializationListener() );
+
+ String nl = System.getProperty("line.separator");
+ assertEquals( "org.apache.poi.ddf.UnknownEscherRecord:" + nl +
+ " isContainer: false" + nl +
+ " options: 0x1234" + nl +
+ " recordId: 0xF112" + nl +
+ " numchildren: 0" + nl
+ , r.toString() );
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/ddf/UnknownEscherRecord.java b/src/java/org/apache/poi/ddf/UnknownEscherRecord.java
new file mode 100644
index 0000000000..e2d21fbabe
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/UnknownEscherRecord.java
@@ -0,0 +1,234 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.ddf;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * This record is used whenever a escher record is encountered that
+ * we do not explicitly support.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class UnknownEscherRecord extends EscherRecord
+{
+ private static final byte[] NO_BYTES = new byte[0];
+
+ /** The data for this record not including the the 8 byte header */
+ private byte[] thedata = NO_BYTES;
+ private List childRecords = new ArrayList();
+
+ public UnknownEscherRecord()
+ {
+ }
+
+ /**
+ * This method deserializes the record from a byte array.
+ *
+ * @param data The byte array containing the escher record information
+ * @param offset The starting offset into <code>data</code>.
+ * @param recordFactory May be null since this is not a container record.
+ * @return The number of bytes read from the byte array.
+ */
+ public int fillFields( byte[] data, int offset, EscherRecordFactory recordFactory )
+ {
+ int bytesRemaining = readHeader( data, offset );
+ if ( isContainerRecord() )
+ {
+ int bytesWritten = 0;
+ thedata = new byte[0];
+ offset += 8;
+ bytesWritten += 8;
+ while ( bytesRemaining > 0 )
+ {
+ EscherRecord child = recordFactory.createRecord( data, offset );
+ int childBytesWritten = child.fillFields( data, offset, recordFactory );
+ bytesWritten += childBytesWritten;
+ offset += childBytesWritten;
+ bytesRemaining -= childBytesWritten;
+ getChildRecords().add( child );
+ }
+ return bytesWritten;
+ }
+ else
+ {
+ thedata = new byte[bytesRemaining];
+ System.arraycopy( data, offset + 8, thedata, 0, bytesRemaining );
+ return bytesRemaining + 8;
+ }
+ }
+
+ /**
+ * Writes this record and any contained records to the supplied byte
+ * array.
+ *
+ * @return the number of bytes written.
+ */
+ public int serialize( int offset, byte[] data, EscherSerializationListener listener )
+ {
+ listener.beforeRecordSerialize( offset, getRecordId(), this );
+
+ LittleEndian.putShort(data, offset, getOptions());
+ LittleEndian.putShort(data, offset+2, getRecordId());
+ int remainingBytes = thedata.length;
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ remainingBytes += r.getRecordSize();
+ }
+ LittleEndian.putInt(data, offset+4, remainingBytes);
+ System.arraycopy(thedata, 0, data, offset+8, thedata.length);
+ int pos = offset+8+thedata.length;
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ pos += r.serialize(pos, data, listener );
+ }
+
+ listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this );
+ return pos - offset;
+ }
+
+ public byte[] getData()
+ {
+ return thedata;
+ }
+
+ /**
+ * Returns the number of bytes that are required to serialize this record.
+ *
+ * @return Number of bytes
+ */
+ public int getRecordSize()
+ {
+ return 8 + thedata.length;
+ }
+
+ public List getChildRecords()
+ {
+ return childRecords;
+ }
+
+ public void setChildRecords( List childRecords )
+ {
+ this.childRecords = childRecords;
+ }
+
+ public Object clone()
+ {
+ // shallow clone
+ return super.clone();
+ }
+
+ /**
+ * The short name for this record
+ */
+ public String getRecordName()
+ {
+ return "Unknown 0x" + HexDump.toHex(getRecordId());
+ }
+
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separator" );
+
+ StringBuffer children = new StringBuffer();
+ if ( getChildRecords().size() > 0 )
+ {
+ children.append( " children: " + nl );
+ for ( Iterator iterator = getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord record = (EscherRecord) iterator.next();
+ children.append( record.toString() );
+ children.append( nl );
+ }
+ }
+
+ String theDumpHex = "";
+ try
+ {
+ if (thedata.length != 0)
+ {
+ theDumpHex = " Extra Data:" + nl;
+ theDumpHex += HexDump.dump(thedata, 0, 0);
+ }
+ }
+ catch ( Exception e )
+ {
+ theDumpHex = "Error!!";
+ }
+
+ return getClass().getName() + ":" + nl +
+ " isContainer: " + isContainerRecord() + nl +
+ " options: 0x" + HexDump.toHex( getOptions() ) + nl +
+ " recordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
+ " numchildren: " + getChildRecords().size() + nl +
+ theDumpHex +
+ children.toString();
+ }
+
+ public void addChildRecord( EscherRecord childRecord )
+ {
+ getChildRecords().add( childRecord );
+ }
+
+}
+
+
+
diff --git a/src/java/org/apache/poi/ddf/package.html b/src/java/org/apache/poi/ddf/package.html
new file mode 100644
index 0000000000..3205d487d0
--- /dev/null
+++ b/src/java/org/apache/poi/ddf/package.html
@@ -0,0 +1,11 @@
+<html>
+
+<body>
+
+ <p>This package contains classes for decoding the Microsoft Office
+ Drawing format otherwise known as escher henceforth known in POI
+ as the Dreadful Drawing Format.
+ </p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/src/java/org/apache/poi/dev/RecordGenerator.java b/src/java/org/apache/poi/dev/RecordGenerator.java
index 3b1338d195..b67c0168b8 100644
--- a/src/java/org/apache/poi/dev/RecordGenerator.java
+++ b/src/java/org/apache/poi/dev/RecordGenerator.java
@@ -88,15 +88,6 @@ public class RecordGenerator {
}
- /**
- * Description of the Method
- *
- *@param defintionsDir Description of the Parameter
- *@param recordStyleDir Description of the Parameter
- *@param destSrcPathDir Description of the Parameter
- *@param testSrcPathDir Description of the Parameter
- *@exception Exception Description of the Exception
- */
private static void generateRecords(String defintionsDir, String recordStyleDir, String destSrcPathDir, String testSrcPathDir)
throws Exception {
File definitionsFile = new File(defintionsDir);
diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java
index da65d5920c..06e2a24658 100644
--- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java
+++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java
@@ -2,7 +2,7 @@
* ====================================================================
* The Apache Software License, Version 1.1
*
- * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -107,7 +107,7 @@ public class BiffViewer {
new POIFSFileSystem(new FileInputStream(filename));
InputStream stream =
fs.createDocumentInputStream("Workbook");
- Record[] records = createRecords(stream, dump);
+ createRecords(stream, dump);
} catch (Exception e) {
e.printStackTrace();
}
@@ -128,17 +128,20 @@ public class BiffViewer {
public static Record[] createRecords(InputStream in, boolean dump)
throws RecordFormatException {
ArrayList records = new ArrayList();
- Record last_record = null;
+ //Record last_record = null;
int loc = 0;
+ RecordDetails activeRecord = null;
+
try {
// long offset = 0;
short rectype = 0;
do {
rectype = LittleEndian.readShort(in);
- System.out.println("============================================");
- System.out.println("Offset 0x" + Integer.toHexString(loc) + " (" + loc + ")");
+ int startloc = loc;
+// System.out.println("============================================");
+// System.out.println("Offset 0x" + Integer.toHexString(loc) + " (" + loc + ")");
loc += 2;
if (rectype != 0) {
short recsize = LittleEndian.readShort(in);
@@ -147,36 +150,27 @@ public class BiffViewer {
byte[] data = new byte[(int) recsize];
in.read(data);
- if ((rectype == WSBoolRecord.sid) && (recsize == 0)) {
- System.out.println(loc);
- }
loc += recsize;
-// offset += 4 + recsize;
- if (dump) {
- dump(rectype, recsize, data);
+ Record record = createRecord(rectype, recsize, data );
+ if (record.getSid() != ContinueRecord.sid)
+ {
+ records.add(record);
+ if (activeRecord != null)
+ activeRecord.dump();
+ activeRecord = new RecordDetails(rectype, recsize, startloc, data, record);
}
- Record[] recs = createRecord(rectype, recsize,
- data);
- // handle MulRK records
-
- Record record = recs[0];
-
- if ((record instanceof UnknownRecord)
- && !dump) {
- // if we didn't already dump
- // just cause dump was on and we're hit an unknow
- dumpUnknownRecord(data);
+ else
+ {
+ activeRecord.getRecord().processContinueRecord(data);
}
- if (record != null) {
- if (rectype == ContinueRecord.sid) {
- dumpContinueRecord(last_record, dump, data);
- } else {
- last_record = record;
- records.add(record);
- }
+ if (dump) {
+ dumpRaw(rectype, recsize, data);
}
}
} while (rectype != 0);
+
+ activeRecord.dump();
+
} catch (IOException e) {
throw new RecordFormatException("Error reading bytes");
}
@@ -186,15 +180,14 @@ public class BiffViewer {
return retval;
}
+ private static void dumpNormal(Record record, int startloc, short rectype, short recsize)
+ {
+ System.out.println("Offset 0x" + Integer.toHexString(startloc) + " (" + startloc + ")");
+ System.out.println( "recordid = 0x" + Integer.toHexString( rectype ) + ", size = " + recsize );
+ System.out.println( record.toString() );
+ }
+
- /**
- * Description of the Method
- *
- *@param last_record Description of the Parameter
- *@param dump Description of the Parameter
- *@param data Description of the Parameter
- *@exception IOException Description of the Exception
- */
private static void dumpContinueRecord(Record last_record, boolean dump, byte[] data) throws IOException {
if (last_record == null) {
throw new RecordFormatException(
@@ -226,12 +219,6 @@ public class BiffViewer {
}
- /**
- * Description of the Method
- *
- *@param data Description of the Parameter
- *@exception IOException Description of the Exception
- */
private static void dumpUnknownRecord(byte[] data) throws IOException {
// record hex dump it!
System.out.println(
@@ -247,10 +234,11 @@ public class BiffViewer {
}
- private static void dump( short rectype, short recsize, byte[] data ) throws IOException
+ private static void dumpRaw( short rectype, short recsize, byte[] data ) throws IOException
{
// System.out
// .println("fixing to recordize the following");
+ System.out.println("============================================");
System.out.print( "rectype = 0x"
+ Integer.toHexString( rectype ) );
System.out.println( ", recsize = 0x"
@@ -275,13 +263,9 @@ public class BiffViewer {
* Essentially a duplicate of RecordFactory. Kept seperate as not to screw
* up non-debug operations.
*
- *@param rectype Description of the Parameter
- *@param size Description of the Parameter
- *@param data Description of the Parameter
- *@return Description of the Return Value
*/
- private static Record[] createRecord( short rectype, short size,
+ private static Record createRecord( short rectype, short size,
byte[] data )
{
Record retval = null;
@@ -429,6 +413,15 @@ public class BiffViewer {
case GridsetRecord.sid:
retval = new GridsetRecord( rectype, size, data );
break;
+ case DrawingGroupRecord.sid:
+ retval = new DrawingGroupRecord( rectype, size, data );
+ break;
+ case DrawingRecordForBiffViewer.sid:
+ retval = new DrawingRecordForBiffViewer( rectype, size, data );
+ break;
+ case DrawingSelectionRecord.sid:
+ retval = new DrawingSelectionRecord( rectype, size, data );
+ break;
case GutsRecord.sid:
retval = new GutsRecord( rectype, size, data );
break;
@@ -631,19 +624,24 @@ public class BiffViewer {
retval = new PaneRecord( rectype, size, data );
break;
case SharedFormulaRecord.sid:
- retval = new SharedFormulaRecord( rectype, size, data);
- break;
+ retval = new SharedFormulaRecord( rectype, size, data);
+ break;
+ case ObjRecord.sid:
+ retval = new ObjRecord( rectype, size, data);
+ break;
+ case TextObjectRecord.sid:
+ retval = new TextObjectRecord( rectype, size, data);
+ break;
+ case HorizontalPageBreakRecord.sid:
+ retval = new HorizontalPageBreakRecord( rectype, size, data);
+ break;
+ case VerticalPageBreakRecord.sid:
+ retval = new VerticalPageBreakRecord( rectype, size, data);
+ break;
default:
retval = new UnknownRecord( rectype, size, data );
}
- if ( realretval == null )
- {
- realretval = new Record[1];
- realretval[0] = retval;
- System.out.println( "recordid = 0x" + Integer.toHexString( rectype ) + ", size =" + size );
- System.out.println( realretval[0].toString() );
- }
- return realretval;
+ return retval;
}
@@ -674,6 +672,7 @@ public class BiffViewer {
public static void main(String[] args) {
try {
+ System.setProperty("poi.deserialize.escher", "true");
BiffViewer viewer = new BiffViewer(args);
if ((args.length > 1) && args[1].equals("on")) {
@@ -696,4 +695,50 @@ public class BiffViewer {
e.printStackTrace();
}
}
+
+ static class RecordDetails
+ {
+ short rectype, recsize;
+ int startloc;
+ byte[] data;
+ Record record;
+
+ public RecordDetails( short rectype, short recsize, int startloc, byte[] data, Record record )
+ {
+ this.rectype = rectype;
+ this.recsize = recsize;
+ this.startloc = startloc;
+ this.data = data;
+ this.record = record;
+ }
+
+ public short getRectype()
+ {
+ return rectype;
+ }
+
+ public short getRecsize()
+ {
+ return recsize;
+ }
+
+ public byte[] getData()
+ {
+ return data;
+ }
+
+ public Record getRecord()
+ {
+ return record;
+ }
+
+ public void dump() throws IOException
+ {
+ if (record instanceof UnknownRecord)
+ dumpUnknownRecord(data);
+ else
+ dumpNormal(record, startloc, rectype, recsize);
+ }
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/model/AbstractShape.java b/src/java/org/apache/poi/hssf/model/AbstractShape.java
new file mode 100644
index 0000000000..522220184f
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/AbstractShape.java
@@ -0,0 +1,126 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.usermodel.*;
+
+/**
+ * An abstract shape is the lowlevel model for a shape.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public abstract class AbstractShape
+{
+ /**
+ * Create a new shape object used to create the escher records.
+ *
+ * @param hssfShape The simple shape this is based on.
+ */
+ public static AbstractShape createShape( HSSFShape hssfShape, int shapeId )
+ {
+ AbstractShape shape = null;
+ if (hssfShape instanceof HSSFTextbox)
+ {
+ shape = new TextboxShape( (HSSFTextbox)hssfShape, shapeId );
+ }
+ else if (hssfShape instanceof HSSFPolygon)
+ {
+ shape = new PolygonShape( (HSSFPolygon) hssfShape, shapeId );
+ }
+ else if (hssfShape instanceof HSSFSimpleShape)
+ {
+ HSSFSimpleShape simpleShape = (HSSFSimpleShape) hssfShape;
+ switch ( simpleShape.getShapeType() )
+ {
+ case HSSFSimpleShape.OBJECT_TYPE_LINE:
+ shape = new LineShape( simpleShape, shapeId );
+ break;
+ case HSSFSimpleShape.OBJECT_TYPE_OVAL:
+ case HSSFSimpleShape.OBJECT_TYPE_RECTANGLE:
+ shape = new SimpleFilledShape( simpleShape, shapeId );
+ break;
+ default:
+ throw new IllegalArgumentException("Do not know how to handle this type of shape");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unknown shape type");
+ }
+ EscherSpRecord sp = shape.getSpContainer().getChildById(EscherSpRecord.RECORD_ID);
+ if (hssfShape.getParent() != null)
+ sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_CHILD);
+ return shape;
+ }
+
+ protected AbstractShape()
+ {
+ }
+
+ /**
+ * @return The shape container and it's children that can represent this
+ * shape.
+ */
+ public abstract EscherContainerRecord getSpContainer();
+
+ /**
+ * @return The object record that is associated with this shape.
+ */
+ public abstract ObjRecord getObjRecord();
+
+ /**
+ * Creates an escher anchor record from a HSSFAnchor.
+ *
+ * @param userAnchor The high level anchor to convert.
+ * @return An escher anchor record.
+ */
+ protected EscherRecord createAnchor( HSSFAnchor userAnchor )
+ {
+ return ConvertAnchor.createAnchor(userAnchor);
+ }
+
+ /**
+ * Add standard properties to the opt record. These properties effect
+ * all records.
+ *
+ * @param shape The user model shape.
+ * @param opt The opt record to add the properties to.
+ * @return The number of options added.
+ */
+ protected int addStandardOptions( HSSFShape shape, EscherOptRecord opt )
+ {
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x080000 ) );
+// opt.addEscherProperty( new EscherBoolProperty( EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x080008 ) );
+ if ( shape.isNoFill() )
+ {
+ // Wonderful... none of the spec's give any clue as to what these constants mean.
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.FILL__NOFILLHITTEST, 0x00110000 ) );
+ }
+ else
+ {
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.FILL__NOFILLHITTEST, 0x00010000 ) );
+ }
+ opt.addEscherProperty( new EscherRGBProperty( EscherProperties.FILL__FILLCOLOR, shape.getFillColor() ) );
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.GROUPSHAPE__PRINT, 0x080000 ) );
+ opt.addEscherProperty( new EscherRGBProperty( EscherProperties.LINESTYLE__COLOR, shape.getLineStyleColor() ) );
+ int options = 5;
+ if (shape.getLineWidth() != HSSFShape.LINEWIDTH_DEFAULT)
+ {
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.LINESTYLE__LINEWIDTH, shape.getLineWidth()));
+ options++;
+ }
+ if (shape.getLineStyle() != HSSFShape.LINESTYLE_SOLID)
+ {
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.LINESTYLE__LINEDASHING, shape.getLineStyle()));
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.LINESTYLE__LINEENDCAPSTYLE, 0));
+ if (shape.getLineStyle() == HSSFShape.LINESTYLE_NONE)
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080000));
+ else
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x00080008));
+ options += 3;
+ }
+ opt.sortProperties();
+ return options; // # options added
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/ConvertAnchor.java b/src/java/org/apache/poi/hssf/model/ConvertAnchor.java
new file mode 100644
index 0000000000..bb07a121a8
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/ConvertAnchor.java
@@ -0,0 +1,50 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherClientAnchorRecord;
+import org.apache.poi.ddf.EscherChildAnchorRecord;
+import org.apache.poi.hssf.usermodel.HSSFAnchor;
+import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
+import org.apache.poi.hssf.usermodel.HSSFChildAnchor;
+
+/**
+ * $Id$
+ */
+public class ConvertAnchor
+{
+ public static EscherRecord createAnchor( HSSFAnchor userAnchor )
+ {
+ if (userAnchor instanceof HSSFClientAnchor)
+ {
+ HSSFClientAnchor a = (HSSFClientAnchor) userAnchor;
+
+ EscherClientAnchorRecord anchor = new EscherClientAnchorRecord();
+ anchor.setRecordId( EscherClientAnchorRecord.RECORD_ID );
+ anchor.setOptions( (short) 0x0000 );
+ anchor.setFlag( (short) 0 );
+ anchor.setCol1( (short) Math.min(a.getCol1(), a.getCol2()) );
+ anchor.setDx1( (short) Math.min(a.getDx1(), a.getDx2()) );
+ anchor.setRow1( (short) Math.min(a.getRow1(), a.getRow2()) );
+ anchor.setDy1( (short) Math.min(a.getDy1(), a.getDy2()) );
+
+ anchor.setCol2( (short) Math.max(a.getCol1(), a.getCol2()) );
+ anchor.setDx2( (short) Math.max(a.getDx1(), a.getDx2()) );
+ anchor.setRow2( (short) Math.max(a.getRow1(), a.getRow2()) );
+ anchor.setDy2( (short) Math.max(a.getDy1(), a.getDy2() ) );
+ return anchor;
+ }
+ else
+ {
+ HSSFChildAnchor a = (HSSFChildAnchor) userAnchor;
+ EscherChildAnchorRecord anchor = new EscherChildAnchorRecord();
+ anchor.setRecordId( EscherChildAnchorRecord.RECORD_ID );
+ anchor.setOptions( (short) 0x0000 );
+ anchor.setDx1( (short) Math.min(a.getDx1(), a.getDx2()) );
+ anchor.setDy1( (short) Math.min(a.getDy1(), a.getDy2()) );
+ anchor.setDx2( (short) Math.max(a.getDx2(), a.getDx1()) );
+ anchor.setDy2( (short) Math.max(a.getDy2(), a.getDy1()) );
+ return anchor;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/DrawingManager.java b/src/java/org/apache/poi/hssf/model/DrawingManager.java
new file mode 100644
index 0000000000..585f6b90b3
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/DrawingManager.java
@@ -0,0 +1,136 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherDgRecord;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Provides utilities to manage drawing groups.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class DrawingManager
+{
+ EscherDggRecord dgg;
+ Map dgMap = new HashMap(); // key = Short(drawingId), value=EscherDgRecord
+
+ public DrawingManager( EscherDggRecord dgg )
+ {
+ this.dgg = dgg;
+ }
+
+ public EscherDgRecord createDgRecord()
+ {
+ EscherDgRecord dg = new EscherDgRecord();
+ dg.setRecordId( EscherDgRecord.RECORD_ID );
+ short dgId = findNewDrawingGroupId();
+ dg.setOptions( (short) ( dgId << 4 ) );
+ dg.setNumShapes( 0 );
+ dg.setLastMSOSPID( -1 );
+ dgg.addCluster( dgId, 0 );
+ dgg.setDrawingsSaved( dgg.getDrawingsSaved() + 1 );
+ dgMap.put( new Short( dgId ), dg );
+ return dg;
+ }
+
+ /**
+ * Allocates new shape id for the new drawing group id.
+ *
+ * @return a new shape id.
+ */
+ public int allocateShapeId(short drawingGroupId)
+ {
+ // Get the last shape id for this drawing group.
+ EscherDgRecord dg = (EscherDgRecord) dgMap.get(new Short(drawingGroupId));
+ int lastShapeId = dg.getLastMSOSPID();
+
+
+ // Have we run out of shapes for this cluster?
+ int newShapeId = 0;
+ if (lastShapeId % 1024 == 1023)
+ {
+ // Yes:
+ // Find the starting shape id of the next free cluster
+ newShapeId = findFreeSPIDBlock();
+ // Create a new cluster in the dgg record.
+ dgg.addCluster(drawingGroupId, 1);
+ }
+ else
+ {
+ // No:
+ // Find the cluster for this drawing group with free space.
+ for (int i = 0; i < dgg.getFileIdClusters().length; i++)
+ {
+ EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i];
+ if (c.getDrawingGroupId() == drawingGroupId)
+ {
+ if (c.getNumShapeIdsUsed() != 1024)
+ {
+ // Increment the number of shapes used for this cluster.
+ c.incrementShapeId();
+ }
+ }
+ // If the last shape id = -1 then we know to find a free block;
+ if (dg.getLastMSOSPID() == -1)
+ {
+ newShapeId = findFreeSPIDBlock();
+ }
+ else
+ {
+ // The new shape id to be the last shapeid of this cluster + 1
+ newShapeId = dg.getLastMSOSPID() + 1;
+ }
+ }
+ }
+ // Increment the total number of shapes used in the dgg.
+ dgg.setNumShapesSaved(dgg.getNumShapesSaved() + 1);
+ // Is the new shape id >= max shape id for dgg?
+ if (newShapeId >= dgg.getShapeIdMax())
+ {
+ // Yes:
+ // Set the max shape id = new shape id + 1
+ dgg.setShapeIdMax(newShapeId + 1);
+ }
+ // Set last shape id for this drawing group.
+ dg.setLastMSOSPID(newShapeId);
+ // Increased the number of shapes used for this drawing group.
+ dg.incrementShapeCount();
+
+
+ return newShapeId;
+ }
+
+ //////////// Non-public methods /////////////
+ short findNewDrawingGroupId()
+ {
+ short dgId = 1;
+ while ( drawingGroupExists( dgId ) )
+ dgId++;
+ return dgId;
+ }
+
+ boolean drawingGroupExists( short dgId )
+ {
+ for ( int i = 0; i < dgg.getFileIdClusters().length; i++ )
+ {
+ if ( dgg.getFileIdClusters()[i].getDrawingGroupId() == dgId )
+ return true;
+ }
+ return false;
+ }
+
+ int findFreeSPIDBlock()
+ {
+ int max = dgg.getShapeIdMax();
+ int next = ( ( max / 1024 ) + 1 ) * 1024;
+ return next;
+ }
+
+ public EscherDggRecord getDgg()
+ {
+ return dgg;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/LineShape.java b/src/java/org/apache/poi/hssf/model/LineShape.java
new file mode 100644
index 0000000000..0b4e4d1e51
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/LineShape.java
@@ -0,0 +1,105 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.usermodel.*;
+
+/**
+ * Represents a line shape and creates all the line specific low level records.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class LineShape
+ extends AbstractShape
+{
+ private EscherContainerRecord spContainer;
+ private ObjRecord objRecord;
+
+ /**
+ * Creates the line shape from the highlevel user shape. All low level
+ * records are created at this point.
+ *
+ * @param hssfShape The user model shape.
+ * @param shapeId The identifier to use for this shape.
+ */
+ LineShape( HSSFSimpleShape hssfShape, int shapeId )
+ {
+ spContainer = createSpContainer(hssfShape, shapeId);
+ objRecord = createObjRecord(hssfShape, shapeId);
+ }
+
+ /**
+ * Creates the lowerlevel escher records for this shape.
+ */
+ private EscherContainerRecord createSpContainer(HSSFSimpleShape hssfShape, int shapeId)
+ {
+ HSSFShape shape = hssfShape;
+
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherRecord anchor = new EscherClientAnchorRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ sp.setOptions( (short) ( (EscherAggregate.ST_LINE << 4) | 0x2 ) );
+
+ sp.setShapeId( shapeId );
+ sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ opt.addEscherProperty( new EscherShapePathProperty( EscherProperties.GEOMETRY__SHAPEPATH, EscherShapePathProperty.COMPLEX ) );
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.LINESTYLE__NOLINEDRAWDASH, 1048592 ) );
+ addStandardOptions(shape, opt);
+ HSSFAnchor userAnchor = shape.getAnchor();
+ if (userAnchor.isHorizontallyFlipped())
+ sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPHORIZ);
+ if (userAnchor.isVerticallyFlipped())
+ sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPVERT);
+ anchor = createAnchor(userAnchor);
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+
+ spContainer.addChildRecord(sp);
+ spContainer.addChildRecord(opt);
+ spContainer.addChildRecord(anchor);
+ spContainer.addChildRecord(clientData);
+
+ return spContainer;
+ }
+
+ /**
+ * Creates the low level OBJ record for this shape.
+ */
+ private ObjRecord createObjRecord(HSSFShape hssfShape, int shapeId)
+ {
+ HSSFShape shape = hssfShape;
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType((short) ((HSSFSimpleShape)shape).getShapeType());
+ c.setObjectId((short) ( shapeId ));
+ c.setLocked(true);
+ c.setPrintable(true);
+ c.setAutofill(true);
+ c.setAutoline(true);
+ EndSubRecord e = new EndSubRecord();
+
+ obj.addSubRecord(c);
+ obj.addSubRecord(e);
+
+ return obj;
+ }
+
+ public EscherContainerRecord getSpContainer()
+ {
+ return spContainer;
+ }
+
+ public ObjRecord getObjRecord()
+ {
+ return objRecord;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/PolygonShape.java b/src/java/org/apache/poi/hssf/model/PolygonShape.java
new file mode 100644
index 0000000000..fccc92e6ac
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/PolygonShape.java
@@ -0,0 +1,143 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.record.EscherAggregate;
+import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
+import org.apache.poi.hssf.record.EndSubRecord;
+import org.apache.poi.hssf.usermodel.HSSFSimpleShape;
+import org.apache.poi.hssf.usermodel.HSSFShape;
+import org.apache.poi.hssf.usermodel.HSSFPolygon;
+import org.apache.poi.util.LittleEndian;
+
+public class PolygonShape
+ extends AbstractShape
+{
+ public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30;
+
+ private EscherContainerRecord spContainer;
+ private ObjRecord objRecord;
+
+ /**
+ * Creates the low evel records for an polygon.
+ *
+ * @param hssfShape The highlevel shape.
+ * @param shapeId The shape id to use for this shape.
+ */
+ PolygonShape( HSSFPolygon hssfShape, int shapeId )
+ {
+ spContainer = createSpContainer( hssfShape, shapeId );
+ objRecord = createObjRecord( hssfShape, shapeId );
+ }
+
+ /**
+ * Generates the shape records for this shape.
+ *
+ */
+ private EscherContainerRecord createSpContainer( HSSFPolygon hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ sp.setOptions( (short) ( ( EscherAggregate.ST_DONUT << 4 ) | 0x2 ) );
+ sp.setShapeId( shapeId );
+ if (hssfShape.getParent() == null)
+ sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ else
+ sp.setFlags( EscherSpRecord.FLAG_CHILD | EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.TRANSFORM__ROTATION, false, false, 0));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__RIGHT, false, false, hssfShape.getDrawAreaWidth()));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__BOTTOM, false, false, hssfShape.getDrawAreaHeight()));
+ opt.addEscherProperty(new EscherShapePathProperty(EscherProperties.GEOMETRY__SHAPEPATH, EscherShapePathProperty.COMPLEX));
+ EscherArrayProperty verticesProp = new EscherArrayProperty(EscherProperties.GEOMETRY__VERTICES, false, new byte[0] );
+ verticesProp.setNumberOfElementsInArray(hssfShape.getXPoints().length+1);
+ verticesProp.setNumberOfElementsInMemory(hssfShape.getXPoints().length+1);
+ verticesProp.setSizeOfElements(0xFFF0);
+ for (int i = 0; i < hssfShape.getXPoints().length; i++)
+ {
+ byte[] data = new byte[4];
+ LittleEndian.putShort(data, 0, (short)hssfShape.getXPoints()[i]);
+ LittleEndian.putShort(data, 2, (short)hssfShape.getYPoints()[i]);
+ verticesProp.setElement(i, data);
+ }
+ int point = hssfShape.getXPoints().length;
+ byte[] data = new byte[4];
+ LittleEndian.putShort(data, 0, (short)hssfShape.getXPoints()[0]);
+ LittleEndian.putShort(data, 2, (short)hssfShape.getYPoints()[0]);
+ verticesProp.setElement(point, data);
+ opt.addEscherProperty(verticesProp);
+ EscherArrayProperty segmentsProp = new EscherArrayProperty(EscherProperties.GEOMETRY__SEGMENTINFO, false, null );
+ segmentsProp.setSizeOfElements(0x0002);
+ segmentsProp.setNumberOfElementsInArray(hssfShape.getXPoints().length * 2 + 4);
+ segmentsProp.setNumberOfElementsInMemory(hssfShape.getXPoints().length * 2 + 4);
+ segmentsProp.setElement(0, new byte[] { (byte)0x00, (byte)0x40 } );
+ segmentsProp.setElement(1, new byte[] { (byte)0x00, (byte)0xAC } );
+ for (int i = 0; i < hssfShape.getXPoints().length; i++)
+ {
+ segmentsProp.setElement(2 + i * 2, new byte[] { (byte)0x01, (byte)0x00 } );
+ segmentsProp.setElement(3 + i * 2, new byte[] { (byte)0x00, (byte)0xAC } );
+ }
+ segmentsProp.setElement(segmentsProp.getNumberOfElementsInArray() - 2, new byte[] { (byte)0x01, (byte)0x60 } );
+ segmentsProp.setElement(segmentsProp.getNumberOfElementsInArray() - 1, new byte[] { (byte)0x00, (byte)0x80 } );
+ opt.addEscherProperty(segmentsProp);
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__FILLOK, false, false, 0x00010001));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINESTARTARROWHEAD, false, false, 0x0));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEENDARROWHEAD, false, false, 0x0));
+ opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEENDCAPSTYLE, false, false, 0x0));
+
+ addStandardOptions(shape, opt);
+
+ EscherRecord anchor = createAnchor( shape.getAnchor() );
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+
+ spContainer.addChildRecord( sp );
+ spContainer.addChildRecord( opt );
+ spContainer.addChildRecord( anchor );
+ spContainer.addChildRecord( clientData );
+
+ return spContainer;
+ }
+
+ /**
+ * Creates the low level OBJ record for this shape.
+ */
+ private ObjRecord createObjRecord( HSSFShape hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType( OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING );
+ c.setObjectId( (short) ( shapeId ) );
+ c.setLocked( true );
+ c.setPrintable( true );
+ c.setAutofill( true );
+ c.setAutoline( true );
+ EndSubRecord e = new EndSubRecord();
+
+ obj.addSubRecord( c );
+ obj.addSubRecord( e );
+
+ return obj;
+ }
+
+ public EscherContainerRecord getSpContainer()
+ {
+ return spContainer;
+ }
+
+ public ObjRecord getObjRecord()
+ {
+ return objRecord;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java
index 456606bc2d..0adcd531aa 100644
--- a/src/java/org/apache/poi/hssf/model/Sheet.java
+++ b/src/java/org/apache/poi/hssf/model/Sheet.java
@@ -122,6 +122,8 @@ public class Sheet implements Model
private Iterator rowRecIterator = null;
protected int eofLoc = 0;
protected ProtectRecord protect = null;
+ protected PageBreakRecord rowBreaks = null;
+ protected PageBreakRecord colBreaks = null;
public static final byte PANE_LOWER_RIGHT = (byte)0;
public static final byte PANE_UPPER_RIGHT = (byte)1;
@@ -155,7 +157,7 @@ public class Sheet implements Model
*/
public static Sheet createSheet(List recs, int sheetnum, int offset)
{
- log.logFormatted(log.DEBUG,
+ log.logFormatted(POILogger.DEBUG,
"Sheet createSheet (existing file) with %",
new Integer(recs.size()));
Sheet retval = new Sheet();
@@ -170,18 +172,18 @@ public class Sheet implements Model
if (rec.getSid() == LabelRecord.sid)
{
- log.log(log.DEBUG, "Hit label record.");
+ log.log(POILogger.DEBUG, "Hit label record.");
retval.containsLabels = true;
}
else if (rec.getSid() == BOFRecord.sid)
{
bofEofNestingLevel++;
- log.log(log.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel);
+ log.log(POILogger.DEBUG, "Hit BOF record. Nesting increased to " + bofEofNestingLevel);
}
else if (rec.getSid() == EOFRecord.sid)
{
--bofEofNestingLevel;
- log.log(log.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel);
+ log.log(POILogger.DEBUG, "Hit EOF record. Nesting decreased to " + bofEofNestingLevel);
if (bofEofNestingLevel == 0) {
records.add(rec);
retval.eofLoc = k;
@@ -296,6 +298,14 @@ public class Sheet implements Model
{
rec = null;
}
+ else if (rec.getSid() == PageBreakRecord.HORIZONTAL_SID)
+ {
+ retval.rowBreaks = (PageBreakRecord)rec;
+ }
+ else if (rec.getSid() == PageBreakRecord.VERTICAL_SID)
+ {
+ retval.colBreaks = (PageBreakRecord)rec;
+ }
if (rec != null)
@@ -312,7 +322,7 @@ public class Sheet implements Model
{
retval.cells = new ValueRecordsAggregate();
}
- log.log(log.DEBUG, "sheet createSheet (existing file) exited");
+ log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
return retval;
}
@@ -371,7 +381,7 @@ public class Sheet implements Model
public static Sheet createSheet(List records, int sheetnum)
{
- log.log(log.DEBUG,
+ log.log(POILogger.DEBUG,
"Sheet createSheet (exisiting file) assumed offset 0");
return createSheet(records, sheetnum, 0);
}
@@ -386,7 +396,7 @@ public class Sheet implements Model
public static Sheet createSheet()
{
- log.log(log.DEBUG, "Sheet createsheet from scratch called");
+ log.log(POILogger.DEBUG, "Sheet createsheet from scratch called");
Sheet retval = new Sheet();
ArrayList records = new ArrayList(30);
@@ -409,6 +419,10 @@ public class Sheet implements Model
(DefaultRowHeightRecord) retval.createDefaultRowHeight();
records.add( retval.defaultrowheight );
records.add( retval.createWSBool() );
+ retval.rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID);
+ records.add(retval.rowBreaks);
+ retval.colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID);
+ records.add(retval.colBreaks);
retval.header = (HeaderRecord) retval.createHeader();
records.add( retval.header );
retval.footer = (FooterRecord) retval.createFooter();
@@ -421,7 +435,7 @@ public class Sheet implements Model
(DefaultColWidthRecord) retval.createDefaultColWidth();
records.add( retval.defaultcolwidth);
retval.dims = ( DimensionsRecord ) retval.createDimensions();
- retval.dimsloc = 19;
+ retval.dimsloc = records.size()-1;
records.add(retval.dims);
records.add(retval.windowTwo = retval.createWindowTwo());
retval.setLoc(records.size() - 1);
@@ -432,7 +446,7 @@ public class Sheet implements Model
records.add(retval.protect);
records.add(retval.createEOF());
retval.records = records;
- log.log(log.DEBUG, "Sheet createsheet from scratch exit");
+ log.log(POILogger.DEBUG, "Sheet createsheet from scratch exit");
return retval;
}
@@ -576,7 +590,7 @@ public class Sheet implements Model
public void convertLabelRecords(Workbook wb)
{
- log.log(log.DEBUG, "convertLabelRecords called");
+ log.log(POILogger.DEBUG, "convertLabelRecords called");
if (containsLabels)
{
for (int k = 0; k < records.size(); k++)
@@ -600,7 +614,7 @@ public class Sheet implements Model
}
}
}
- log.log(log.DEBUG, "convertLabelRecords exit");
+ log.log(POILogger.DEBUG, "convertLabelRecords exit");
}
/**
@@ -614,8 +628,8 @@ public class Sheet implements Model
{
checkCells();
checkRows();
- log.log(log.DEBUG, "Sheet.getNumRecords");
- log.logFormatted(log.DEBUG, "returning % + % + % - 2 = %", new int[]
+ log.log(POILogger.DEBUG, "Sheet.getNumRecords");
+ log.logFormatted(POILogger.DEBUG, "returning % + % + % - 2 = %", new int[]
{
records.size(), cells.getPhysicalNumberOfCells(),
rows.getPhysicalNumberOfRows(),
@@ -638,8 +652,8 @@ public class Sheet implements Model
public void setDimensions(int firstrow, short firstcol, int lastrow,
short lastcol)
{
- log.log(log.DEBUG, "Sheet.setDimensions");
- log.log(log.DEBUG,
+ log.log(POILogger.DEBUG, "Sheet.setDimensions");
+ log.log(POILogger.DEBUG,
(new StringBuffer("firstrow")).append(firstrow)
.append("firstcol").append(firstcol).append("lastrow")
.append(lastrow).append("lastcol").append(lastcol)
@@ -2560,4 +2574,192 @@ public class Sheet implements Model
{
return protect;
}
+
+ public int aggregateDrawingRecords(DrawingManager drawingManager)
+ {
+ int loc = findFirstRecordLocBySid(DrawingRecord.sid);
+ boolean noDrawingRecordsFound = loc == -1;
+ if (noDrawingRecordsFound)
+ {
+ EscherAggregate aggregate = new EscherAggregate( drawingManager );
+ loc = findFirstRecordLocBySid(EscherAggregate.sid);
+ if (loc == -1)
+ {
+ loc = findFirstRecordLocBySid( WindowTwoRecord.sid );
+ }
+ else
+ {
+ getRecords().remove(loc);
+ }
+ getRecords().add( loc, aggregate );
+ return loc;
+ }
+ else
+ {
+ List records = getRecords();
+ EscherAggregate r = EscherAggregate.createAggregate( records, loc, drawingManager );
+ int startloc = loc;
+ while ( loc + 1 < records.size()
+ && records.get( loc ) instanceof DrawingRecord
+ && records.get( loc + 1 ) instanceof ObjRecord )
+ {
+ loc += 2;
+ }
+ int endloc = loc-1;
+ for(int i = 0; i < (endloc - startloc + 1); i++)
+ records.remove(startloc);
+ records.add(startloc, r);
+
+ return startloc;
+ }
+ }
+
+ /**
+ * Perform any work necessary before the sheet is about to be serialized.
+ * For instance the escher aggregates size needs to be calculated before
+ * serialization so that the dgg record (which occurs first) can be written.
+ */
+ public void preSerialize()
+ {
+ for ( Iterator iterator = getRecords().iterator(); iterator.hasNext(); )
+ {
+ Record r = (Record) iterator.next();
+ if (r instanceof EscherAggregate)
+ r.getRecordSize(); // Trigger flatterning of user model and corresponding update of dgg record.
+ }
+ }
+
+ /**
+ * Shifts all the page breaks in the range "count" number of rows/columns
+ * @param breaks The page record to be shifted
+ * @param start Starting "main" value to shift breaks
+ * @param stop Ending "main" value to shift breaks
+ * @param count number of units (rows/columns) to shift by
+ */
+ public void shiftBreaks(PageBreakRecord breaks, short start, short stop, int count) {
+
+ if(rowBreaks == null)
+ return;
+ Iterator iterator = breaks.getBreaksIterator();
+ List shiftedBreak = new ArrayList();
+ while(iterator.hasNext())
+ {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ short breakLocation = breakItem.main;
+ boolean inStart = (breakLocation >= start);
+ boolean inEnd = (breakLocation <= stop);
+ if(inStart && inEnd)
+ shiftedBreak.add(breakItem);
+ }
+
+ iterator = shiftedBreak.iterator();
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ breaks.removeBreak(breakItem.main);
+ breaks.addBreak((short)(breakItem.main+count), breakItem.subFrom, breakItem.subTo);
+ }
+ }
+
+ /**
+ * Sets a page break at the indicated row
+ * @param row
+ */
+ public void setRowBreak(int row, short fromCol, short toCol) {
+ rowBreaks.addBreak((short)row, fromCol, toCol);
+ }
+
+ /**
+ * Removes a page break at the indicated row
+ * @param row
+ */
+ public void removeRowBreak(int row) {
+ rowBreaks.removeBreak((short)row);
+ }
+
+ /**
+ * Queries if the specified row has a page break
+ * @param row
+ * @return true if the specified row has a page break
+ */
+ public boolean isRowBroken(int row) {
+ return rowBreaks.getBreak((short)row) != null;
+ }
+
+ /**
+ * Sets a page break at the indicated column
+ * @param row
+ */
+ public void setColumnBreak(short column, short fromRow, short toRow) {
+ colBreaks.addBreak(column, fromRow, toRow);
+ }
+
+ /**
+ * Removes a page break at the indicated column
+ * @param row
+ */
+ public void removeColumnBreak(short column) {
+ colBreaks.removeBreak(column);
+ }
+
+ /**
+ * Queries if the specified column has a page break
+ * @param row
+ * @return true if the specified column has a page break
+ */
+ public boolean isColumnBroken(short column) {
+ return colBreaks.getBreak(column) != null;
+ }
+
+ /**
+ * Shifts the horizontal page breaks for the indicated count
+ * @param startingRow
+ * @param endingRow
+ * @param count
+ */
+ public void shiftRowBreaks(int startingRow, int endingRow, int count) {
+ shiftBreaks(rowBreaks, (short)startingRow, (short)endingRow, (short)count);
+ }
+
+ /**
+ * Shifts the vertical page breaks for the indicated count
+ * @param startingCol
+ * @param endingCol
+ * @param count
+ */
+ public void shiftColumnBreaks(short startingCol, short endingCol, short count) {
+ shiftBreaks(colBreaks, startingCol, endingCol, count);
+ }
+
+ /**
+ * Returns all the row page breaks
+ * @return
+ */
+ public Iterator getRowBreaks() {
+ return rowBreaks.getBreaksIterator();
+ }
+
+ /**
+ * Returns the number of row page breaks
+ * @return
+ */
+ public int getNumRowBreaks(){
+ return (int)rowBreaks.getNumBreaks();
+ }
+
+ /**
+ * Returns all the column page breaks
+ * @return
+ */
+ public Iterator getColumnBreaks(){
+ return colBreaks.getBreaksIterator();
+ }
+
+ /**
+ * Returns the number of column page breaks
+ * @return
+ */
+ public int getNumColumnBreaks(){
+ return (int)colBreaks.getNumBreaks();
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/model/SimpleFilledShape.java b/src/java/org/apache/poi/hssf/model/SimpleFilledShape.java
new file mode 100644
index 0000000000..dadb02d79d
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/SimpleFilledShape.java
@@ -0,0 +1,111 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.record.EscherAggregate;
+import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
+import org.apache.poi.hssf.record.EndSubRecord;
+import org.apache.poi.hssf.usermodel.HSSFSimpleShape;
+import org.apache.poi.hssf.usermodel.HSSFShape;
+
+public class SimpleFilledShape
+ extends AbstractShape
+{
+ private EscherContainerRecord spContainer;
+ private ObjRecord objRecord;
+
+ /**
+ * Creates the low evel records for an oval.
+ *
+ * @param hssfShape The highlevel shape.
+ * @param shapeId The shape id to use for this shape.
+ */
+ SimpleFilledShape( HSSFSimpleShape hssfShape, int shapeId )
+ {
+ spContainer = createSpContainer( hssfShape, shapeId );
+ objRecord = createObjRecord( hssfShape, shapeId );
+ }
+
+ /**
+ * Generates the shape records for this shape.
+ *
+ * @param hssfShape
+ * @param shapeId
+ * @return
+ */
+ private EscherContainerRecord createSpContainer( HSSFSimpleShape hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ short shapeType = objTypeToShapeType( hssfShape.getShapeType() );
+ sp.setOptions( (short) ( ( shapeType << 4 ) | 0x2 ) );
+ sp.setShapeId( shapeId );
+ sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ addStandardOptions(shape, opt);
+ EscherRecord anchor = createAnchor( shape.getAnchor() );
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+
+ spContainer.addChildRecord( sp );
+ spContainer.addChildRecord( opt );
+ spContainer.addChildRecord( anchor );
+ spContainer.addChildRecord( clientData );
+
+ return spContainer;
+ }
+
+ private short objTypeToShapeType( int objType )
+ {
+ short shapeType;
+ if (objType == HSSFSimpleShape.OBJECT_TYPE_OVAL)
+ shapeType = EscherAggregate.ST_ELLIPSE;
+ else if (objType == HSSFSimpleShape.OBJECT_TYPE_RECTANGLE)
+ shapeType = EscherAggregate.ST_RECTANGLE;
+ else
+ throw new IllegalArgumentException("Unable to handle an object of this type");
+ return shapeType;
+ }
+
+ /**
+ * Creates the low level OBJ record for this shape.
+ */
+ private ObjRecord createObjRecord( HSSFShape hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType( (short) ( (HSSFSimpleShape) shape ).getShapeType() );
+ c.setObjectId( (short) ( shapeId ) );
+ c.setLocked( true );
+ c.setPrintable( true );
+ c.setAutofill( true );
+ c.setAutoline( true );
+ EndSubRecord e = new EndSubRecord();
+
+ obj.addSubRecord( c );
+ obj.addSubRecord( e );
+
+ return obj;
+ }
+
+ public EscherContainerRecord getSpContainer()
+ {
+ return spContainer;
+ }
+
+ public ObjRecord getObjRecord()
+ {
+ return objRecord;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/TestDrawingManager.java b/src/java/org/apache/poi/hssf/model/TestDrawingManager.java
new file mode 100644
index 0000000000..606ffccc34
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/TestDrawingManager.java
@@ -0,0 +1,81 @@
+package org.apache.poi.hssf.model;
+
+import junit.framework.TestCase;
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherDgRecord;
+
+public class TestDrawingManager extends TestCase
+{
+ public void testFindFreeSPIDBlock() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ DrawingManager dm = new DrawingManager( dgg );
+ dgg.setShapeIdMax( 1024 );
+ assertEquals( 2048, dm.findFreeSPIDBlock() );
+ dgg.setShapeIdMax( 1025 );
+ assertEquals( 2048, dm.findFreeSPIDBlock() );
+ dgg.setShapeIdMax( 2047 );
+ assertEquals( 2048, dm.findFreeSPIDBlock() );
+ }
+
+ public void testFindNewDrawingGroupId() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ dgg.setDrawingsSaved( 1 );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{
+ new EscherDggRecord.FileIdCluster( 2, 10 )} );
+ DrawingManager dm = new DrawingManager( dgg );
+ assertEquals( 1, dm.findNewDrawingGroupId() );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{
+ new EscherDggRecord.FileIdCluster( 1, 10 ),
+ new EscherDggRecord.FileIdCluster( 2, 10 )} );
+ assertEquals( 3, dm.findNewDrawingGroupId() );
+ }
+
+ public void testDrawingGroupExists() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ dgg.setDrawingsSaved( 1 );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{
+ new EscherDggRecord.FileIdCluster( 2, 10 )} );
+ DrawingManager dm = new DrawingManager( dgg );
+ assertFalse( dm.drawingGroupExists( (short) 1 ) );
+ assertTrue( dm.drawingGroupExists( (short) 2 ) );
+ assertFalse( dm.drawingGroupExists( (short) 3 ) );
+ }
+
+ public void testCreateDgRecord() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ dgg.setDrawingsSaved( 0 );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{} );
+ DrawingManager dm = new DrawingManager( dgg );
+
+ EscherDgRecord dgRecord = dm.createDgRecord();
+ assertEquals( -1, dgRecord.getLastMSOSPID() );
+ assertEquals( 0, dgRecord.getNumShapes() );
+ assertEquals( 1, dm.getDgg().getDrawingsSaved() );
+ assertEquals( 1, dm.getDgg().getFileIdClusters().length );
+ assertEquals( 1, dm.getDgg().getFileIdClusters()[0].getDrawingGroupId() );
+ assertEquals( 0, dm.getDgg().getFileIdClusters()[0].getNumShapeIdsUsed() );
+ }
+
+ public void testAllocateShapeId() throws Exception
+ {
+ EscherDggRecord dgg = new EscherDggRecord();
+ dgg.setDrawingsSaved( 0 );
+ dgg.setFileIdClusters( new EscherDggRecord.FileIdCluster[]{} );
+ DrawingManager dm = new DrawingManager( dgg );
+
+ EscherDgRecord dg = dm.createDgRecord();
+ int shapeId = dm.allocateShapeId( dg.getDrawingGroupId() );
+ assertEquals( 1024, shapeId );
+ assertEquals( 1025, dgg.getShapeIdMax() );
+ assertEquals( 1, dgg.getDrawingsSaved() );
+ assertEquals( 1, dgg.getFileIdClusters()[0].getDrawingGroupId() );
+ assertEquals( 1, dgg.getFileIdClusters()[0].getNumShapeIdsUsed() );
+ assertEquals( 1024, dg.getLastMSOSPID() );
+ assertEquals( 1, dg.getNumShapes() );
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/model/TextboxShape.java b/src/java/org/apache/poi/hssf/model/TextboxShape.java
new file mode 100644
index 0000000000..02143974db
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/model/TextboxShape.java
@@ -0,0 +1,151 @@
+package org.apache.poi.hssf.model;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.usermodel.*;
+
+/**
+ * Represents an textbox shape and converts between the highlevel records
+ * and lowlevel records for an oval.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TextboxShape
+ extends AbstractShape
+{
+ private EscherContainerRecord spContainer;
+ private TextObjectRecord textObjectRecord;
+ private ObjRecord objRecord;
+ private EscherTextboxRecord escherTextbox;
+
+ /**
+ * Creates the low evel records for an textbox.
+ *
+ * @param hssfShape The highlevel shape.
+ * @param shapeId The shape id to use for this shape.
+ */
+ TextboxShape( HSSFTextbox hssfShape, int shapeId )
+ {
+ spContainer = createSpContainer( hssfShape, shapeId );
+ objRecord = createObjRecord( hssfShape, shapeId );
+ textObjectRecord = createTextObjectRecord( hssfShape, shapeId );
+ }
+
+ /**
+ * Creates the low level OBJ record for this shape.
+ */
+ private ObjRecord createObjRecord( HSSFTextbox hssfShape, int shapeId )
+ {
+ HSSFShape shape = hssfShape;
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
+ c.setObjectType( (short) ( (HSSFSimpleShape) shape ).getShapeType() );
+ c.setObjectId( (short) ( shapeId ) );
+ c.setLocked( true );
+ c.setPrintable( true );
+ c.setAutofill( true );
+ c.setAutoline( true );
+ EndSubRecord e = new EndSubRecord();
+
+ obj.addSubRecord( c );
+ obj.addSubRecord( e );
+
+ return obj;
+ }
+
+ /**
+ * Generates the escher shape records for this shape.
+ *
+ * @param hssfShape
+ * @param shapeId
+ * @return
+ */
+ private EscherContainerRecord createSpContainer( HSSFTextbox hssfShape, int shapeId )
+ {
+ HSSFTextbox shape = hssfShape;
+
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherRecord anchor = new EscherClientAnchorRecord();
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+ escherTextbox = new EscherTextboxRecord();
+
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ sp.setOptions( (short) ( ( EscherAggregate.ST_TEXTBOX << 4 ) | 0x2 ) );
+
+ sp.setShapeId( shapeId );
+ sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ // opt.addEscherProperty( new EscherBoolProperty( EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 262144 ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTID, 0 ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTLEFT, shape.getMarginLeft() ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTRIGHT, shape.getMarginRight() ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTBOTTOM, shape.getMarginBottom() ) );
+ opt.addEscherProperty( new EscherSimpleProperty( EscherProperties.TEXT__TEXTTOP, shape.getMarginTop() ) );
+ addStandardOptions( shape, opt );
+ HSSFAnchor userAnchor = shape.getAnchor();
+ // if (userAnchor.isHorizontallyFlipped())
+ // sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPHORIZ);
+ // if (userAnchor.isVerticallyFlipped())
+ // sp.setFlags(sp.getFlags() | EscherSpRecord.FLAG_FLIPVERT);
+ anchor = createAnchor( userAnchor );
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+ escherTextbox.setRecordId( EscherTextboxRecord.RECORD_ID );
+ escherTextbox.setOptions( (short) 0x0000 );
+
+ spContainer.addChildRecord( sp );
+ spContainer.addChildRecord( opt );
+ spContainer.addChildRecord( anchor );
+ spContainer.addChildRecord( clientData );
+ spContainer.addChildRecord( escherTextbox );
+
+ return spContainer;
+ }
+
+ /**
+ * Textboxes also have an extra TXO record associated with them that most
+ * other shapes dont have.
+ */
+ private TextObjectRecord createTextObjectRecord( HSSFTextbox hssfShape, int shapeId )
+ {
+ HSSFTextbox shape = hssfShape;
+
+ TextObjectRecord obj = new TextObjectRecord();
+ obj.setHorizontalTextAlignment( TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED );
+ obj.setVerticalTextAlignment( TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP );
+ obj.setTextLocked( true );
+ obj.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );
+ int frLength = ( shape.getString().numFormattingRuns() + 1 ) * 8;
+ obj.setFormattingRunLength( (short) frLength );
+ obj.setTextLength( (short) shape.getString().length() );
+ obj.setStr( shape.getString() );
+ obj.setReserved7( 0 );
+
+ return obj;
+ }
+
+ public EscherContainerRecord getSpContainer()
+ {
+ return spContainer;
+ }
+
+ public ObjRecord getObjRecord()
+ {
+ return objRecord;
+ }
+
+ public TextObjectRecord getTextObjectRecord()
+ {
+ return textObjectRecord;
+ }
+
+ public EscherRecord getEscherTextbox()
+ {
+ return escherTextbox;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java
index e3593414c6..7a0a3e33a7 100644
--- a/src/java/org/apache/poi/hssf/model/Workbook.java
+++ b/src/java/org/apache/poi/hssf/model/Workbook.java
@@ -60,6 +60,7 @@ import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.hssf.util.SheetReferences;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
+import org.apache.poi.ddf.*;
import java.util.ArrayList;
import java.util.Iterator;
@@ -133,6 +134,7 @@ public class Workbook implements Model {
protected int numfonts = 0; // hold the number of font records
private short maxformatid = -1; // holds the max format id
private boolean uses1904datewindowing = false; // whether 1904 date windowing is being used
+ private DrawingManager drawingManager;
private static POILogger log = POILogFactory.getLogger(Workbook.class);
@@ -2090,6 +2092,56 @@ public class Workbook implements Model {
return palette;
}
+ /**
+ * Creates a drawing group record. If it already exists then it's left
+ * alone.
+ */
+ public void createDrawingGroup()
+ {
+ int dggLoc = findFirstRecordLocBySid(EscherContainerRecord.DGG_CONTAINER);
+ if (dggLoc == -1)
+ {
+ EscherContainerRecord dggContainer = new EscherContainerRecord();
+ EscherDggRecord dgg = new EscherDggRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherSplitMenuColorsRecord splitMenuColors = new EscherSplitMenuColorsRecord();
+
+ dggContainer.setRecordId((short) 0xF000);
+ dggContainer.setOptions((short) 0x000F);
+ dgg.setRecordId(EscherDggRecord.RECORD_ID);
+ dgg.setOptions((short)0x0000);
+ dgg.setShapeIdMax(1024);
+ dgg.setNumShapesSaved(0);
+ dgg.setDrawingsSaved(0);
+ dgg.setFileIdClusters(new EscherDggRecord.FileIdCluster[] {} );
+ drawingManager = new DrawingManager(dgg);
+ opt.setRecordId((short) 0xF00B);
+ opt.setOptions((short) 0x0033);
+ opt.addEscherProperty( new EscherBoolProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 524296) );
+ opt.addEscherProperty( new EscherRGBProperty(EscherProperties.FILL__FILLCOLOR, 134217737) );
+ opt.addEscherProperty( new EscherRGBProperty(EscherProperties.LINESTYLE__COLOR, 134217792) );
+ splitMenuColors.setRecordId((short) 0xF11E);
+ splitMenuColors.setOptions((short) 0x0040);
+ splitMenuColors.setColor1(0x0800000D);
+ splitMenuColors.setColor2(0x0800000C);
+ splitMenuColors.setColor3(0x08000017);
+ splitMenuColors.setColor4(0x100000F7);
+
+ dggContainer.addChildRecord(dgg);
+ dggContainer.addChildRecord(opt);
+ dggContainer.addChildRecord(splitMenuColors);
+
+ DrawingGroupRecord drawingGroup = new DrawingGroupRecord();
+ drawingGroup.addEscherRecord(dggContainer);
+ int loc = findFirstRecordLocBySid(CountryRecord.sid);
+ getRecords().add(loc+1, drawingGroup);
+ }
+ }
+
+ public DrawingManager getDrawingManager()
+ {
+ return drawingManager;
+ }
}
diff --git a/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java b/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java
new file mode 100644
index 0000000000..59d85acff6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/AbstractEscherHolderRecord.java
@@ -0,0 +1,266 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordFactory;
+import org.apache.poi.ddf.NullEscherSerializationListener;
+import org.apache.poi.util.LittleEndian;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The escher container record is used to hold escher records. It is abstract and
+ * must be subclassed for maximum benefit.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public abstract class AbstractEscherHolderRecord
+ extends Record
+{
+ private static final boolean DESERIALISE = System.getProperty("poi.deserialize.escher") != null;
+
+ private List escherRecords;
+ private byte[] rawData;
+
+
+ public AbstractEscherHolderRecord()
+ {
+ escherRecords = new ArrayList();
+ }
+
+ /**
+ * Constructs a Bar record and sets its fields appropriately.
+ *
+ * @param id id must be 0x1017 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public AbstractEscherHolderRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a Bar record and sets its fields appropriately.
+ *
+ * @param id id must be 0x1017 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public AbstractEscherHolderRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != getSid())
+ {
+ throw new RecordFormatException("Not a Bar record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+ escherRecords = new ArrayList();
+ if (! DESERIALISE )
+ {
+ rawData = new byte[size];
+ System.arraycopy(data, offset, rawData, 0, size);
+ }
+ else
+ {
+ EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
+ int pos = offset;
+ while ( pos < offset + size )
+ {
+ EscherRecord r = recordFactory.createRecord(data, pos);
+ int bytesRead = r.fillFields(data, pos, recordFactory );
+ escherRecords.add(r);
+ pos += bytesRead;
+ }
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ final String nl = System.getProperty("line.separator");
+ buffer.append("[" + getRecordName() + "]" + nl);
+ for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ buffer.append(r.toString());
+ }
+ buffer.append("[/" + getRecordName() + "]" + nl);
+
+ return buffer.toString();
+ }
+
+ protected abstract String getRecordName();
+
+ public int serialize(int offset, byte[] data)
+ {
+ if (escherRecords.size() == 0 && rawData != null)
+ {
+ System.arraycopy( rawData, 0, data, offset, rawData.length);
+ return rawData.length;
+ }
+ else
+ {
+ collapseShapeInformation();
+
+ LittleEndian.putShort(data, 0 + offset, getSid());
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+ int pos = offset + 4;
+ for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ pos += r.serialize(pos, data, new NullEscherSerializationListener() );
+ }
+
+ return getRecordSize();
+ }
+ }
+
+ /**
+ * Size of record (including 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ if (escherRecords.size() == 0 && rawData != null)
+ {
+ return rawData.length;
+ }
+ else
+ {
+ collapseShapeInformation();
+
+ int size = 4;
+ for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ size += r.getRecordSize();
+ }
+ return size;
+ }
+ }
+
+ private void collapseShapeInformation()
+ {
+
+ }
+
+ public abstract short getSid();
+
+ public Object clone()
+ {
+ throw new IllegalStateException("Not implemented yet.");
+ }
+
+ public void addEscherRecord(int index, EscherRecord element)
+ {
+ escherRecords.add( index, element );
+ }
+
+ public boolean addEscherRecord(EscherRecord element)
+ {
+ return escherRecords.add( element );
+ }
+
+ public List getEscherRecords()
+ {
+ return escherRecords;
+ }
+
+ public void clearEscherRecords()
+ {
+ escherRecords.clear();
+ }
+
+
+ public EscherRecord getEscherRecord(int index)
+ {
+ return (EscherRecord) escherRecords.get(index);
+ }
+
+
+} // END OF CLASS
+
+
+
+
diff --git a/src/java/org/apache/poi/hssf/record/CommonObjectDataSubRecord.java b/src/java/org/apache/poi/hssf/record/CommonObjectDataSubRecord.java
new file mode 100644
index 0000000000..65c76d6041
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/CommonObjectDataSubRecord.java
@@ -0,0 +1,498 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * The common object data record is used to store all common preferences for an excel object.
+ * NOTE: This source is automatically generated please do not modify this file. Either subclass or
+ * remove the record in src/records/definitions.
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class CommonObjectDataSubRecord
+ extends SubRecord
+{
+ public final static short sid = 0x15;
+ private short field_1_objectType;
+ public final static short OBJECT_TYPE_GROUP = 0;
+ public final static short OBJECT_TYPE_LINE = 1;
+ public final static short OBJECT_TYPE_RECTANGLE = 2;
+ public final static short OBJECT_TYPE_OVAL = 3;
+ public final static short OBJECT_TYPE_ARC = 4;
+ public final static short OBJECT_TYPE_CHART = 5;
+ public final static short OBJECT_TYPE_TEXT = 6;
+ public final static short OBJECT_TYPE_BUTTON = 7;
+ public final static short OBJECT_TYPE_PICTURE = 8;
+ public final static short OBJECT_TYPE_POLYGON = 9;
+ public final static short OBJECT_TYPE_RESERVED1 = 10;
+ public final static short OBJECT_TYPE_CHECKBOX = 11;
+ public final static short OBJECT_TYPE_OPTION_BUTTON = 12;
+ public final static short OBJECT_TYPE_EDIT_BOX = 13;
+ public final static short OBJECT_TYPE_LABEL = 14;
+ public final static short OBJECT_TYPE_DIALOG_BOX = 15;
+ public final static short OBJECT_TYPE_SPINNER = 16;
+ public final static short OBJECT_TYPE_SCROLL_BAR = 17;
+ public final static short OBJECT_TYPE_LIST_BOX = 18;
+ public final static short OBJECT_TYPE_GROUP_BOX = 19;
+ public final static short OBJECT_TYPE_COMBO_BOX = 20;
+ public final static short OBJECT_TYPE_RESERVED2 = 21;
+ public final static short OBJECT_TYPE_RESERVED3 = 22;
+ public final static short OBJECT_TYPE_RESERVED4 = 23;
+ public final static short OBJECT_TYPE_RESERVED5 = 24;
+ public final static short OBJECT_TYPE_COMMENT = 25;
+ public final static short OBJECT_TYPE_RESERVED6 = 26;
+ public final static short OBJECT_TYPE_RESERVED7 = 27;
+ public final static short OBJECT_TYPE_RESERVED8 = 28;
+ public final static short OBJECT_TYPE_RESERVED9 = 29;
+ public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30;
+ private short field_2_objectId;
+ private short field_3_option;
+ private BitField locked = new BitField(0x1);
+ private BitField printable = new BitField(0x10);
+ private BitField autofill = new BitField(0x2000);
+ private BitField autoline = new BitField(0x4000);
+ private int field_4_reserved1;
+ private int field_5_reserved2;
+ private int field_6_reserved3;
+
+
+ public CommonObjectDataSubRecord()
+ {
+
+ }
+
+ /**
+ * Constructs a CommonObjectData record and sets its fields appropriately.
+ *
+ * @param id id must be 0x15 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public CommonObjectDataSubRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a CommonObjectData record and sets its fields appropriately.
+ *
+ * @param id id must be 0x15 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public CommonObjectDataSubRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a CommonObjectData record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+
+ int pos = 0;
+ field_1_objectType = LittleEndian.getShort(data, pos + 0x0 + offset);
+ field_2_objectId = LittleEndian.getShort(data, pos + 0x2 + offset);
+ field_3_option = LittleEndian.getShort(data, pos + 0x4 + offset);
+ field_4_reserved1 = LittleEndian.getInt(data, pos + 0x6 + offset);
+ field_5_reserved2 = LittleEndian.getInt(data, pos + 0xa + offset);
+ field_6_reserved3 = LittleEndian.getInt(data, pos + 0xe + offset);
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[ftCmo]\n");
+ buffer.append(" .objectType = ")
+ .append("0x").append(HexDump.toHex( getObjectType ()))
+ .append(" (").append( getObjectType() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .objectId = ")
+ .append("0x").append(HexDump.toHex( getObjectId ()))
+ .append(" (").append( getObjectId() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .option = ")
+ .append("0x").append(HexDump.toHex( getOption ()))
+ .append(" (").append( getOption() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .locked = ").append(isLocked()).append('\n');
+ buffer.append(" .printable = ").append(isPrintable()).append('\n');
+ buffer.append(" .autofill = ").append(isAutofill()).append('\n');
+ buffer.append(" .autoline = ").append(isAutoline()).append('\n');
+ buffer.append(" .reserved1 = ")
+ .append("0x").append(HexDump.toHex( getReserved1 ()))
+ .append(" (").append( getReserved1() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved2 = ")
+ .append("0x").append(HexDump.toHex( getReserved2 ()))
+ .append(" (").append( getReserved2() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved3 = ")
+ .append("0x").append(HexDump.toHex( getReserved3 ()))
+ .append(" (").append( getReserved3() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+
+ buffer.append("[/ftCmo]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+ LittleEndian.putShort(data, 4 + offset + pos, field_1_objectType);
+ LittleEndian.putShort(data, 6 + offset + pos, field_2_objectId);
+ LittleEndian.putShort(data, 8 + offset + pos, field_3_option);
+ LittleEndian.putInt(data, 10 + offset + pos, field_4_reserved1);
+ LittleEndian.putInt(data, 14 + offset + pos, field_5_reserved2);
+ LittleEndian.putInt(data, 18 + offset + pos, field_6_reserved3);
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ return 4 + 2 + 2 + 2 + 4 + 4 + 4;
+ }
+
+ public short getSid()
+ {
+ return this.sid;
+ }
+
+ public Object clone() {
+ CommonObjectDataSubRecord rec = new CommonObjectDataSubRecord();
+
+ rec.field_1_objectType = field_1_objectType;
+ rec.field_2_objectId = field_2_objectId;
+ rec.field_3_option = field_3_option;
+ rec.field_4_reserved1 = field_4_reserved1;
+ rec.field_5_reserved2 = field_5_reserved2;
+ rec.field_6_reserved3 = field_6_reserved3;
+ return rec;
+ }
+
+
+ /**
+ * Get the object type field for the CommonObjectData record.
+ *
+ * @return One of
+ * OBJECT_TYPE_GROUP
+ * OBJECT_TYPE_LINE
+ * OBJECT_TYPE_RECTANGLE
+ * OBJECT_TYPE_OVAL
+ * OBJECT_TYPE_ARC
+ * OBJECT_TYPE_CHART
+ * OBJECT_TYPE_TEXT
+ * OBJECT_TYPE_BUTTON
+ * OBJECT_TYPE_PICTURE
+ * OBJECT_TYPE_POLYGON
+ * OBJECT_TYPE_RESERVED1
+ * OBJECT_TYPE_CHECKBOX
+ * OBJECT_TYPE_OPTION_BUTTON
+ * OBJECT_TYPE_EDIT_BOX
+ * OBJECT_TYPE_LABEL
+ * OBJECT_TYPE_DIALOG_BOX
+ * OBJECT_TYPE_SPINNER
+ * OBJECT_TYPE_SCROLL_BAR
+ * OBJECT_TYPE_LIST_BOX
+ * OBJECT_TYPE_GROUP_BOX
+ * OBJECT_TYPE_COMBO_BOX
+ * OBJECT_TYPE_RESERVED2
+ * OBJECT_TYPE_RESERVED3
+ * OBJECT_TYPE_RESERVED4
+ * OBJECT_TYPE_RESERVED5
+ * OBJECT_TYPE_COMMENT
+ * OBJECT_TYPE_RESERVED6
+ * OBJECT_TYPE_RESERVED7
+ * OBJECT_TYPE_RESERVED8
+ * OBJECT_TYPE_RESERVED9
+ * OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING
+ */
+ public short getObjectType()
+ {
+ return field_1_objectType;
+ }
+
+ /**
+ * Set the object type field for the CommonObjectData record.
+ *
+ * @param field_1_objectType
+ * One of
+ * OBJECT_TYPE_GROUP
+ * OBJECT_TYPE_LINE
+ * OBJECT_TYPE_RECTANGLE
+ * OBJECT_TYPE_OVAL
+ * OBJECT_TYPE_ARC
+ * OBJECT_TYPE_CHART
+ * OBJECT_TYPE_TEXT
+ * OBJECT_TYPE_BUTTON
+ * OBJECT_TYPE_PICTURE
+ * OBJECT_TYPE_POLYGON
+ * OBJECT_TYPE_RESERVED1
+ * OBJECT_TYPE_CHECKBOX
+ * OBJECT_TYPE_OPTION_BUTTON
+ * OBJECT_TYPE_EDIT_BOX
+ * OBJECT_TYPE_LABEL
+ * OBJECT_TYPE_DIALOG_BOX
+ * OBJECT_TYPE_SPINNER
+ * OBJECT_TYPE_SCROLL_BAR
+ * OBJECT_TYPE_LIST_BOX
+ * OBJECT_TYPE_GROUP_BOX
+ * OBJECT_TYPE_COMBO_BOX
+ * OBJECT_TYPE_RESERVED2
+ * OBJECT_TYPE_RESERVED3
+ * OBJECT_TYPE_RESERVED4
+ * OBJECT_TYPE_RESERVED5
+ * OBJECT_TYPE_COMMENT
+ * OBJECT_TYPE_RESERVED6
+ * OBJECT_TYPE_RESERVED7
+ * OBJECT_TYPE_RESERVED8
+ * OBJECT_TYPE_RESERVED9
+ * OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING
+ */
+ public void setObjectType(short field_1_objectType)
+ {
+ this.field_1_objectType = field_1_objectType;
+ }
+
+ /**
+ * Get the object id field for the CommonObjectData record.
+ */
+ public short getObjectId()
+ {
+ return field_2_objectId;
+ }
+
+ /**
+ * Set the object id field for the CommonObjectData record.
+ */
+ public void setObjectId(short field_2_objectId)
+ {
+ this.field_2_objectId = field_2_objectId;
+ }
+
+ /**
+ * Get the option field for the CommonObjectData record.
+ */
+ public short getOption()
+ {
+ return field_3_option;
+ }
+
+ /**
+ * Set the option field for the CommonObjectData record.
+ */
+ public void setOption(short field_3_option)
+ {
+ this.field_3_option = field_3_option;
+ }
+
+ /**
+ * Get the reserved1 field for the CommonObjectData record.
+ */
+ public int getReserved1()
+ {
+ return field_4_reserved1;
+ }
+
+ /**
+ * Set the reserved1 field for the CommonObjectData record.
+ */
+ public void setReserved1(int field_4_reserved1)
+ {
+ this.field_4_reserved1 = field_4_reserved1;
+ }
+
+ /**
+ * Get the reserved2 field for the CommonObjectData record.
+ */
+ public int getReserved2()
+ {
+ return field_5_reserved2;
+ }
+
+ /**
+ * Set the reserved2 field for the CommonObjectData record.
+ */
+ public void setReserved2(int field_5_reserved2)
+ {
+ this.field_5_reserved2 = field_5_reserved2;
+ }
+
+ /**
+ * Get the reserved3 field for the CommonObjectData record.
+ */
+ public int getReserved3()
+ {
+ return field_6_reserved3;
+ }
+
+ /**
+ * Set the reserved3 field for the CommonObjectData record.
+ */
+ public void setReserved3(int field_6_reserved3)
+ {
+ this.field_6_reserved3 = field_6_reserved3;
+ }
+
+ /**
+ * Sets the locked field value.
+ * true if object is locked when sheet has been protected
+ */
+ public void setLocked(boolean value)
+ {
+ field_3_option = locked.setShortBoolean(field_3_option, value);
+ }
+
+ /**
+ * true if object is locked when sheet has been protected
+ * @return the locked field value.
+ */
+ public boolean isLocked()
+ {
+ return locked.isSet(field_3_option);
+ }
+
+ /**
+ * Sets the printable field value.
+ * object appears when printed
+ */
+ public void setPrintable(boolean value)
+ {
+ field_3_option = printable.setShortBoolean(field_3_option, value);
+ }
+
+ /**
+ * object appears when printed
+ * @return the printable field value.
+ */
+ public boolean isPrintable()
+ {
+ return printable.isSet(field_3_option);
+ }
+
+ /**
+ * Sets the autofill field value.
+ * whether object uses an automatic fill style
+ */
+ public void setAutofill(boolean value)
+ {
+ field_3_option = autofill.setShortBoolean(field_3_option, value);
+ }
+
+ /**
+ * whether object uses an automatic fill style
+ * @return the autofill field value.
+ */
+ public boolean isAutofill()
+ {
+ return autofill.isSet(field_3_option);
+ }
+
+ /**
+ * Sets the autoline field value.
+ * whether object uses an automatic line style
+ */
+ public void setAutoline(boolean value)
+ {
+ field_3_option = autoline.setShortBoolean(field_3_option, value);
+ }
+
+ /**
+ * whether object uses an automatic line style
+ * @return the autoline field value.
+ */
+ public boolean isAutoline()
+ {
+ return autoline.isSet(field_3_option);
+ }
+
+
+} // END OF CLASS
+
+
diff --git a/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java b/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java
new file mode 100644
index 0000000000..9d3ab19ce1
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/DrawingGroupRecord.java
@@ -0,0 +1,30 @@
+package org.apache.poi.hssf.record;
+
+public class DrawingGroupRecord extends AbstractEscherHolderRecord
+{
+ public static final short sid = 0xEB;
+
+ public DrawingGroupRecord()
+ {
+ }
+
+ public DrawingGroupRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public DrawingGroupRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ protected String getRecordName()
+ {
+ return "MSODRAWINGGROUP";
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/DrawingRecord.java b/src/java/org/apache/poi/hssf/record/DrawingRecord.java
new file mode 100644
index 0000000000..cb847afeb6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/DrawingRecord.java
@@ -0,0 +1,97 @@
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.util.LittleEndian;
+
+public class DrawingRecord extends Record
+{
+ public static final short sid = 0xEC;
+
+ private byte[] recordData;
+
+ public DrawingRecord()
+ {
+ }
+
+ public DrawingRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public DrawingRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a MSODRAWING record");
+ }
+ }
+
+ protected void fillFields( byte[] data, short size, int offset )
+ {
+ if (offset == 0 && size == data.length)
+ {
+ recordData = data;
+ }
+ else
+ {
+ recordData = new byte[size];
+ System.arraycopy(data, offset, recordData, 0, size);
+ }
+ }
+
+ protected void fillFields( byte[] data, short size )
+ {
+ recordData = data;
+ }
+
+ public int serialize( int offset, byte[] data )
+ {
+ if (recordData == null)
+ {
+ recordData = new byte[ 0 ];
+ }
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, ( short ) (recordData.length));
+ if (recordData.length > 0)
+ {
+ System.arraycopy(recordData, 0, data, 4 + offset, recordData.length);
+ }
+ return getRecordSize();
+ }
+
+ public int getRecordSize()
+ {
+ int retval = 4;
+
+ if (recordData != null)
+ {
+ retval += recordData.length;
+ }
+ return retval;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public byte[] getData()
+ {
+ return recordData;
+ }
+
+ public void setData( byte[] thedata )
+ {
+ this.recordData = thedata;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java b/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java
new file mode 100644
index 0000000000..2dbd71418f
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/DrawingRecordForBiffViewer.java
@@ -0,0 +1,35 @@
+package org.apache.poi.hssf.record;
+
+/**
+ * This is purely for the biff viewer. During normal operations we don't want
+ * to be seeing this.
+ */
+public class DrawingRecordForBiffViewer
+ extends AbstractEscherHolderRecord
+{
+ public static final short sid = 0xEC;
+
+ public DrawingRecordForBiffViewer()
+ {
+ }
+
+ public DrawingRecordForBiffViewer( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public DrawingRecordForBiffViewer( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ protected String getRecordName()
+ {
+ return "MSODRAWING";
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/DrawingSelectionRecord.java b/src/java/org/apache/poi/hssf/record/DrawingSelectionRecord.java
new file mode 100644
index 0000000000..2ae8660fc1
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/DrawingSelectionRecord.java
@@ -0,0 +1,30 @@
+package org.apache.poi.hssf.record;
+
+public class DrawingSelectionRecord extends AbstractEscherHolderRecord
+{
+ public static final short sid = 0xED;
+
+ public DrawingSelectionRecord()
+ {
+ }
+
+ public DrawingSelectionRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public DrawingSelectionRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ protected String getRecordName()
+ {
+ return "MSODRAWINGSELECTION";
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/EndSubRecord.java b/src/java/org/apache/poi/hssf/record/EndSubRecord.java
new file mode 100644
index 0000000000..22ac23462d
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/EndSubRecord.java
@@ -0,0 +1,176 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * The end data record is used to denote the end of the subrecords.
+ * NOTE: This source is automatically generated please do not modify this file. Either subclass or
+ * remove the record in src/records/definitions.
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EndSubRecord
+ extends SubRecord
+{
+ public final static short sid = 0x00;
+
+
+ public EndSubRecord()
+ {
+
+ }
+
+ /**
+ * Constructs a End record and sets its fields appropriately.
+ *
+ * @param id id must be 0x00 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public EndSubRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a End record and sets its fields appropriately.
+ *
+ * @param id id must be 0x00 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public EndSubRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a End record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+
+ int pos = 0;
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[ftEnd]\n");
+
+ buffer.append("[/ftEnd]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ return 4 ;
+ }
+
+ public short getSid()
+ {
+ return this.sid;
+ }
+
+ public Object clone() {
+ EndSubRecord rec = new EndSubRecord();
+
+ return rec;
+ }
+
+
+
+} // END OF CLASS
+
+
diff --git a/src/java/org/apache/poi/hssf/record/EscherAggregate.java b/src/java/org/apache/poi/hssf/record/EscherAggregate.java
new file mode 100644
index 0000000000..a45621927b
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/EscherAggregate.java
@@ -0,0 +1,696 @@
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.hssf.model.AbstractShape;
+import org.apache.poi.hssf.model.TextboxShape;
+import org.apache.poi.hssf.model.DrawingManager;
+import org.apache.poi.hssf.model.ConvertAnchor;
+
+import java.util.*;
+
+/**
+ * This class is used to aggregate the MSODRAWING and OBJ record
+ * combinations. This is necessary due to the bizare way in which
+ * these records are serialized. What happens is that you get a
+ * combination of MSODRAWING -> OBJ -> MSODRAWING -> OBJ records
+ * but the escher records are serialized _across_ the MSODRAWING
+ * records.
+ * <p>
+ * It gets even worse when you start looking at TXO records.
+ * <p>
+ * So what we do with this class is aggregate lazily. That is
+ * we don't aggregate the MSODRAWING -> OBJ records unless we
+ * need to modify them.
+ *
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherAggregate extends AbstractEscherHolderRecord
+{
+ public static final short sid = 9876;
+
+ public static final short ST_MIN = (short) 0;
+ public static final short ST_NOT_PRIMATIVE = ST_MIN;
+ public static final short ST_RECTANGLE = (short) 1;
+ public static final short ST_ROUNDRECTANGLE = (short) 2;
+ public static final short ST_ELLIPSE = (short) 3;
+ public static final short ST_DIAMOND = (short) 4;
+ public static final short ST_ISOCELESTRIANGLE = (short) 5;
+ public static final short ST_RIGHTTRIANGLE = (short) 6;
+ public static final short ST_PARALLELOGRAM = (short) 7;
+ public static final short ST_TRAPEZOID = (short) 8;
+ public static final short ST_HEXAGON = (short) 9;
+ public static final short ST_OCTAGON = (short) 10;
+ public static final short ST_PLUS = (short) 11;
+ public static final short ST_STAR = (short) 12;
+ public static final short ST_ARROW = (short) 13;
+ public static final short ST_THICKARROW = (short) 14;
+ public static final short ST_HOMEPLATE = (short) 15;
+ public static final short ST_CUBE = (short) 16;
+ public static final short ST_BALLOON = (short) 17;
+ public static final short ST_SEAL = (short) 18;
+ public static final short ST_ARC = (short) 19;
+ public static final short ST_LINE = (short) 20;
+ public static final short ST_PLAQUE = (short) 21;
+ public static final short ST_CAN = (short) 22;
+ public static final short ST_DONUT = (short) 23;
+ public static final short ST_TEXTSIMPLE = (short) 24;
+ public static final short ST_TEXTOCTAGON = (short) 25;
+ public static final short ST_TEXTHEXAGON = (short) 26;
+ public static final short ST_TEXTCURVE = (short) 27;
+ public static final short ST_TEXTWAVE = (short) 28;
+ public static final short ST_TEXTRING = (short) 29;
+ public static final short ST_TEXTONCURVE = (short) 30;
+ public static final short ST_TEXTONRING = (short) 31;
+ public static final short ST_STRAIGHTCONNECTOR1 = (short) 32;
+ public static final short ST_BENTCONNECTOR2 = (short) 33;
+ public static final short ST_BENTCONNECTOR3 = (short) 34;
+ public static final short ST_BENTCONNECTOR4 = (short) 35;
+ public static final short ST_BENTCONNECTOR5 = (short) 36;
+ public static final short ST_CURVEDCONNECTOR2 = (short) 37;
+ public static final short ST_CURVEDCONNECTOR3 = (short) 38;
+ public static final short ST_CURVEDCONNECTOR4 = (short) 39;
+ public static final short ST_CURVEDCONNECTOR5 = (short) 40;
+ public static final short ST_CALLOUT1 = (short) 41;
+ public static final short ST_CALLOUT2 = (short) 42;
+ public static final short ST_CALLOUT3 = (short) 43;
+ public static final short ST_ACCENTCALLOUT1 = (short) 44;
+ public static final short ST_ACCENTCALLOUT2 = (short) 45;
+ public static final short ST_ACCENTCALLOUT3 = (short) 46;
+ public static final short ST_BORDERCALLOUT1 = (short) 47;
+ public static final short ST_BORDERCALLOUT2 = (short) 48;
+ public static final short ST_BORDERCALLOUT3 = (short) 49;
+ public static final short ST_ACCENTBORDERCALLOUT1 = (short) 50;
+ public static final short ST_ACCENTBORDERCALLOUT2 = (short) 51;
+ public static final short ST_ACCENTBORDERCALLOUT3 = (short) 52;
+ public static final short ST_RIBBON = (short) 53;
+ public static final short ST_RIBBON2 = (short) 54;
+ public static final short ST_CHEVRON = (short) 55;
+ public static final short ST_PENTAGON = (short) 56;
+ public static final short ST_NOSMOKING = (short) 57;
+ public static final short ST_SEAL8 = (short) 58;
+ public static final short ST_SEAL16 = (short) 59;
+ public static final short ST_SEAL32 = (short) 60;
+ public static final short ST_WEDGERECTCALLOUT = (short) 61;
+ public static final short ST_WEDGERRECTCALLOUT = (short) 62;
+ public static final short ST_WEDGEELLIPSECALLOUT = (short) 63;
+ public static final short ST_WAVE = (short) 64;
+ public static final short ST_FOLDEDCORNER = (short) 65;
+ public static final short ST_LEFTARROW = (short) 66;
+ public static final short ST_DOWNARROW = (short) 67;
+ public static final short ST_UPARROW = (short) 68;
+ public static final short ST_LEFTRIGHTARROW = (short) 69;
+ public static final short ST_UPDOWNARROW = (short) 70;
+ public static final short ST_IRREGULARSEAL1 = (short) 71;
+ public static final short ST_IRREGULARSEAL2 = (short) 72;
+ public static final short ST_LIGHTNINGBOLT = (short) 73;
+ public static final short ST_HEART = (short) 74;
+ public static final short ST_PICTUREFRAME = (short) 75;
+ public static final short ST_QUADARROW = (short) 76;
+ public static final short ST_LEFTARROWCALLOUT = (short) 77;
+ public static final short ST_RIGHTARROWCALLOUT = (short) 78;
+ public static final short ST_UPARROWCALLOUT = (short) 79;
+ public static final short ST_DOWNARROWCALLOUT = (short) 80;
+ public static final short ST_LEFTRIGHTARROWCALLOUT = (short) 81;
+ public static final short ST_UPDOWNARROWCALLOUT = (short) 82;
+ public static final short ST_QUADARROWCALLOUT = (short) 83;
+ public static final short ST_BEVEL = (short) 84;
+ public static final short ST_LEFTBRACKET = (short) 85;
+ public static final short ST_RIGHTBRACKET = (short) 86;
+ public static final short ST_LEFTBRACE = (short) 87;
+ public static final short ST_RIGHTBRACE = (short) 88;
+ public static final short ST_LEFTUPARROW = (short) 89;
+ public static final short ST_BENTUPARROW = (short) 90;
+ public static final short ST_BENTARROW = (short) 91;
+ public static final short ST_SEAL24 = (short) 92;
+ public static final short ST_STRIPEDRIGHTARROW = (short) 93;
+ public static final short ST_NOTCHEDRIGHTARROW = (short) 94;
+ public static final short ST_BLOCKARC = (short) 95;
+ public static final short ST_SMILEYFACE = (short) 96;
+ public static final short ST_VERTICALSCROLL = (short) 97;
+ public static final short ST_HORIZONTALSCROLL = (short) 98;
+ public static final short ST_CIRCULARARROW = (short) 99;
+ public static final short ST_NOTCHEDCIRCULARARROW = (short) 100;
+ public static final short ST_UTURNARROW = (short) 101;
+ public static final short ST_CURVEDRIGHTARROW = (short) 102;
+ public static final short ST_CURVEDLEFTARROW = (short) 103;
+ public static final short ST_CURVEDUPARROW = (short) 104;
+ public static final short ST_CURVEDDOWNARROW = (short) 105;
+ public static final short ST_CLOUDCALLOUT = (short) 106;
+ public static final short ST_ELLIPSERIBBON = (short) 107;
+ public static final short ST_ELLIPSERIBBON2 = (short) 108;
+ public static final short ST_FLOWCHARTPROCESS = (short) 109;
+ public static final short ST_FLOWCHARTDECISION = (short) 110;
+ public static final short ST_FLOWCHARTINPUTOUTPUT = (short) 111;
+ public static final short ST_FLOWCHARTPREDEFINEDPROCESS = (short) 112;
+ public static final short ST_FLOWCHARTINTERNALSTORAGE = (short) 113;
+ public static final short ST_FLOWCHARTDOCUMENT = (short) 114;
+ public static final short ST_FLOWCHARTMULTIDOCUMENT = (short) 115;
+ public static final short ST_FLOWCHARTTERMINATOR = (short) 116;
+ public static final short ST_FLOWCHARTPREPARATION = (short) 117;
+ public static final short ST_FLOWCHARTMANUALINPUT = (short) 118;
+ public static final short ST_FLOWCHARTMANUALOPERATION = (short) 119;
+ public static final short ST_FLOWCHARTCONNECTOR = (short) 120;
+ public static final short ST_FLOWCHARTPUNCHEDCARD = (short) 121;
+ public static final short ST_FLOWCHARTPUNCHEDTAPE = (short) 122;
+ public static final short ST_FLOWCHARTSUMMINGJUNCTION = (short) 123;
+ public static final short ST_FLOWCHARTOR = (short) 124;
+ public static final short ST_FLOWCHARTCOLLATE = (short) 125;
+ public static final short ST_FLOWCHARTSORT = (short) 126;
+ public static final short ST_FLOWCHARTEXTRACT = (short) 127;
+ public static final short ST_FLOWCHARTMERGE = (short) 128;
+ public static final short ST_FLOWCHARTOFFLINESTORAGE = (short) 129;
+ public static final short ST_FLOWCHARTONLINESTORAGE = (short) 130;
+ public static final short ST_FLOWCHARTMAGNETICTAPE = (short) 131;
+ public static final short ST_FLOWCHARTMAGNETICDISK = (short) 132;
+ public static final short ST_FLOWCHARTMAGNETICDRUM = (short) 133;
+ public static final short ST_FLOWCHARTDISPLAY = (short) 134;
+ public static final short ST_FLOWCHARTDELAY = (short) 135;
+ public static final short ST_TEXTPLAINTEXT = (short) 136;
+ public static final short ST_TEXTSTOP = (short) 137;
+ public static final short ST_TEXTTRIANGLE = (short) 138;
+ public static final short ST_TEXTTRIANGLEINVERTED = (short) 139;
+ public static final short ST_TEXTCHEVRON = (short) 140;
+ public static final short ST_TEXTCHEVRONINVERTED = (short) 141;
+ public static final short ST_TEXTRINGINSIDE = (short) 142;
+ public static final short ST_TEXTRINGOUTSIDE = (short) 143;
+ public static final short ST_TEXTARCHUPCURVE = (short) 144;
+ public static final short ST_TEXTARCHDOWNCURVE = (short) 145;
+ public static final short ST_TEXTCIRCLECURVE = (short) 146;
+ public static final short ST_TEXTBUTTONCURVE = (short) 147;
+ public static final short ST_TEXTARCHUPPOUR = (short) 148;
+ public static final short ST_TEXTARCHDOWNPOUR = (short) 149;
+ public static final short ST_TEXTCIRCLEPOUR = (short) 150;
+ public static final short ST_TEXTBUTTONPOUR = (short) 151;
+ public static final short ST_TEXTCURVEUP = (short) 152;
+ public static final short ST_TEXTCURVEDOWN = (short) 153;
+ public static final short ST_TEXTCASCADEUP = (short) 154;
+ public static final short ST_TEXTCASCADEDOWN = (short) 155;
+ public static final short ST_TEXTWAVE1 = (short) 156;
+ public static final short ST_TEXTWAVE2 = (short) 157;
+ public static final short ST_TEXTWAVE3 = (short) 158;
+ public static final short ST_TEXTWAVE4 = (short) 159;
+ public static final short ST_TEXTINFLATE = (short) 160;
+ public static final short ST_TEXTDEFLATE = (short) 161;
+ public static final short ST_TEXTINFLATEBOTTOM = (short) 162;
+ public static final short ST_TEXTDEFLATEBOTTOM = (short) 163;
+ public static final short ST_TEXTINFLATETOP = (short) 164;
+ public static final short ST_TEXTDEFLATETOP = (short) 165;
+ public static final short ST_TEXTDEFLATEINFLATE = (short) 166;
+ public static final short ST_TEXTDEFLATEINFLATEDEFLATE = (short) 167;
+ public static final short ST_TEXTFADERIGHT = (short) 168;
+ public static final short ST_TEXTFADELEFT = (short) 169;
+ public static final short ST_TEXTFADEUP = (short) 170;
+ public static final short ST_TEXTFADEDOWN = (short) 171;
+ public static final short ST_TEXTSLANTUP = (short) 172;
+ public static final short ST_TEXTSLANTDOWN = (short) 173;
+ public static final short ST_TEXTCANUP = (short) 174;
+ public static final short ST_TEXTCANDOWN = (short) 175;
+ public static final short ST_FLOWCHARTALTERNATEPROCESS = (short) 176;
+ public static final short ST_FLOWCHARTOFFPAGECONNECTOR = (short) 177;
+ public static final short ST_CALLOUT90 = (short) 178;
+ public static final short ST_ACCENTCALLOUT90 = (short) 179;
+ public static final short ST_BORDERCALLOUT90 = (short) 180;
+ public static final short ST_ACCENTBORDERCALLOUT90 = (short) 181;
+ public static final short ST_LEFTRIGHTUPARROW = (short) 182;
+ public static final short ST_SUN = (short) 183;
+ public static final short ST_MOON = (short) 184;
+ public static final short ST_BRACKETPAIR = (short) 185;
+ public static final short ST_BRACEPAIR = (short) 186;
+ public static final short ST_SEAL4 = (short) 187;
+ public static final short ST_DOUBLEWAVE = (short) 188;
+ public static final short ST_ACTIONBUTTONBLANK = (short) 189;
+ public static final short ST_ACTIONBUTTONHOME = (short) 190;
+ public static final short ST_ACTIONBUTTONHELP = (short) 191;
+ public static final short ST_ACTIONBUTTONINFORMATION = (short) 192;
+ public static final short ST_ACTIONBUTTONFORWARDNEXT = (short) 193;
+ public static final short ST_ACTIONBUTTONBACKPREVIOUS = (short) 194;
+ public static final short ST_ACTIONBUTTONEND = (short) 195;
+ public static final short ST_ACTIONBUTTONBEGINNING = (short) 196;
+ public static final short ST_ACTIONBUTTONRETURN = (short) 197;
+ public static final short ST_ACTIONBUTTONDOCUMENT = (short) 198;
+ public static final short ST_ACTIONBUTTONSOUND = (short) 199;
+ public static final short ST_ACTIONBUTTONMOVIE = (short) 200;
+ public static final short ST_HOSTCONTROL = (short) 201;
+ public static final short ST_TEXTBOX = (short) 202;
+ public static final short ST_NIL = (short) 0x0FFF;
+
+ protected HSSFPatriarch patriarch;
+
+ /** Maps shape container objects to their OBJ records */
+ private Map shapeToObj = new HashMap();
+ private DrawingManager drawingManager;
+ private short drawingGroupId;
+
+ public EscherAggregate( DrawingManager drawingManager )
+ {
+ this.drawingManager = drawingManager;
+ }
+
+ /**
+ * @return Returns the current sid.
+ */
+ public short getSid()
+ {
+ return sid;
+ }
+
+ /**
+ * Unused since this is an aggregate record. Use createAggregate().
+ *
+ * @see #createAggregate
+ */
+ protected void fillFields( byte[] data, short size, int offset )
+ {
+ throw new IllegalStateException( "Should not reach here" );
+ }
+
+ /**
+ * Calculates the string representation of this record. This is
+ * simply a dump of all the records.
+ */
+ public String toString()
+ {
+ String nl = System.getProperty( "line.separtor" );
+
+ StringBuffer result = new StringBuffer();
+ result.append( '[' ).append( getRecordName() ).append( ']' + nl );
+ for ( Iterator iterator = getEscherRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord escherRecord = (EscherRecord) iterator.next();
+ result.append( escherRecord.toString() );
+ }
+ result.append( "[/" ).append( getRecordName() ).append( ']' + nl );
+
+ return result.toString();
+ }
+
+ /**
+ * Collapses the drawing records into an aggregate.
+ */
+ public static EscherAggregate createAggregate( List records, int locFirstDrawingRecord, DrawingManager drawingManager )
+ {
+ // Keep track of any shape records created so we can match them back to the object id's.
+ // Textbox objects are also treated as shape objects.
+ final List shapeRecords = new ArrayList();
+ EscherRecordFactory recordFactory = new DefaultEscherRecordFactory()
+ {
+ public EscherRecord createRecord( byte[] data, int offset )
+ {
+ EscherRecord r = super.createRecord( data, offset );
+ if ( r.getRecordId() == EscherClientDataRecord.RECORD_ID || r.getRecordId() == EscherTextboxRecord.RECORD_ID )
+ {
+ shapeRecords.add( r );
+ }
+ return r;
+ }
+ };
+
+ // Calculate the size of the buffer
+ EscherAggregate agg = new EscherAggregate(drawingManager);
+ int loc = locFirstDrawingRecord;
+ int dataSize = 0;
+ while ( loc + 1 < records.size()
+ && sid( records, loc ) == DrawingRecord.sid
+ && isObjectRecord( records, loc + 1 ) )
+ {
+ dataSize += ( (DrawingRecord) records.get( loc ) ).getData().length;
+ loc += 2;
+ }
+
+ // Create one big buffer
+ byte buffer[] = new byte[dataSize];
+ int offset = 0;
+ loc = locFirstDrawingRecord;
+ while ( loc + 1 < records.size()
+ && sid( records, loc ) == DrawingRecord.sid
+ && isObjectRecord( records, loc + 1 ) )
+ {
+ DrawingRecord drawingRecord = (DrawingRecord) records.get( loc );
+ System.arraycopy( drawingRecord.getData(), 0, buffer, offset, drawingRecord.getData().length );
+ offset += drawingRecord.getData().length;
+ loc += 2;
+ }
+
+ // Decode the shapes
+ // agg.escherRecords = new ArrayList();
+ int pos = 0;
+ while ( pos < dataSize )
+ {
+ EscherRecord r = recordFactory.createRecord( buffer, pos );
+ int bytesRead = r.fillFields( buffer, pos, recordFactory );
+ agg.addEscherRecord( r );
+ pos += bytesRead;
+ }
+
+ // Associate the object records with the shapes
+ loc = locFirstDrawingRecord;
+ int shapeIndex = 0;
+ agg.shapeToObj = new HashMap();
+ while ( loc + 1 < records.size()
+ && sid( records, loc ) == DrawingRecord.sid
+ && isObjectRecord( records, loc + 1 ) )
+ {
+ Record objRecord = (Record) records.get( loc + 1 );
+ agg.shapeToObj.put( shapeRecords.get( shapeIndex++ ), objRecord );
+ loc += 2;
+ }
+
+ return agg;
+
+ }
+
+ /**
+ * Serializes this aggregate to a byte array. Since this is an aggregate
+ * record it will effectively serialize the aggregated records.
+ *
+ * @param offset The offset into the start of the array.
+ * @param data The byte array to serialize to.
+ * @return The number of bytes serialized.
+ */
+ public int serialize( int offset, byte[] data )
+ {
+ convertUserModelToRecords();
+
+ // Determine buffer size
+ List records = getEscherRecords();
+ int size = getEscherRecordSize( records );
+ byte[] buffer = new byte[size];
+
+
+ // Serialize escher records into one big data structure and keep note of ending offsets.
+ final List spEndingOffsets = new ArrayList();
+ final List shapes = new ArrayList();
+ int pos = 0;
+ for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord e = (EscherRecord) iterator.next();
+ pos += e.serialize( pos, buffer, new EscherSerializationListener()
+ {
+ public void beforeRecordSerialize( int offset, short recordId, EscherRecord record )
+ {
+ }
+
+ public void afterRecordSerialize( int offset, short recordId, int size, EscherRecord record )
+ {
+ if ( recordId == EscherClientDataRecord.RECORD_ID || recordId == EscherTextboxRecord.RECORD_ID )
+ {
+ spEndingOffsets.add( new Integer( offset ) );
+ shapes.add( record );
+ }
+ }
+ } );
+ }
+ // todo: fix this
+ shapes.add( 0, null );
+ spEndingOffsets.add( 0, null );
+
+ // Split escher records into separate MSODRAWING and OBJ, TXO records. (We don't break on
+ // the first one because it's the patriach).
+ pos = offset;
+ for ( int i = 1; i < shapes.size(); i++ )
+ {
+ int endOffset = ( (Integer) spEndingOffsets.get( i ) ).intValue() - 1;
+ int startOffset;
+ if ( i == 1 )
+ startOffset = 0;
+ else
+ startOffset = ( (Integer) spEndingOffsets.get( i - 1 ) ).intValue();
+
+ // Create and write a new MSODRAWING record
+ DrawingRecord drawing = new DrawingRecord();
+ byte[] drawingData = new byte[endOffset - startOffset + 1];
+ System.arraycopy( buffer, startOffset, drawingData, 0, drawingData.length );
+ drawing.setData( drawingData );
+ int temp = drawing.serialize( pos, data );
+ pos += temp;
+
+ // Write the matching OBJ record
+ Record obj = (Record) shapeToObj.get( shapes.get( i ) );
+ temp = obj.serialize( pos, data );
+ pos += temp;
+
+ }
+
+ int bytesWritten = pos - offset;
+ if ( bytesWritten != getRecordSize() )
+ throw new RecordFormatException( bytesWritten + " bytes written but getRecordSize() reports " + getRecordSize() );
+ return bytesWritten;
+ }
+
+ /**
+ * How many bytes do the raw escher records contain.
+ * @param records List of escher records
+ * @return the number of bytes
+ */
+ private int getEscherRecordSize( List records )
+ {
+ int size = 0;
+ for ( Iterator iterator = records.iterator(); iterator.hasNext(); )
+ size += ( (EscherRecord) iterator.next() ).getRecordSize();
+ return size;
+ }
+
+ /**
+ * The number of bytes required to serialize this record.
+ */
+ public int getRecordSize()
+ {
+ convertUserModelToRecords();
+ List records = getEscherRecords();
+ int rawEscherSize = getEscherRecordSize( records );
+ int drawingRecordSize = rawEscherSize + ( shapeToObj.size() ) * 4;
+ int objRecordSize = 0;
+ for ( Iterator iterator = shapeToObj.values().iterator(); iterator.hasNext(); )
+ {
+ Record r = (Record) iterator.next();
+ objRecordSize += r.getRecordSize();
+ }
+ return drawingRecordSize + objRecordSize;
+ }
+
+ /**
+ * Associates an escher record to an OBJ record or a TXO record.
+ */
+ public Object assoicateShapeToObjRecord( EscherRecord r, Record objRecord )
+ {
+ return shapeToObj.put( r, objRecord );
+ }
+
+ public HSSFPatriarch getPatriarch()
+ {
+ return patriarch;
+ }
+
+ public void setPatriarch( HSSFPatriarch patriarch )
+ {
+ this.patriarch = patriarch;
+ }
+
+ public void clear()
+ {
+ clearEscherRecords();
+ shapeToObj.clear();
+// lastShapeId = 1024;
+ }
+
+ protected String getRecordName()
+ {
+ return "ESCHERAGGREGATE";
+ }
+
+ // =============== Private methods ========================
+
+ private static boolean isObjectRecord( List records, int loc )
+ {
+ return sid( records, loc ) == ObjRecord.sid || sid( records, loc ) == TextObjectRecord.sid;
+ }
+
+ private void convertUserModelToRecords()
+ {
+ if ( patriarch != null )
+ {
+ shapeToObj.clear();
+ clearEscherRecords();
+ if ( patriarch.getChildren().size() != 0 )
+ {
+ convertPatriarch( patriarch );
+ EscherContainerRecord dgContainer = (EscherContainerRecord) getEscherRecord( 0 );
+ EscherContainerRecord spgrContainer = null;
+ for ( int i = 0; i < dgContainer.getChildRecords().size(); i++ )
+ if ( dgContainer.getChild( i ).getRecordId() == EscherContainerRecord.SPGR_CONTAINER )
+ spgrContainer = (EscherContainerRecord) dgContainer.getChild( i );
+ convertShapes( patriarch, spgrContainer, shapeToObj );
+
+ patriarch = null;
+ }
+ }
+ }
+
+ private void convertShapes( HSSFShapeContainer parent, EscherContainerRecord escherParent, Map shapeToObj )
+ {
+ if ( escherParent == null ) throw new IllegalArgumentException( "Parent record required" );
+
+ List shapes = parent.getChildren();
+ for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); )
+ {
+ HSSFShape shape = (HSSFShape) iterator.next();
+ if ( shape instanceof HSSFShapeGroup )
+ {
+ convertGroup( (HSSFShapeGroup) shape, escherParent, shapeToObj );
+ }
+ else
+ {
+ AbstractShape shapeModel = AbstractShape.createShape(
+ shape,
+ drawingManager.allocateShapeId(drawingGroupId) );
+ shapeToObj.put( findClientData( shapeModel.getSpContainer() ), shapeModel.getObjRecord() );
+ if ( shapeModel instanceof TextboxShape )
+ {
+ EscherRecord escherTextbox = ( (TextboxShape) shapeModel ).getEscherTextbox();
+ shapeToObj.put( escherTextbox, ( (TextboxShape) shapeModel ).getTextObjectRecord() );
+ // escherParent.addChildRecord(escherTextbox);
+ }
+ escherParent.addChildRecord( shapeModel.getSpContainer() );
+ }
+ }
+ }
+
+ private void convertGroup( HSSFShapeGroup shape, EscherContainerRecord escherParent, Map shapeToObj )
+ {
+ EscherContainerRecord spgrContainer = new EscherContainerRecord();
+ EscherContainerRecord spContainer = new EscherContainerRecord();
+ EscherSpgrRecord spgr = new EscherSpgrRecord();
+ EscherSpRecord sp = new EscherSpRecord();
+ EscherOptRecord opt = new EscherOptRecord();
+ EscherRecord anchor;
+ EscherClientDataRecord clientData = new EscherClientDataRecord();
+
+ spgrContainer.setRecordId( EscherContainerRecord.SPGR_CONTAINER );
+ spgrContainer.setOptions( (short) 0x000F );
+ spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer.setOptions( (short) 0x000F );
+ spgr.setRecordId( EscherSpgrRecord.RECORD_ID );
+ spgr.setOptions( (short) 0x0001 );
+ spgr.setRectX1( shape.getX1() );
+ spgr.setRectY1( shape.getY1() );
+ spgr.setRectX2( shape.getX2() );
+ spgr.setRectY2( shape.getY2() );
+ sp.setRecordId( EscherSpRecord.RECORD_ID );
+ sp.setOptions( (short) 0x0002 );
+ int shapeId = drawingManager.allocateShapeId(drawingGroupId);
+ sp.setShapeId( shapeId );
+ if (shape.getAnchor() instanceof HSSFClientAnchor)
+ sp.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR );
+ else
+ sp.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_CHILD );
+ opt.setRecordId( EscherOptRecord.RECORD_ID );
+ opt.setOptions( (short) 0x0023 );
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x00040004 ) );
+ opt.addEscherProperty( new EscherBoolProperty( EscherProperties.GROUPSHAPE__PRINT, 0x00080000 ) );
+
+ anchor = ConvertAnchor.createAnchor( shape.getAnchor() );
+// clientAnchor.setCol1( ( (HSSFClientAnchor) shape.getAnchor() ).getCol1() );
+// clientAnchor.setRow1( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow1() );
+// clientAnchor.setDx1( (short) shape.getAnchor().getDx1() );
+// clientAnchor.setDy1( (short) shape.getAnchor().getDy1() );
+// clientAnchor.setCol2( ( (HSSFClientAnchor) shape.getAnchor() ).getCol2() );
+// clientAnchor.setRow2( (short) ( (HSSFClientAnchor) shape.getAnchor() ).getRow2() );
+// clientAnchor.setDx2( (short) shape.getAnchor().getDx2() );
+// clientAnchor.setDy2( (short) shape.getAnchor().getDy2() );
+ clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
+ clientData.setOptions( (short) 0x0000 );
+
+ spgrContainer.addChildRecord( spContainer );
+ spContainer.addChildRecord( spgr );
+ spContainer.addChildRecord( sp );
+ spContainer.addChildRecord( opt );
+ spContainer.addChildRecord( anchor );
+ spContainer.addChildRecord( clientData );
+
+ ObjRecord obj = new ObjRecord();
+ CommonObjectDataSubRecord cmo = new CommonObjectDataSubRecord();
+ cmo.setObjectType( CommonObjectDataSubRecord.OBJECT_TYPE_GROUP );
+ cmo.setObjectId( (short) ( shapeId ) );
+ cmo.setLocked( true );
+ cmo.setPrintable( true );
+ cmo.setAutofill( true );
+ cmo.setAutoline( true );
+ GroupMarkerSubRecord gmo = new GroupMarkerSubRecord();
+ EndSubRecord end = new EndSubRecord();
+ obj.addSubRecord( cmo );
+ obj.addSubRecord( gmo );
+ obj.addSubRecord( end );
+ shapeToObj.put( clientData, obj );
+
+ escherParent.addChildRecord( spgrContainer );
+
+ convertShapes( shape, spgrContainer, shapeToObj );
+
+ }
+
+ private EscherRecord findClientData( EscherContainerRecord spContainer )
+ {
+ for ( Iterator iterator = spContainer.getChildRecords().iterator(); iterator.hasNext(); )
+ {
+ EscherRecord r = (EscherRecord) iterator.next();
+ if ( r.getRecordId() == EscherClientDataRecord.RECORD_ID )
+ return r;
+ }
+ throw new IllegalArgumentException( "Can not find client data record" );
+ }
+
+ private void convertPatriarch( HSSFPatriarch patriarch )
+ {
+ EscherContainerRecord dgContainer = new EscherContainerRecord();
+ EscherDgRecord dg;
+ EscherContainerRecord spgrContainer = new EscherContainerRecord();
+ EscherContainerRecord spContainer1 = new EscherContainerRecord();
+ EscherSpgrRecord spgr = new EscherSpgrRecord();
+ EscherSpRecord sp1 = new EscherSpRecord();
+
+ dgContainer.setRecordId( EscherContainerRecord.DG_CONTAINER );
+ dgContainer.setOptions( (short) 0x000F );
+ dg = drawingManager.createDgRecord();
+ drawingGroupId = dg.getDrawingGroupId();
+// dg.setOptions( (short) ( drawingId << 4 ) );
+// dg.setNumShapes( getNumberOfShapes( patriarch ) );
+// dg.setLastMSOSPID( 0 ); // populated after all shape id's are assigned.
+ spgrContainer.setRecordId( EscherContainerRecord.SPGR_CONTAINER );
+ spgrContainer.setOptions( (short) 0x000F );
+ spContainer1.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer1.setOptions( (short) 0x000F );
+ spgr.setRecordId( EscherSpgrRecord.RECORD_ID );
+ spgr.setOptions( (short) 0x0001 ); // don't know what the 1 is for.
+ spgr.setRectX1( patriarch.getX1() );
+ spgr.setRectY1( patriarch.getY1() );
+ spgr.setRectX2( patriarch.getX2() );
+ spgr.setRectY2( patriarch.getY2() );
+ sp1.setRecordId( EscherSpRecord.RECORD_ID );
+ sp1.setOptions( (short) 0x0002 );
+ sp1.setShapeId( drawingManager.allocateShapeId(dg.getDrawingGroupId()) );
+ sp1.setFlags( EscherSpRecord.FLAG_GROUP | EscherSpRecord.FLAG_PATRIARCH );
+
+ dgContainer.addChildRecord( dg );
+ dgContainer.addChildRecord( spgrContainer );
+ spgrContainer.addChildRecord( spContainer1 );
+ spContainer1.addChildRecord( spgr );
+ spContainer1.addChildRecord( sp1 );
+
+ addEscherRecord( dgContainer );
+ }
+
+ /** Retrieve the number of shapes (including the patriarch). */
+// private int getNumberOfShapes( HSSFPatriarch patriarch )
+// {
+// return patriarch.countOfAllChildren();
+// }
+
+ private static short sid( List records, int loc )
+ {
+ return ( (Record) records.get( loc ) ).getSid();
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/GroupMarkerSubRecord.java b/src/java/org/apache/poi/hssf/record/GroupMarkerSubRecord.java
new file mode 100644
index 0000000000..56209aefd2
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/GroupMarkerSubRecord.java
@@ -0,0 +1,176 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * The group marker record is used as a position holder for groups.
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class GroupMarkerSubRecord
+ extends SubRecord
+{
+ public final static short sid = 0x06;
+
+ private byte[] reserved = new byte[0]; // would really love to know what goes in here.
+
+ public GroupMarkerSubRecord()
+ {
+
+ }
+
+ /**
+ * Constructs a group marker record and sets its fields appropriately.
+ *
+ * @param id id must be 0x00 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public GroupMarkerSubRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a group marker record and sets its fields appropriately.
+ *
+ * @param id id must be 0x00 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public GroupMarkerSubRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a Group Marker record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+// int pos = 0;
+ reserved = new byte[size];
+ System.arraycopy(data, offset, reserved, 0, size);
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ String nl = System.getProperty("line.separator");
+ buffer.append("[ftGmo]" + nl);
+ buffer.append(" reserved = ").append(HexDump.toHex(reserved)).append(nl);
+ buffer.append("[/ftGmo]" + nl);
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+ System.arraycopy(reserved, 0, data, offset + 4, getRecordSize() - 4);
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ return 4 + reserved.length;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public Object clone() {
+ GroupMarkerSubRecord rec = new GroupMarkerSubRecord();
+ rec.reserved = new byte[reserved.length];
+ for ( int i = 0; i < reserved.length; i++ )
+ rec.reserved[i] = reserved[i];
+ return rec;
+ }
+
+
+
+} // END OF CLASS
+
+
diff --git a/src/java/org/apache/poi/hssf/record/HorizontalPageBreakRecord.java b/src/java/org/apache/poi/hssf/record/HorizontalPageBreakRecord.java
new file mode 100644
index 0000000000..b7d1eb5121
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/HorizontalPageBreakRecord.java
@@ -0,0 +1,107 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.hssf.record;
+
+/**
+ * HorizontalPageBreak record that stores page breaks at rows
+ * <p>
+ * This class is just used so that SID compares work properly in the RecordFactory
+ * @see PageBreakRecord
+ * @author Danny Mui (dmui at apache dot org)
+ */
+public class HorizontalPageBreakRecord extends PageBreakRecord {
+
+ public static final short sid = PageBreakRecord.HORIZONTAL_SID;
+
+ /**
+ *
+ */
+ public HorizontalPageBreakRecord() {
+ super();
+ }
+
+ /**
+ * @param sid
+ */
+ public HorizontalPageBreakRecord(short sid) {
+ super(sid);
+ }
+
+ /**
+ * @param id
+ * @param size
+ * @param data
+ */
+ public HorizontalPageBreakRecord(short id, short size, byte[] data) {
+ super(id, size, data);
+ }
+
+ /**
+ * @param id
+ * @param size
+ * @param data
+ * @param offset
+ */
+ public HorizontalPageBreakRecord(short id, short size, byte[] data, int offset) {
+ super(id, size, data, offset);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.poi.hssf.record.Record#getSid()
+ */
+ public short getSid() {
+ return sid;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/ObjRecord.java b/src/java/org/apache/poi/hssf/record/ObjRecord.java
new file mode 100644
index 0000000000..5939b487ab
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/ObjRecord.java
@@ -0,0 +1,227 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * The obj record is used to hold various graphic objects and controls.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class ObjRecord
+ extends Record
+{
+ public final static short sid = 0x5D;
+ private List subrecords;
+
+ //00000000 15 00 12 00 01 00 01 00 11 60 00 00 00 00 00 0D .........`......
+ //00000010 26 01 00 00 00 00 00 00 00 00 &.........
+
+
+ public ObjRecord()
+ {
+ subrecords = new ArrayList(2);
+ }
+
+ /**
+ * Constructs a OBJ record and sets its fields appropriately.
+ *
+ * @param id id must be 0x5D or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public ObjRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+ }
+
+ /**
+ * Constructs a obj record and sets its fields appropriately.
+ *
+ * @param id id must be 0x5D or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+ public ObjRecord(short id, short size, byte[] data, int offset)
+ {
+ super(id, size, data, offset);
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not an OBJ record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+ subrecords = new ArrayList();
+ int pos = offset;
+ while (pos - offset < size)
+ {
+ short subRecordSid = LittleEndian.getShort(data, pos);
+ short subRecordSize = LittleEndian.getShort(data, pos + 2);
+ Record subRecord = SubRecord.createSubRecord(subRecordSid, subRecordSize, data, pos + 4);
+ subrecords.add(subRecord);
+ pos += 4 + subRecordSize;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[OBJ]\n");
+ for ( Iterator iterator = subrecords.iterator(); iterator.hasNext(); )
+ {
+ Record record = (Record) iterator.next();
+ buffer.append("SUBRECORD: " + record.toString());
+ }
+ buffer.append("[/OBJ]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+ pos = offset + 4;
+ for ( Iterator iterator = subrecords.iterator(); iterator.hasNext(); )
+ {
+ Record record = (Record) iterator.next();
+ pos += record.serialize(pos, data);
+ }
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (excluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ int size = 0;
+ for ( Iterator iterator = subrecords.iterator(); iterator.hasNext(); )
+ {
+ Record record = (Record) iterator.next();
+ size += record.getRecordSize();
+ }
+ return 4 + size;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public List getSubRecords()
+ {
+ return subrecords;
+ }
+
+ public void clearSubRecords()
+ {
+ subrecords.clear();
+ }
+
+ public void addSubRecord(int index, Object element)
+ {
+ subrecords.add( index, element );
+ }
+
+ public boolean addSubRecord(Object o)
+ {
+ return subrecords.add( o );
+ }
+
+ public Object clone()
+ {
+ ObjRecord rec = new ObjRecord();
+ rec.subrecords = new ArrayList();
+
+ for ( Iterator iterator = subrecords.iterator(); iterator.hasNext(); )
+ subrecords.add(( (Record) iterator.next() ).clone());
+
+ return rec;
+ }
+
+} // END OF CLASS
+
+
+
+
diff --git a/src/java/org/apache/poi/hssf/record/PageBreakRecord.java b/src/java/org/apache/poi/hssf/record/PageBreakRecord.java
new file mode 100644
index 0000000000..6420e5e400
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/PageBreakRecord.java
@@ -0,0 +1,304 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.hssf.record;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Record that contains the functionality page breaks (horizontal and vertical)
+ * <p>
+ * The other two classes just specifically set the SIDS for record creation
+ * @see HorizontalPageBreakRecord
+ * @see VerticalPageBreakREcord
+ *
+ * REFERENCE: Microsoft Excel SDK page 322 and 420
+ * @author Danny Mui (dmui at apache dot org)
+ */
+public class PageBreakRecord extends Record {
+ public static final short HORIZONTAL_SID = (short)0x1B;
+ public static final short VERTICAL_SID = (short)0x1A;
+ public short sid;
+ private short numBreaks;
+ private List breaks;
+ private Map BreakMap;
+
+ /**
+ * Since both records store 2byte integers (short), no point in
+ * differentiating it in the records.
+ * <p>
+ * The subs (rows or columns, don't seem to be able to set but excel sets
+ * them automatically)
+ */
+ public class Break
+ {
+
+ public short main;
+ public short subFrom;
+ public short subTo;
+
+ public Break(short main, short subFrom, short subTo)
+ {
+ this.main = main;
+ this.subFrom = subFrom;
+ this.subTo = subTo;
+ }
+ }
+
+ public PageBreakRecord()
+ {
+
+ }
+
+ /**
+ *
+ * @param sid
+ */
+ public PageBreakRecord(short sid) {
+ super();
+ this.sid = sid;
+ }
+
+ public PageBreakRecord(short id, short size, byte data[])
+ {
+ super(id, size, data);
+ this.sid = id;
+ }
+
+ public PageBreakRecord(short id, short size, byte data[], int offset)
+ {
+ super(id, size, data, offset);
+ this.sid = id;
+ }
+
+ protected void fillFields(byte data[], short size, int offset)
+ {
+ short loadedBreaks = LittleEndian.getShort(data, 0 + offset);
+ setNumBreaks(loadedBreaks);
+ int pos = 2;
+ for(int k = 0; k < loadedBreaks; k++)
+ {
+ addBreak((short)(LittleEndian.getShort(data, pos + offset) - 1), LittleEndian.getShort(data, pos + 2 + offset), LittleEndian.getShort(data, pos + 4 + offset));
+ pos += 6;
+ }
+
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public int serialize(int offset, byte data[])
+ {
+ int recordsize = getRecordSize();
+ int pos = 6;
+ LittleEndian.putShort(data, offset + 0, getSid());
+ LittleEndian.putShort(data, offset + 2, (short)(recordsize - 4));
+ LittleEndian.putShort(data, offset + 4, getNumBreaks());
+ for(Iterator iterator = getBreaksIterator(); iterator.hasNext();)
+ {
+ Break Break = (Break)iterator.next();
+ LittleEndian.putShort(data, offset + pos, (short)(Break.main + 1));
+ pos += 2;
+ LittleEndian.putShort(data, offset + pos, Break.subFrom);
+ pos += 2;
+ LittleEndian.putShort(data, offset + pos, Break.subTo);
+ pos += 2;
+ }
+
+ return recordsize;
+ }
+
+ protected void validateSid(short id)
+ {
+ if(id != HORIZONTAL_SID && id != VERTICAL_SID)
+ throw new RecordFormatException("NOT A HorizontalPageBreak or VerticalPageBreak RECORD!! " + id);
+ else
+ return;
+ }
+
+ public short getNumBreaks()
+ {
+ return breaks != null ? (short)breaks.size() : numBreaks;
+ }
+
+ public void setNumBreaks(short numBreaks)
+ {
+ this.numBreaks = numBreaks;
+ }
+
+ public Iterator getBreaksIterator()
+ {
+ if(breaks == null)
+ return Collections.EMPTY_LIST.iterator();
+ else
+ return breaks.iterator();
+ }
+
+ public String toString()
+ {
+ StringBuffer retval = new StringBuffer();
+
+ if (getSid() != HORIZONTAL_SID && getSid()!= VERTICAL_SID)
+ return "[INVALIDPAGEBREAK]\n .sid ="+getSid()+"[INVALIDPAGEBREAK]";
+
+ String label;
+ String mainLabel;
+ String subLabel;
+
+ if (getSid() == HORIZONTAL_SID) {
+ label = "HORIZONTALPAGEBREAK";
+ mainLabel = "row";
+ subLabel = "col";
+ } else {
+ label = "VERTICALPAGEBREAK";
+ mainLabel = "column";
+ subLabel = "row";
+ }
+
+ retval.append("["+label+"]").append("\n");
+ retval.append(" .sid =").append(getSid()).append("\n");
+ retval.append(" .numbreaks =").append(getNumBreaks()).append("\n");
+ Iterator iterator = getBreaksIterator();
+ for(int k = 0; k < getNumBreaks(); k++)
+ {
+ Break region = (Break)iterator.next();
+
+ retval.append(" .").append(mainLabel).append(" (zero-based) =").append(region.main).append("\n");
+ retval.append(" .").append(subLabel).append("From =").append(region.subFrom).append("\n");
+ retval.append(" .").append(subLabel).append("To =").append(region.subTo).append("\n");
+ }
+
+ retval.append("["+label+"]").append("\n");
+ return retval.toString();
+ }
+
+ /**
+ * Adds the page break at the specified parameters
+ * @param main Depending on sid, will determine row or column to put page break (zero-based)
+ * @param subFrom No user-interface to set (defaults to minumum, 0)
+ * @param subTo No user-interface to set
+ */
+ public void addBreak(short main, short subFrom, short subTo)
+ {
+ if(breaks == null)
+ {
+ breaks = new ArrayList(getNumBreaks() + 10);
+ BreakMap = new HashMap();
+ }
+ Integer key = new Integer(main);
+ Break region = (Break)BreakMap.get(key);
+ if(region != null)
+ {
+ region.main = main;
+ region.subFrom = subFrom;
+ region.subTo = subTo;
+ } else
+ {
+ region = new Break(main, subFrom, subTo);
+ breaks.add(region);
+ }
+ BreakMap.put(key, region);
+ }
+
+ /**
+ * Removes the break indicated by the parameter
+ * @param main (zero-based)
+ */
+ public void removeBreak(short main)
+ {
+ Integer rowKey = new Integer(main);
+ Break region = (Break)BreakMap.get(rowKey);
+ breaks.remove(region);
+ BreakMap.remove(rowKey);
+ }
+
+ public int getRecordSize()
+ {
+ return 6 + getNumBreaks() * 6;
+ }
+
+ /**
+ * Retrieves the region at the row/column indicated
+ * @param main
+ * @return
+ */
+ public Break getBreak(short main)
+ {
+ Integer rowKey = new Integer(main);
+ return (Break)BreakMap.get(rowKey);
+ }
+
+ /* Clones the page break record
+ * @see java.lang.Object#clone()
+ */
+ public Object clone() {
+ PageBreakRecord record = new PageBreakRecord(getSid());
+ Iterator iterator = getBreaksIterator();
+ while (iterator.hasNext()) {
+ Break original = (Break)iterator.next();
+ record.addBreak(original.main, original.subFrom, original.subTo);
+ }
+ return record;
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java
index f12f903b06..f2b5b28f00 100644
--- a/src/java/org/apache/poi/hssf/record/RecordFactory.java
+++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java
@@ -112,7 +112,10 @@ public class RecordFactory
FormulaRecord.class, BoolErrRecord.class, ExternSheetRecord.class,
NameRecord.class, LeftMarginRecord.class, RightMarginRecord.class,
TopMarginRecord.class, BottomMarginRecord.class,
- PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class
+ PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
+ DrawingRecord.class, DrawingGroupRecord.class, DrawingSelectionRecord.class,
+ ObjRecord.class, TextObjectRecord.class,
+ HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class
};
} else {
records = new Class[]
@@ -143,7 +146,10 @@ public class RecordFactory
BoolErrRecord.class, ExternSheetRecord.class, NameRecord.class,
LeftMarginRecord.class, RightMarginRecord.class,
TopMarginRecord.class, BottomMarginRecord.class,
- PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class
+ PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
+ DrawingRecord.class, DrawingGroupRecord.class, DrawingSelectionRecord.class,
+ ObjRecord.class, TextObjectRecord.class,
+ HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class
};
}
diff --git a/src/java/org/apache/poi/hssf/record/SubRecord.java b/src/java/org/apache/poi/hssf/record/SubRecord.java
new file mode 100644
index 0000000000..6b27eb3e03
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/SubRecord.java
@@ -0,0 +1,98 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.poi.hssf.record;
+
+/**
+ * Subrecords are part of the OBJ class.
+ */
+abstract public class SubRecord
+ extends Record
+{
+ public SubRecord()
+ {
+ }
+
+ public SubRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public SubRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ public static Record createSubRecord( short subRecordSid, short size, byte[] data, int offset )
+ {
+ Record r = null;
+
+ switch ( subRecordSid )
+ {
+ case CommonObjectDataSubRecord.sid:
+ r = new CommonObjectDataSubRecord( subRecordSid, size, data, offset );
+ break;
+ case GroupMarkerSubRecord.sid:
+ r = new GroupMarkerSubRecord( subRecordSid, size, data, offset );
+ break;
+ case EndSubRecord.sid:
+ r = new EndSubRecord( subRecordSid, size, data, offset );
+ break;
+ default:
+ r = new UnknownRecord( subRecordSid, size, data, offset );
+ }
+
+ return r;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java b/src/java/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java
new file mode 100644
index 0000000000..2ffe2c2ce2
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestCommonObjectDataSubRecord.java
@@ -0,0 +1,127 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the serialization and deserialization of the CommonObjectDataSubRecord
+ * class works correctly. Test data taken directly from a real
+ * Excel file.
+ *
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestCommonObjectDataSubRecord
+ extends TestCase
+{
+ byte[] data = new byte[] {
+ (byte)0x12,(byte)0x00,(byte)0x01,(byte)0x00,
+ (byte)0x01,(byte)0x00,(byte)0x11,(byte)0x60,
+ (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
+ (byte)0x00,(byte)0x0D,(byte)0x26,(byte)0x01,
+ (byte)0x00,(byte)0x00,
+ };
+
+ public TestCommonObjectDataSubRecord(String name)
+ {
+ super(name);
+ }
+
+ public void testLoad()
+ throws Exception
+ {
+ CommonObjectDataSubRecord record = new CommonObjectDataSubRecord((short)0x15, (short)data.length, data);
+
+
+ assertEquals( CommonObjectDataSubRecord.OBJECT_TYPE_LIST_BOX, record.getObjectType());
+ assertEquals( (short)1, record.getObjectId());
+ assertEquals( (short)1, record.getOption());
+ assertEquals( true , record.isLocked() );
+ assertEquals( false, record.isPrintable() );
+ assertEquals( false, record.isAutofill() );
+ assertEquals( false, record.isAutoline() );
+ assertEquals( (int)24593, record.getReserved1());
+ assertEquals( (int)218103808, record.getReserved2());
+ assertEquals( (int)294, record.getReserved3());
+ assertEquals( 22 , record.getRecordSize() );
+
+ record.validateSid((short)0x15);
+ }
+
+ public void testStore()
+ {
+ CommonObjectDataSubRecord record = new CommonObjectDataSubRecord();
+
+ record.setObjectType( CommonObjectDataSubRecord.OBJECT_TYPE_LIST_BOX );
+ record.setObjectId( (short) 1);
+ record.setOption( (short) 1);
+ record.setLocked( true );
+ record.setPrintable( false );
+ record.setAutofill( false );
+ record.setAutoline( false );
+ record.setReserved1( (int) 24593);
+ record.setReserved2( (int) 218103808);
+ record.setReserved3( (int) 294);
+
+ byte [] recordBytes = record.serialize();
+ assertEquals(recordBytes.length - 4, data.length);
+ for (int i = 0; i < data.length; i++)
+ assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestDrawingGroupRecord.java b/src/java/org/apache/poi/hssf/record/TestDrawingGroupRecord.java
new file mode 100644
index 0000000000..4c42eca886
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestDrawingGroupRecord.java
@@ -0,0 +1,36 @@
+package org.apache.poi.hssf.record;
+
+import junit.framework.TestCase;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherSpRecord;
+import org.apache.poi.util.HexDump;
+
+public class TestDrawingGroupRecord extends TestCase
+{
+ public void testGetRecordSize()
+ throws Exception
+ {
+ DrawingGroupRecord r = new DrawingGroupRecord();
+ assertEquals(4, r.getRecordSize());
+
+ EscherSpRecord sp = new EscherSpRecord();
+ sp.setRecordId(EscherSpRecord.RECORD_ID);
+ sp.setOptions((short) 0x1111);
+ sp.setFlags(-1);
+ sp.setShapeId(-1);
+ EscherContainerRecord dggContainer = new EscherContainerRecord();
+ dggContainer.setOptions((short) 0x000F);
+ dggContainer.setRecordId((short) 0xF000);
+ dggContainer.addChildRecord(sp);
+
+ r.addEscherRecord(dggContainer);
+ assertEquals(28, r.getRecordSize());
+
+ byte[] data = new byte[28];
+ int size = r.serialize(0, data);
+ assertEquals("[EB, 00, 18, 00, 0F, 00, 00, F0, 10, 00, 00, 00, 11, 11, 0A, F0, 08, 00, 00, 00, FF, FF, FF, FF, FF, FF, FF, FF, ]", HexDump.toHex(data));
+ assertEquals(28, size);
+
+ assertEquals(24, dggContainer.getRecordSize());
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestEndSubRecord.java b/src/java/org/apache/poi/hssf/record/TestEndSubRecord.java
new file mode 100644
index 0000000000..d338601948
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestEndSubRecord.java
@@ -0,0 +1,102 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the serialization and deserialization of the EndSubRecord
+ * class works correctly. Test data taken directly from a real
+ * Excel file.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestEndSubRecord
+ extends TestCase
+{
+ byte[] data = new byte[] {
+
+ };
+
+ public TestEndSubRecord(String name)
+ {
+ super(name);
+ }
+
+ public void testLoad()
+ throws Exception
+ {
+ EndSubRecord record = new EndSubRecord((short)0x00, (short)data.length, data);
+
+
+
+ assertEquals( 4, record.getRecordSize() );
+
+ record.validateSid((short)0x00);
+ }
+
+ public void testStore()
+ {
+ EndSubRecord record = new EndSubRecord();
+
+ byte [] recordBytes = record.serialize();
+ assertEquals(recordBytes.length - 4, data.length);
+ for (int i = 0; i < data.length; i++)
+ assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestEscherAggregate.java b/src/java/org/apache/poi/hssf/record/TestEscherAggregate.java
new file mode 100644
index 0000000000..f7acf2f0d5
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestEscherAggregate.java
@@ -0,0 +1,124 @@
+package org.apache.poi.hssf.record;
+
+import junit.framework.TestCase;
+import org.apache.poi.util.HexRead;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherSpRecord;
+import org.apache.poi.ddf.EscherClientDataRecord;
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.hssf.model.DrawingManager;
+import org.apache.poi.hssf.model.Sheet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests the EscherAggregate class.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestEscherAggregate extends TestCase
+{
+ /**
+ * Tests that the create aggregate method correctly rejoins escher records together.
+ *
+ * @throws Exception
+ */
+ public void testCreateAggregate() throws Exception
+ {
+ String msoDrawingRecord1 =
+ "0F 00 02 F0 20 01 00 00 10 00 08 F0 08 00 00 00 \n" +
+ "03 00 00 00 02 04 00 00 0F 00 03 F0 08 01 00 00 \n" +
+ "0F 00 04 F0 28 00 00 00 01 00 09 F0 10 00 00 00 \n" +
+ "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" +
+ "02 00 0A F0 08 00 00 00 00 04 00 00 05 00 00 00 \n" +
+ "0F 00 04 F0 64 00 00 00 42 01 0A F0 08 00 00 00 \n" +
+ "01 04 00 00 00 0A 00 00 73 00 0B F0 2A 00 00 00 \n" +
+ "BF 00 08 00 08 00 44 01 04 00 00 00 7F 01 00 00 \n" +
+ "01 00 BF 01 00 00 11 00 C0 01 40 00 00 08 FF 01 \n" +
+ "10 00 10 00 BF 03 00 00 08 00 00 00 10 F0 12 00 \n" +
+ "00 00 00 00 01 00 54 00 05 00 45 00 01 00 88 03 \n" +
+ "05 00 94 00 00 00 11 F0 00 00 00 00";
+
+ String msoDrawingRecord2 =
+ "0F 00 04 F0 64 00 00 00 42 01 0A F0 08 00 00 00 " +
+ "02 04 00 00 80 0A 00 00 73 00 0B F0 2A 00 00 00 " +
+ "BF 00 08 00 08 00 44 01 04 00 00 00 7F 01 00 00 " +
+ "01 00 BF 01 00 00 11 00 C0 01 40 00 00 08 FF 01 " +
+ "10 00 10 00 BF 03 00 00 08 00 00 00 10 F0 12 00 " +
+ "00 00 00 00 01 00 8D 03 05 00 E4 00 03 00 4D 03 " +
+ "0B 00 0C 00 00 00 11 F0 00 00 00 00";
+
+ DrawingRecord d1 = new DrawingRecord();
+ d1.setData( HexRead.readFromString( msoDrawingRecord1 ) );
+
+ ObjRecord r1 = new ObjRecord();
+
+ DrawingRecord d2 = new DrawingRecord();
+ d2.setData( HexRead.readFromString( msoDrawingRecord2 ) );
+
+ ObjRecord r2 = new ObjRecord();
+
+ List records = new ArrayList();
+ records.add( d1 );
+ records.add( r1 );
+ records.add( d2 );
+ records.add( r2 );
+
+ DrawingManager drawingManager = new DrawingManager(new EscherDggRecord() );
+ EscherAggregate aggregate = EscherAggregate.createAggregate( records, 0, drawingManager );
+
+ assertEquals( 1, aggregate.getEscherRecords().size() );
+ assertEquals( (short) 0xF002, aggregate.getEscherRecord( 0 ).getRecordId() );
+ assertEquals( 2, aggregate.getEscherRecord( 0 ).getChildRecords().size() );
+
+// System.out.println( "aggregate = " + aggregate );
+ }
+
+ public void testSerialize() throws Exception
+ {
+
+ EscherContainerRecord container1 = new EscherContainerRecord();
+ EscherContainerRecord spContainer1 = new EscherContainerRecord();
+ EscherContainerRecord spContainer2 = new EscherContainerRecord();
+ EscherContainerRecord spContainer3 = new EscherContainerRecord();
+ EscherSpRecord sp1 = new EscherSpRecord();
+ EscherSpRecord sp2 = new EscherSpRecord();
+ EscherSpRecord sp3 = new EscherSpRecord();
+ EscherClientDataRecord d2 = new EscherClientDataRecord();
+ EscherClientDataRecord d3 = new EscherClientDataRecord();
+
+ container1.setOptions( (short) 0x000F );
+ spContainer1.setOptions( (short) 0x000F );
+ spContainer1.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer2.setOptions( (short) 0x000F );
+ spContainer2.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ spContainer3.setOptions( (short) 0x000F );
+ spContainer3.setRecordId( EscherContainerRecord.SP_CONTAINER );
+ d2.setRecordId( EscherClientDataRecord.RECORD_ID );
+ d2.setRemainingData( new byte[0] );
+ d3.setRecordId( EscherClientDataRecord.RECORD_ID );
+ d3.setRemainingData( new byte[0] );
+ container1.addChildRecord( spContainer1 );
+ container1.addChildRecord( spContainer2 );
+ container1.addChildRecord( spContainer3 );
+ spContainer1.addChildRecord( sp1 );
+ spContainer2.addChildRecord( sp2 );
+ spContainer3.addChildRecord( sp3 );
+ spContainer2.addChildRecord( d2 );
+ spContainer3.addChildRecord( d3 );
+
+ EscherAggregate aggregate = new EscherAggregate(null);
+ aggregate.addEscherRecord( container1 );
+ aggregate.assoicateShapeToObjRecord( d2, new ObjRecord() );
+ aggregate.assoicateShapeToObjRecord( d3, new ObjRecord() );
+
+ byte[] data = new byte[112];
+ int bytesWritten = aggregate.serialize( 0, data );
+ assertEquals( 112, bytesWritten );
+ assertEquals( "[EC, 00, 40, 00, 0F, 00, 00, 00, 58, 00, 00, 00, 0F, 00, 04, F0, 10, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, EC, 00, 20, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, ]",
+ HexDump.toHex( data ) );
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java b/src/java/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java
new file mode 100644
index 0000000000..1e95653aef
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TestTextObjectBaseRecord.java
@@ -0,0 +1,137 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the serialization and deserialization of the TextObjectBaseRecord
+ * class works correctly. Test data taken directly from a real
+ * Excel file.
+ *
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestTextObjectBaseRecord
+ extends TestCase
+{
+ byte[] data = new byte[] {
+ 0x44, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
+ public TestTextObjectBaseRecord(String name)
+ {
+ super(name);
+ }
+
+ public void testLoad()
+ throws Exception
+ {
+ TextObjectBaseRecord record = new TextObjectBaseRecord((short)0x1B6, (short)data.length, data);
+
+
+// assertEquals( (short), record.getOptions());
+ assertEquals( false, record.isReserved1() );
+ assertEquals( TextObjectBaseRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment() );
+ assertEquals( TextObjectBaseRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment() );
+ assertEquals( 0, record.getReserved2() );
+ assertEquals( true, record.isTextLocked() );
+ assertEquals( 0, record.getReserved3() );
+ assertEquals( TextObjectBaseRecord.TEXT_ORIENTATION_ROT_RIGHT, record.getTextOrientation());
+ assertEquals( 0, record.getReserved4());
+ assertEquals( 0, record.getReserved5());
+ assertEquals( 0, record.getReserved6());
+ assertEquals( 2, record.getTextLength());
+ assertEquals( 2, record.getFormattingRunLength());
+ assertEquals( 0, record.getReserved7());
+
+
+ assertEquals( 22, record.getRecordSize() );
+
+ record.validateSid((short)0x1B6);
+ }
+
+ public void testStore()
+ {
+ TextObjectBaseRecord record = new TextObjectBaseRecord();
+
+
+
+// record.setOptions( (short) 0x0000);
+ record.setReserved1( false );
+ record.setHorizontalTextAlignment( TextObjectBaseRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED );
+ record.setVerticalTextAlignment( TextObjectBaseRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY );
+ record.setReserved2( (short)0 );
+ record.setTextLocked( true );
+ record.setReserved3( (short)0 );
+ record.setTextOrientation( TextObjectBaseRecord.TEXT_ORIENTATION_ROT_RIGHT );
+ record.setReserved4( (short)0 );
+ record.setReserved5( (short)0 );
+ record.setReserved6( (short)0 );
+ record.setTextLength( (short)2 );
+ record.setFormattingRunLength( (short)2 );
+ record.setReserved7( 0 );
+
+ byte [] recordBytes = record.serialize();
+ assertEquals(recordBytes.length - 4, data.length);
+ for (int i = 0; i < data.length; i++)
+ assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java b/src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java
new file mode 100644
index 0000000000..21d83a0c87
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TextObjectBaseRecord.java
@@ -0,0 +1,519 @@
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+
+package org.apache.poi.hssf.record;
+
+
+
+import org.apache.poi.util.*;
+
+/**
+ * The TXO record is used to define the properties of a text box. It is followed
+ by two continue records unless there is no actual text. The first continue record contains
+ the text data and the next continue record contains the formatting runs.
+ * NOTE: This source is automatically generated please do not modify this file. Either subclass or
+ * remove the record in src/records/definitions.
+
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TextObjectBaseRecord
+ extends Record
+{
+ public final static short sid = 0x1B6;
+ private short field_1_options;
+ private BitField reserved1 = new BitField(0x1);
+ private BitField HorizontalTextAlignment = new BitField(0x000E);
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
+ public final static short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
+ private BitField VerticalTextAlignment = new BitField(0x0070);
+ public final static short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
+ public final static short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
+ public final static short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
+ public final static short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
+ private BitField reserved2 = new BitField(0x0180);
+ private BitField textLocked = new BitField(0x200);
+ private BitField reserved3 = new BitField(0xFC00);
+ private short field_2_textOrientation;
+ public final static short TEXT_ORIENTATION_NONE = 0;
+ public final static short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
+ public final static short TEXT_ORIENTATION_ROT_RIGHT = 2;
+ public final static short TEXT_ORIENTATION_ROT_LEFT = 3;
+ private short field_3_reserved4;
+ private short field_4_reserved5;
+ private short field_5_reserved6;
+ private short field_6_textLength;
+ private short field_7_formattingRunLength;
+ private int field_8_reserved7;
+
+
+ public TextObjectBaseRecord()
+ {
+
+ }
+
+ /**
+ * Constructs a TextObjectBase record and sets its fields appropriately.
+ *
+ * @param id id must be 0x1B6 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public TextObjectBaseRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+
+ }
+
+ /**
+ * Constructs a TextObjectBase record and sets its fields appropriately.
+ *
+ * @param id id must be 0x1B6 or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public TextObjectBaseRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException("Not a TextObjectBase record");
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+
+ int pos = 0;
+ field_1_options = LittleEndian.getShort(data, pos + 0x0 + offset);
+ field_2_textOrientation = LittleEndian.getShort(data, pos + 0x2 + offset);
+ field_3_reserved4 = LittleEndian.getShort(data, pos + 0x4 + offset);
+ field_4_reserved5 = LittleEndian.getShort(data, pos + 0x6 + offset);
+ field_5_reserved6 = LittleEndian.getShort(data, pos + 0x8 + offset);
+ field_6_textLength = LittleEndian.getShort(data, pos + 0xa + offset);
+ field_7_formattingRunLength = LittleEndian.getShort(data, pos + 0xc + offset);
+ field_8_reserved7 = LittleEndian.getInt(data, pos + 0xe + offset);
+
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[TXO]\n");
+ buffer.append(" .options = ")
+ .append("0x").append(HexDump.toHex( getOptions ()))
+ .append(" (").append( getOptions() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved1 = ").append(isReserved1()).append('\n');
+ buffer.append(" .HorizontalTextAlignment = ").append(getHorizontalTextAlignment()).append('\n');
+ buffer.append(" .VerticalTextAlignment = ").append(getVerticalTextAlignment()).append('\n');
+ buffer.append(" .reserved2 = ").append(getReserved2()).append('\n');
+ buffer.append(" .textLocked = ").append(isTextLocked()).append('\n');
+ buffer.append(" .reserved3 = ").append(getReserved3()).append('\n');
+ buffer.append(" .textOrientation = ")
+ .append("0x").append(HexDump.toHex( getTextOrientation ()))
+ .append(" (").append( getTextOrientation() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved4 = ")
+ .append("0x").append(HexDump.toHex( getReserved4 ()))
+ .append(" (").append( getReserved4() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved5 = ")
+ .append("0x").append(HexDump.toHex( getReserved5 ()))
+ .append(" (").append( getReserved5() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved6 = ")
+ .append("0x").append(HexDump.toHex( getReserved6 ()))
+ .append(" (").append( getReserved6() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .textLength = ")
+ .append("0x").append(HexDump.toHex( getTextLength ()))
+ .append(" (").append( getTextLength() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .formattingRunLength = ")
+ .append("0x").append(HexDump.toHex( getFormattingRunLength ()))
+ .append(" (").append( getFormattingRunLength() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+ buffer.append(" .reserved7 = ")
+ .append("0x").append(HexDump.toHex( getReserved7 ()))
+ .append(" (").append( getReserved7() ).append(" )");
+ buffer.append(System.getProperty("line.separator"));
+
+ buffer.append("[/TXO]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+
+ LittleEndian.putShort(data, 4 + offset + pos, field_1_options);
+ LittleEndian.putShort(data, 6 + offset + pos, field_2_textOrientation);
+ LittleEndian.putShort(data, 8 + offset + pos, field_3_reserved4);
+ LittleEndian.putShort(data, 10 + offset + pos, field_4_reserved5);
+ LittleEndian.putShort(data, 12 + offset + pos, field_5_reserved6);
+ LittleEndian.putShort(data, 14 + offset + pos, field_6_textLength);
+ LittleEndian.putShort(data, 16 + offset + pos, field_7_formattingRunLength);
+ LittleEndian.putInt(data, 18 + offset + pos, field_8_reserved7);
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+ return 4 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 4;
+ }
+
+ public short getSid()
+ {
+ return this.sid;
+ }
+
+ public Object clone() {
+ TextObjectBaseRecord rec = new TextObjectBaseRecord();
+
+ rec.field_1_options = field_1_options;
+ rec.field_2_textOrientation = field_2_textOrientation;
+ rec.field_3_reserved4 = field_3_reserved4;
+ rec.field_4_reserved5 = field_4_reserved5;
+ rec.field_5_reserved6 = field_5_reserved6;
+ rec.field_6_textLength = field_6_textLength;
+ rec.field_7_formattingRunLength = field_7_formattingRunLength;
+ rec.field_8_reserved7 = field_8_reserved7;
+ return rec;
+ }
+
+
+
+
+ /**
+ * Get the options field for the TextObjectBase record.
+ */
+ public short getOptions()
+ {
+ return field_1_options;
+ }
+
+ /**
+ * Set the options field for the TextObjectBase record.
+ */
+ public void setOptions(short field_1_options)
+ {
+ this.field_1_options = field_1_options;
+ }
+
+ /**
+ * Get the text orientation field for the TextObjectBase record.
+ *
+ * @return One of
+ * TEXT_ORIENTATION_NONE
+ * TEXT_ORIENTATION_TOP_TO_BOTTOM
+ * TEXT_ORIENTATION_ROT_RIGHT
+ * TEXT_ORIENTATION_ROT_LEFT
+ */
+ public short getTextOrientation()
+ {
+ return field_2_textOrientation;
+ }
+
+ /**
+ * Set the text orientation field for the TextObjectBase record.
+ *
+ * @param field_2_textOrientation
+ * One of
+ * TEXT_ORIENTATION_NONE
+ * TEXT_ORIENTATION_TOP_TO_BOTTOM
+ * TEXT_ORIENTATION_ROT_RIGHT
+ * TEXT_ORIENTATION_ROT_LEFT
+ */
+ public void setTextOrientation(short field_2_textOrientation)
+ {
+ this.field_2_textOrientation = field_2_textOrientation;
+ }
+
+ /**
+ * Get the reserved4 field for the TextObjectBase record.
+ */
+ public short getReserved4()
+ {
+ return field_3_reserved4;
+ }
+
+ /**
+ * Set the reserved4 field for the TextObjectBase record.
+ */
+ public void setReserved4(short field_3_reserved4)
+ {
+ this.field_3_reserved4 = field_3_reserved4;
+ }
+
+ /**
+ * Get the reserved5 field for the TextObjectBase record.
+ */
+ public short getReserved5()
+ {
+ return field_4_reserved5;
+ }
+
+ /**
+ * Set the reserved5 field for the TextObjectBase record.
+ */
+ public void setReserved5(short field_4_reserved5)
+ {
+ this.field_4_reserved5 = field_4_reserved5;
+ }
+
+ /**
+ * Get the reserved6 field for the TextObjectBase record.
+ */
+ public short getReserved6()
+ {
+ return field_5_reserved6;
+ }
+
+ /**
+ * Set the reserved6 field for the TextObjectBase record.
+ */
+ public void setReserved6(short field_5_reserved6)
+ {
+ this.field_5_reserved6 = field_5_reserved6;
+ }
+
+ /**
+ * Get the text length field for the TextObjectBase record.
+ */
+ public short getTextLength()
+ {
+ return field_6_textLength;
+ }
+
+ /**
+ * Set the text length field for the TextObjectBase record.
+ */
+ public void setTextLength(short field_6_textLength)
+ {
+ this.field_6_textLength = field_6_textLength;
+ }
+
+ /**
+ * Get the formatting run length field for the TextObjectBase record.
+ */
+ public short getFormattingRunLength()
+ {
+ return field_7_formattingRunLength;
+ }
+
+ /**
+ * Set the formatting run length field for the TextObjectBase record.
+ */
+ public void setFormattingRunLength(short field_7_formattingRunLength)
+ {
+ this.field_7_formattingRunLength = field_7_formattingRunLength;
+ }
+
+ /**
+ * Get the reserved7 field for the TextObjectBase record.
+ */
+ public int getReserved7()
+ {
+ return field_8_reserved7;
+ }
+
+ /**
+ * Set the reserved7 field for the TextObjectBase record.
+ */
+ public void setReserved7(int field_8_reserved7)
+ {
+ this.field_8_reserved7 = field_8_reserved7;
+ }
+
+ /**
+ * Sets the reserved1 field value.
+ * reserved field
+ */
+ public void setReserved1(boolean value)
+ {
+ field_1_options = reserved1.setShortBoolean(field_1_options, value);
+ }
+
+ /**
+ * reserved field
+ * @return the reserved1 field value.
+ */
+ public boolean isReserved1()
+ {
+ return reserved1.isSet(field_1_options);
+ }
+
+ /**
+ * Sets the Horizontal text alignment field value.
+ *
+ */
+ public void setHorizontalTextAlignment(short value)
+ {
+ field_1_options = HorizontalTextAlignment.setShortValue(field_1_options, value);
+ }
+
+ /**
+ *
+ * @return the Horizontal text alignment field value.
+ */
+ public short getHorizontalTextAlignment()
+ {
+ return HorizontalTextAlignment.getShortValue(field_1_options);
+ }
+
+ /**
+ * Sets the Vertical text alignment field value.
+ *
+ */
+ public void setVerticalTextAlignment(short value)
+ {
+ field_1_options = VerticalTextAlignment.setShortValue(field_1_options, value);
+ }
+
+ /**
+ *
+ * @return the Vertical text alignment field value.
+ */
+ public short getVerticalTextAlignment()
+ {
+ return VerticalTextAlignment.getShortValue(field_1_options);
+ }
+
+ /**
+ * Sets the reserved2 field value.
+ *
+ */
+ public void setReserved2(short value)
+ {
+ field_1_options = reserved2.setShortValue(field_1_options, value);
+ }
+
+ /**
+ *
+ * @return the reserved2 field value.
+ */
+ public short getReserved2()
+ {
+ return reserved2.getShortValue(field_1_options);
+ }
+
+ /**
+ * Sets the text locked field value.
+ * Text has been locked
+ */
+ public void setTextLocked(boolean value)
+ {
+ field_1_options = textLocked.setShortBoolean(field_1_options, value);
+ }
+
+ /**
+ * Text has been locked
+ * @return the text locked field value.
+ */
+ public boolean isTextLocked()
+ {
+ return textLocked.isSet(field_1_options);
+ }
+
+ /**
+ * Sets the reserved3 field value.
+ *
+ */
+ public void setReserved3(short value)
+ {
+ field_1_options = reserved3.setShortValue(field_1_options, value);
+ }
+
+ /**
+ *
+ * @return the reserved3 field value.
+ */
+ public short getReserved3()
+ {
+ return reserved3.getShortValue(field_1_options);
+ }
+
+
+} // END OF CLASS
+
+
+
+
diff --git a/src/java/org/apache/poi/hssf/record/TextObjectRecord.java b/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
new file mode 100644
index 0000000000..e5652e802c
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
@@ -0,0 +1,216 @@
+package org.apache.poi.hssf.record;
+
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.util.StringUtil;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.HexDump;
+import java.io.UnsupportedEncodingException;
+
+public class TextObjectRecord
+ extends TextObjectBaseRecord
+{
+ HSSFRichTextString str = new HSSFRichTextString( "" );
+ int continueRecordCount = 0; // how many times has continue record been called?
+
+ public TextObjectRecord()
+ {
+ }
+
+ public TextObjectRecord( short id, short size, byte[] data )
+ {
+ super( id, size, data );
+ }
+
+ public TextObjectRecord( short id, short size, byte[] data, int offset )
+ {
+ super( id, size, data, offset );
+ }
+
+ public int getRecordSize()
+ {
+ int continue1Size = 0;
+ int continue2Size = 0;
+ if (str.length() != 0)
+ {
+ continue1Size = str.length() * 2 + 1 + 4;
+ continue2Size = (str.numFormattingRuns() + 1) * 8 + 4;
+ }
+ return super.getRecordSize() + continue1Size + continue2Size;
+ }
+
+ public int serialize( int offset, byte[] data )
+ {
+ // Temporarily blank out str so that record size is calculated without the continue records.
+ HSSFRichTextString temp = str;
+ str = new HSSFRichTextString("");
+ int bytesWritten1 = super.serialize( offset, data );
+ str = temp;
+
+ int pos = offset + bytesWritten1;
+ if ( str.toString().equals( "" ) == false )
+ {
+ ContinueRecord c1 = createContinue1();
+ ContinueRecord c2 = createContinue2();
+ int bytesWritten2 = c1.serialize( pos, data );
+ pos += bytesWritten2;
+ int bytesWritten3 = c2.serialize( pos, data );
+ pos += bytesWritten3;
+
+ int size = bytesWritten1 + bytesWritten2 + bytesWritten3;
+ if ( size != getRecordSize() )
+ throw new RecordFormatException(size + " bytes written but getRecordSize() reports " + getRecordSize());
+ return size;
+ }
+ if ( bytesWritten1 != getRecordSize() )
+ throw new RecordFormatException(bytesWritten1 + " bytes written but getRecordSize() reports " + getRecordSize());
+ return bytesWritten1;
+ }
+
+ private ContinueRecord createContinue1()
+ {
+ ContinueRecord c1 = new ContinueRecord();
+ byte[] c1Data = new byte[str.length() * 2 + 1];
+ try
+ {
+ c1Data[0] = 1;
+ System.arraycopy( str.toString().getBytes( "UTF-16LE" ), 0, c1Data, 1, str.length() * 2 );
+ }
+ catch ( UnsupportedEncodingException e )
+ {
+ throw new RuntimeException( e.getMessage() );
+ }
+ c1.setData( c1Data );
+ return c1;
+ }
+
+ private ContinueRecord createContinue2()
+ {
+ ContinueRecord c2 = new ContinueRecord();
+ byte[] c2Data = new byte[str.numFormattingRuns() * 8 + 8];
+ int pos = 0;
+ for ( int i = 0; i < str.numFormattingRuns(); i++ )
+ {
+ LittleEndian.putShort( c2Data, pos, (short) str.getIndexOfFormattingRun( i ) );
+ pos += 2;
+ LittleEndian.putShort( c2Data, pos, str.getFontOfFormattingRun( i ) == str.NO_FONT ? 0 : str.getFontOfFormattingRun( i ) );
+ pos += 2;
+ pos += 4; // skip reserved
+ }
+ LittleEndian.putShort( c2Data, pos, (short) str.length() );
+ pos += 2;
+ LittleEndian.putShort( c2Data, pos, (short) 0 );
+ pos += 2;
+ pos += 4; // skip reserved
+
+ c2.setData( c2Data );
+
+ return c2;
+ }
+
+ public void processContinueRecord( byte[] data )
+ {
+ if ( continueRecordCount == 0 )
+ processRawString( data );
+ else
+ processFontRuns( data );
+ continueRecordCount++;
+ }
+
+ private void processFontRuns( byte[] data )
+ {
+ int pos = 0;
+ do
+ {
+ short index = LittleEndian.getShort( data, pos );
+ pos += 2;
+ short iFont = LittleEndian.getShort( data, pos );
+ pos += 2;
+ pos += 4; // skip reserved.
+
+ if ( index >= str.length() )
+ break;
+ str.applyFont( index, str.length(), iFont );
+ }
+ while ( true );
+ }
+
+ private void processRawString( byte[] data )
+ {
+ String s;
+ int pos = 0;
+ byte compressByte = data[pos++];
+ boolean isCompressed = compressByte == 0;
+ try
+ {
+ if ( isCompressed )
+ {
+ s = new String( data, pos, getTextLength(), StringUtil.getPreferredEncoding() );
+ }
+ else
+ {
+ s = new String( data, pos, getTextLength() * 2, "UTF-16LE" );
+ }
+ }
+ catch ( UnsupportedEncodingException e )
+ {
+ throw new RuntimeException( e.getMessage() );
+ }
+ str = new HSSFRichTextString( s );
+ }
+
+ public HSSFRichTextString getStr()
+ {
+ return str;
+ }
+
+ public void setStr( HSSFRichTextString str )
+ {
+ this.str = str;
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append( "[TXO]\n" );
+ buffer.append( " .options = " )
+ .append( "0x" ).append( HexDump.toHex( getOptions() ) )
+ .append( " (" ).append( getOptions() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved1 = " ).append( isReserved1() ).append( '\n' );
+ buffer.append( " .HorizontalTextAlignment = " ).append( getHorizontalTextAlignment() ).append( '\n' );
+ buffer.append( " .VerticalTextAlignment = " ).append( getVerticalTextAlignment() ).append( '\n' );
+ buffer.append( " .reserved2 = " ).append( getReserved2() ).append( '\n' );
+ buffer.append( " .textLocked = " ).append( isTextLocked() ).append( '\n' );
+ buffer.append( " .reserved3 = " ).append( getReserved3() ).append( '\n' );
+ buffer.append( " .textOrientation = " )
+ .append( "0x" ).append( HexDump.toHex( getTextOrientation() ) )
+ .append( " (" ).append( getTextOrientation() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved4 = " )
+ .append( "0x" ).append( HexDump.toHex( getReserved4() ) )
+ .append( " (" ).append( getReserved4() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved5 = " )
+ .append( "0x" ).append( HexDump.toHex( getReserved5() ) )
+ .append( " (" ).append( getReserved5() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved6 = " )
+ .append( "0x" ).append( HexDump.toHex( getReserved6() ) )
+ .append( " (" ).append( getReserved6() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .textLength = " )
+ .append( "0x" ).append( HexDump.toHex( getTextLength() ) )
+ .append( " (" ).append( getTextLength() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+ buffer.append( " .reserved7 = " )
+ .append( "0x" ).append( HexDump.toHex( getReserved7() ) )
+ .append( " (" ).append( getReserved7() ).append( " )" );
+ buffer.append( System.getProperty( "line.separator" ) );
+
+ buffer.append( " .string = " ).append(str).append('\n');
+
+ buffer.append( "[/TXO]\n" );
+ return buffer.toString();
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/record/UnknownRecord.java b/src/java/org/apache/poi/hssf/record/UnknownRecord.java
index 88ddd4ce05..ae6f6006b2 100644
--- a/src/java/org/apache/poi/hssf/record/UnknownRecord.java
+++ b/src/java/org/apache/poi/hssf/record/UnknownRecord.java
@@ -95,6 +95,13 @@ public class UnknownRecord
this.thedata = data;
}
+ public UnknownRecord( short id, short size, byte[] data, int offset )
+ {
+ sid = id;
+ thedata = new byte[size];
+ System.arraycopy(data, offset, thedata, 0, size);
+ }
+
/**
* spit the record out AS IS. no interperatation or identification
*/
diff --git a/src/java/org/apache/poi/hssf/record/VerticalPageBreakRecord.java b/src/java/org/apache/poi/hssf/record/VerticalPageBreakRecord.java
new file mode 100644
index 0000000000..10a9b7cd4b
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/record/VerticalPageBreakRecord.java
@@ -0,0 +1,107 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2004 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache POI" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache POI", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.poi.hssf.record;
+
+/**
+ * VerticalPageBreak record that stores page breaks at columns
+ * <p>
+ * This class is just used so that SID compares work properly in the RecordFactory
+ * @see PageBreakRecord
+ * @author Danny Mui (dmui at apache dot org)
+ */
+public class VerticalPageBreakRecord extends PageBreakRecord {
+
+ public static final short sid = PageBreakRecord.VERTICAL_SID;
+
+ /**
+ *
+ */
+ public VerticalPageBreakRecord() {
+ super();
+ }
+
+ /**
+ * @param sid
+ */
+ public VerticalPageBreakRecord(short sid) {
+ super(sid);
+ }
+
+ /**
+ * @param id
+ * @param size
+ * @param data
+ */
+ public VerticalPageBreakRecord(short id, short size, byte[] data) {
+ super(id, size, data);
+ }
+
+ /**
+ * @param id
+ * @param size
+ * @param data
+ * @param offset
+ */
+ public VerticalPageBreakRecord(short id, short size, byte[] data, int offset) {
+ super(id, size, data, offset);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.poi.hssf.record.Record#getSid()
+ */
+ public short getSid() {
+ return PageBreakRecord.VERTICAL_SID;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java
new file mode 100644
index 0000000000..44b8e4d83b
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java
@@ -0,0 +1,773 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.awt.font.GlyphVector;
+import java.awt.font.FontRenderContext;
+import java.util.Map;
+import java.text.AttributedCharacterIterator;
+
+public class DummyGraphics2d
+ extends Graphics2D
+{
+ BufferedImage img;
+ private Graphics2D g2D;
+
+ public DummyGraphics2d()
+ {
+ img = new BufferedImage(1000, 1000, 2);
+ g2D = (Graphics2D)img.getGraphics();
+ }
+
+ public void addRenderingHints(Map hints)
+ {
+ System.out.println( "addRenderingHinds(Map):" );
+ System.out.println( " hints = " + hints );
+ g2D.addRenderingHints( hints );
+ }
+
+ public void clip(Shape s)
+ {
+ System.out.println( "clip(Shape):" );
+ System.out.println( " s = " + s );
+ g2D.clip( s );
+ }
+
+ public void draw(Shape s)
+ {
+ System.out.println( "draw(Shape):" );
+ System.out.println( "s = " + s );
+ g2D.draw( s );
+ }
+
+ public void drawGlyphVector(GlyphVector g, float x, float y)
+ {
+ System.out.println( "drawGlyphVector(GlyphVector, float, float):" );
+ System.out.println( "g = " + g );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawGlyphVector( g, x, y );
+ }
+
+ public void drawImage(BufferedImage img,
+ BufferedImageOp op,
+ int x,
+ int y)
+ {
+ System.out.println( "drawImage(BufferedImage, BufferedImageOp, x, y):" );
+ System.out.println( "img = " + img );
+ System.out.println( "op = " + op );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawImage( img, op, x, y );
+ }
+
+ public boolean drawImage(Image img,
+ AffineTransform xform,
+ ImageObserver obs)
+ {
+ System.out.println( "drawImage(Image,AfflineTransform,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "xform = " + xform );
+ System.out.println( "obs = " + obs );
+ return g2D.drawImage( img, xform, obs );
+ }
+
+ public void drawRenderableImage(RenderableImage img,
+ AffineTransform xform)
+ {
+ System.out.println( "drawRenderableImage(RenderableImage, AfflineTransform):" );
+ System.out.println( "img = " + img );
+ System.out.println( "xform = " + xform );
+ g2D.drawRenderableImage( img, xform );
+ }
+
+ public void drawRenderedImage(RenderedImage img,
+ AffineTransform xform)
+ {
+ System.out.println( "drawRenderedImage(RenderedImage, AffineTransform):" );
+ System.out.println( "img = " + img );
+ System.out.println( "xform = " + xform );
+ g2D.drawRenderedImage( img, xform );
+ }
+
+ public void drawString(AttributedCharacterIterator iterator,
+ float x, float y)
+ {
+ System.out.println( "drawString(AttributedCharacterIterator):" );
+ System.out.println( "iterator = " + iterator );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawString( iterator, x, y );
+ }
+
+// public void drawString(AttributedCharacterIterator iterator,
+// int x, int y)
+// {
+// g2D.drawString( iterator, x, y );
+// }
+
+ public void drawString(String s, float x, float y)
+ {
+ System.out.println( "drawString(s,x,y):" );
+ System.out.println( "s = " + s );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawString( s, x, y );
+ }
+
+// public void drawString(String str, int x, int y)
+// {
+// g2D.drawString( str, x, y );
+// }
+
+ public void fill(Shape s)
+ {
+ System.out.println( "fill(Shape):" );
+ System.out.println( "s = " + s );
+ g2D.fill( s );
+ }
+
+// public void fill3DRect(int x, int y, int width, int height,
+// boolean raised) {
+// g2D.fill3DRect( x, y, width, height, raised );
+// }
+
+ public Color getBackground()
+ {
+ System.out.println( "getBackground():" );
+ return g2D.getBackground();
+ }
+
+ public Composite getComposite()
+ {
+ System.out.println( "getComposite():" );
+ return g2D.getComposite();
+ }
+
+ public GraphicsConfiguration getDeviceConfiguration()
+ {
+ System.out.println( "getDeviceConfiguration():" );
+ return g2D.getDeviceConfiguration();
+ }
+
+ public FontRenderContext getFontRenderContext()
+ {
+ System.out.println( "getFontRenderContext():" );
+ return g2D.getFontRenderContext();
+ }
+
+ public Paint getPaint()
+ {
+ System.out.println( "getPaint():" );
+ return g2D.getPaint();
+ }
+
+ public Object getRenderingHint(RenderingHints.Key hintKey)
+ {
+ System.out.println( "getRenderingHint(RenderingHints.Key):" );
+ System.out.println( "hintKey = " + hintKey );
+ return g2D.getRenderingHint( hintKey );
+ }
+
+ public RenderingHints getRenderingHints()
+ {
+ System.out.println( "getRenderingHints():" );
+ return g2D.getRenderingHints();
+ }
+
+ public Stroke getStroke()
+ {
+ System.out.println( "getStroke():" );
+ return g2D.getStroke();
+ }
+
+ public AffineTransform getTransform()
+ {
+ System.out.println( "getTransform():" );
+ return g2D.getTransform();
+ }
+
+ public boolean hit(Rectangle rect,
+ Shape s,
+ boolean onStroke)
+ {
+ System.out.println( "hit(Rectangle, Shape, onStroke):" );
+ System.out.println( "rect = " + rect );
+ System.out.println( "s = " + s );
+ System.out.println( "onStroke = " + onStroke );
+ return g2D.hit( rect, s, onStroke );
+ }
+
+ public void rotate(double theta)
+ {
+ System.out.println( "rotate(theta):" );
+ System.out.println( "theta = " + theta );
+ g2D.rotate( theta );
+ }
+
+ public void rotate(double theta, double x, double y)
+ {
+ System.out.println( "rotate(double,double,double):" );
+ System.out.println( "theta = " + theta );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.rotate( theta, x, y );
+ }
+
+ public void scale(double sx, double sy)
+ {
+ System.out.println( "scale(double,double):" );
+ System.out.println( "sx = " + sx );
+ System.out.println( "sy" );
+ g2D.scale( sx, sy );
+ }
+
+ public void setBackground(Color color)
+ {
+ System.out.println( "setBackground(Color):" );
+ System.out.println( "color = " + color );
+ g2D.setBackground( color );
+ }
+
+ public void setComposite(Composite comp)
+ {
+ System.out.println( "setComposite(Composite):" );
+ System.out.println( "comp = " + comp );
+ g2D.setComposite( comp );
+ }
+
+ public void setPaint( Paint paint )
+ {
+ System.out.println( "setPain(Paint):" );
+ System.out.println( "paint = " + paint );
+ g2D.setPaint( paint );
+ }
+
+ public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
+ {
+ System.out.println( "setRenderingHint(RenderingHints.Key, Object):" );
+ System.out.println( "hintKey = " + hintKey );
+ System.out.println( "hintValue = " + hintValue );
+ g2D.setRenderingHint( hintKey, hintValue );
+ }
+
+ public void setRenderingHints(Map hints)
+ {
+ System.out.println( "setRenderingHints(Map):" );
+ System.out.println( "hints = " + hints );
+ g2D.setRenderingHints( hints );
+ }
+
+ public void setStroke(Stroke s)
+ {
+ System.out.println( "setStroke(Stoke):" );
+ System.out.println( "s = " + s );
+ g2D.setStroke( s );
+ }
+
+ public void setTransform(AffineTransform Tx)
+ {
+ System.out.println( "setTransform():" );
+ System.out.println( "Tx = " + Tx );
+ g2D.setTransform( Tx );
+ }
+
+ public void shear(double shx, double shy)
+ {
+ System.out.println( "shear(shx, dhy):" );
+ System.out.println( "shx = " + shx );
+ System.out.println( "shy = " + shy );
+ g2D.shear( shx, shy );
+ }
+
+ public void transform(AffineTransform Tx)
+ {
+ System.out.println( "transform(AffineTransform):" );
+ System.out.println( "Tx = " + Tx );
+ g2D.transform( Tx );
+ }
+
+ public void translate(double tx, double ty)
+ {
+ System.out.println( "translate(double, double):" );
+ System.out.println( "tx = " + tx );
+ System.out.println( "ty = " + ty );
+ g2D.translate( tx, ty );
+ }
+
+// public void translate(int x, int y)
+// {
+// g2D.translate( x, y );
+// }
+
+ public void clearRect(int x, int y, int width, int height)
+ {
+ System.out.println( "clearRect(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.clearRect( x, y, width, height );
+ }
+
+ public void clipRect(int x, int y, int width, int height)
+ {
+ System.out.println( "clipRect(int, int, int, int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.clipRect( x, y, width, height );
+ }
+
+ public void copyArea(int x, int y, int width, int height,
+ int dx, int dy)
+ {
+ System.out.println( "copyArea(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.copyArea( x, y, width, height, dx, dy );
+ }
+
+ public Graphics create()
+ {
+ System.out.println( "create():" );
+ return g2D.create();
+ }
+
+ public Graphics create(int x, int y, int width, int height) {
+ System.out.println( "create(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ return g2D.create( x, y, width, height );
+ }
+
+ public void dispose()
+ {
+ System.out.println( "dispose():" );
+ g2D.dispose();
+ }
+
+ public void draw3DRect(int x, int y, int width, int height,
+ boolean raised) {
+ System.out.println( "draw3DRect(int,int,int,int,boolean):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "raised = " + raised );
+ g2D.draw3DRect( x, y, width, height, raised );
+ }
+
+ public void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ System.out.println( "drawArc(int,int,int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "startAngle = " + startAngle );
+ System.out.println( "arcAngle = " + arcAngle );
+ g2D.drawArc( x, y, width, height, startAngle, arcAngle );
+ }
+
+ public void drawBytes(byte data[], int offset, int length, int x, int y) {
+ System.out.println( "drawBytes(byte[],int,int,int,int):" );
+ System.out.println( "data = " + data );
+ System.out.println( "offset = " + offset );
+ System.out.println( "length = " + length );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawBytes( data, offset, length, x, y );
+ }
+
+ public void drawChars(char data[], int offset, int length, int x, int y) {
+ System.out.println( "drawChars(data,int,int,int,int):" );
+ System.out.println( "data = " + data );
+ System.out.println( "offset = " + offset );
+ System.out.println( "length = " + length );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawChars( data, offset, length, x, y );
+ }
+
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,int,int,int,int,int,int,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "dx1 = " + dx1 );
+ System.out.println( "dy1 = " + dy1 );
+ System.out.println( "dx2 = " + dx2 );
+ System.out.println( "dy2 = " + dy2 );
+ System.out.println( "sx1 = " + sx1 );
+ System.out.println( "sy1 = " + sy1 );
+ System.out.println( "sx2 = " + sx2 );
+ System.out.println( "sy2 = " + sy2 );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer );
+ }
+
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ Color bgcolor,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,int,int,int,int,int,int,Color,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "dx1 = " + dx1 );
+ System.out.println( "dy1 = " + dy1 );
+ System.out.println( "dx2 = " + dx2 );
+ System.out.println( "dy2 = " + dy2 );
+ System.out.println( "sx1 = " + sx1 );
+ System.out.println( "sy1 = " + sy1 );
+ System.out.println( "sx2 = " + sx2 );
+ System.out.println( "sy2 = " + sy2 );
+ System.out.println( "bgcolor = " + bgcolor );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ Color bgcolor,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,Color,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "bgcolor = " + bgcolor );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, x, y, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,observer):" );
+ System.out.println( "img = " + img );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, x, y, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ Color bgcolor,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,int,int,Color,ImageObserver):" );
+ System.out.println( "img = " + img );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "bgcolor = " + bgcolor );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, x, y, width, height, bgcolor, observer );
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ ImageObserver observer)
+ {
+ System.out.println( "drawImage(Image,int,int,width,height,observer):" );
+ System.out.println( "img = " + img );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "observer = " + observer );
+ return g2D.drawImage( img, x, y, width, height, observer );
+ }
+
+ public void drawLine(int x1, int y1, int x2, int y2)
+ {
+ System.out.println( "drawLine(int,int,int,int):" );
+ System.out.println( "x1 = " + x1 );
+ System.out.println( "y1 = " + y1 );
+ System.out.println( "x2 = " + x2 );
+ System.out.println( "y2 = " + y2 );
+ g2D.drawLine( x1, y1, x2, y2 );
+ }
+
+ public void drawOval(int x, int y, int width, int height)
+ {
+ System.out.println( "drawOval(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.drawOval( x, y, width, height );
+ }
+
+ public void drawPolygon(Polygon p) {
+ System.out.println( "drawPolygon(Polygon):" );
+ System.out.println( "p = " + p );
+ g2D.drawPolygon( p );
+ }
+
+ public void drawPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ System.out.println( "drawPolygon(int[],int[],int):" );
+ System.out.println( "xPoints = " + xPoints );
+ System.out.println( "yPoints = " + yPoints );
+ System.out.println( "nPoints = " + nPoints );
+ g2D.drawPolygon( xPoints, yPoints, nPoints );
+ }
+
+ public void drawPolyline(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ System.out.println( "drawPolyline(int[],int[],int):" );
+ System.out.println( "xPoints = " + xPoints );
+ System.out.println( "yPoints = " + yPoints );
+ System.out.println( "nPoints = " + nPoints );
+ g2D.drawPolyline( xPoints, yPoints, nPoints );
+ }
+
+ public void drawRect(int x, int y, int width, int height) {
+ System.out.println( "drawRect(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.drawRect( x, y, width, height );
+ }
+
+ public void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ System.out.println( "drawRoundRect(int,int,int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "arcWidth = " + arcWidth );
+ System.out.println( "arcHeight = " + arcHeight );
+ g2D.drawRoundRect( x, y, width, height, arcWidth, arcHeight );
+ }
+
+ public void drawString(AttributedCharacterIterator iterator,
+ int x, int y)
+ {
+ System.out.println( "drawString(AttributedCharacterIterator,int,int):" );
+ System.out.println( "iterator = " + iterator );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawString( iterator, x, y );
+ }
+
+ public void drawString(String str, int x, int y)
+ {
+ System.out.println( "drawString(str,int,int):" );
+ System.out.println( "str = " + str );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.drawString( str, x, y );
+ }
+
+ public void fill3DRect(int x, int y, int width, int height,
+ boolean raised) {
+ System.out.println( "fill3DRect(int,int,int,int,boolean):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "raised = " + raised );
+ g2D.fill3DRect( x, y, width, height, raised );
+ }
+
+ public void fillArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ System.out.println( "fillArc(int,int,int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ System.out.println( "startAngle = " + startAngle );
+ System.out.println( "arcAngle = " + arcAngle );
+ g2D.fillArc( x, y, width, height, startAngle, arcAngle );
+ }
+
+ public void fillOval(int x, int y, int width, int height)
+ {
+ System.out.println( "fillOval(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.fillOval( x, y, width, height );
+ }
+
+ public void fillPolygon(Polygon p) {
+ System.out.println( "fillPolygon(Polygon):" );
+ System.out.println( "p = " + p );
+ g2D.fillPolygon( p );
+ }
+
+ public void fillPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ System.out.println( "fillPolygon(int[],int[],int):" );
+ System.out.println( "xPoints = " + xPoints );
+ System.out.println( "yPoints = " + yPoints );
+ System.out.println( "nPoints = " + nPoints );
+ g2D.fillPolygon( xPoints, yPoints, nPoints );
+ }
+
+ public void fillRect(int x, int y, int width, int height)
+ {
+ System.out.println( "fillRect(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.fillRect( x, y, width, height );
+ }
+
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ System.out.println( "fillRoundRect(int,int,int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.fillRoundRect( x, y, width, height, arcWidth, arcHeight );
+ }
+
+ public void finalize() {
+ System.out.println( "finalize():" );
+ g2D.finalize();
+ }
+
+ public Shape getClip()
+ {
+ System.out.println( "getClip():" );
+ return g2D.getClip();
+ }
+
+ public Rectangle getClipBounds()
+ {
+ System.out.println( "getClipBounds():" );
+ return g2D.getClipBounds();
+ }
+
+ public Rectangle getClipBounds(Rectangle r) {
+ System.out.println( "getClipBounds(Rectangle):" );
+ System.out.println( "r = " + r );
+ return g2D.getClipBounds( r );
+ }
+
+ public Rectangle getClipRect() {
+ System.out.println( "getClipRect():" );
+ return g2D.getClipRect();
+ }
+
+ public Color getColor()
+ {
+ System.out.println( "getColor():" );
+ return g2D.getColor();
+ }
+
+ public Font getFont()
+ {
+ System.out.println( "getFont():" );
+ return g2D.getFont();
+ }
+
+ public FontMetrics getFontMetrics() {
+ System.out.println( "getFontMetrics():" );
+ return g2D.getFontMetrics();
+ }
+
+ public FontMetrics getFontMetrics(Font f)
+ {
+ System.out.println( "getFontMetrics():" );
+ return g2D.getFontMetrics( f );
+ }
+
+ public boolean hitClip(int x, int y, int width, int height) {
+ System.out.println( "hitClip(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ return g2D.hitClip( x, y, width, height );
+ }
+
+ public void setClip(Shape clip)
+ {
+ System.out.println( "setClip(Shape):" );
+ System.out.println( "clip = " + clip );
+ g2D.setClip( clip );
+ }
+
+ public void setClip(int x, int y, int width, int height)
+ {
+ System.out.println( "setClip(int,int,int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ System.out.println( "width = " + width );
+ System.out.println( "height = " + height );
+ g2D.setClip( x, y, width, height );
+ }
+
+ public void setColor(Color c)
+ {
+ System.out.println( "setColor():" );
+ System.out.println( "c = " + c );
+ g2D.setColor( c );
+ }
+
+ public void setFont(Font font)
+ {
+ System.out.println( "setFont(Font):" );
+ System.out.println( "font = " + font );
+ g2D.setFont( font );
+ }
+
+ public void setPaintMode()
+ {
+ System.out.println( "setPaintMode():" );
+ g2D.setPaintMode();
+ }
+
+ public void setXORMode(Color c1)
+ {
+ System.out.println( "setXORMode(Color):" );
+ System.out.println( "c1 = " + c1 );
+ g2D.setXORMode( c1 );
+ }
+
+ public String toString() {
+ System.out.println( "toString():" );
+ return g2D.toString();
+ }
+
+ public void translate(int x, int y)
+ {
+ System.out.println( "translate(int,int):" );
+ System.out.println( "x = " + x );
+ System.out.println( "y = " + y );
+ g2D.translate( x, y );
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java
new file mode 100644
index 0000000000..fa8065a44c
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java
@@ -0,0 +1,450 @@
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.hssf.util.HSSFColor;
+
+import java.awt.*;
+import java.awt.image.ImageObserver;
+import java.text.AttributedCharacterIterator;
+
+/**
+ * Translates Graphics calls into escher calls. The translation is lossy so
+ * many features are not supported and some just aren't implemented yet. If
+ * in doubt test the specific calls you wish to make. Graphics calls are
+ * always performed into an EscherGroup so one will need to be created.
+ * <p>
+ * <b>Important:</b>
+ * <blockquote>
+ * One important concept worth considering is that of font size. One of the
+ * difficulties in converting Graphics calls into escher drawing calls is that
+ * Excel does not have the concept of absolute pixel positions. It measures
+ * it's cell widths in 'characters' and the cell heights in points.
+ * Unfortunately it's not defined exactly what a type of character it's
+ * measuring. Presumably this is due to the fact that the Excel will be
+ * using different fonts on different platforms or even within the same
+ * platform.
+ * <p>
+ * Because of this constraint we've had to calculate the
+ * verticalPointsPerPixel. This the amount the font should be scaled by when
+ * you issue commands such as drawString(). A good way to calculate this
+ * is to use the follow formula:
+ * <p>
+ * <pre>
+ * multipler = groupHeightInPoints / heightOfGroup
+ * </pre>
+ * <p>
+ * The height of the group is calculated fairly simply by calculating the
+ * difference between the y coordinates of the bounding box of the shape. The
+ * height of the group can be calculated by using a convenience called
+ * <code>HSSFClientAnchor.getAnchorHeightInPoints()</code>.
+ * </blockquote>
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherGraphics
+ extends Graphics
+{
+ private HSSFShapeGroup escherGroup;
+ private HSSFWorkbook workbook;
+ private float verticalPointsPerPixel = 1.0f;
+ private float verticalPixelsPerPoint;
+ private Color foreground;
+ private Color background = Color.white;
+ private Font font;
+ private static POILogger logger = POILogFactory.getLogger(EscherGraphics.class);
+
+ /**
+ * Construct an escher graphics object.
+ *
+ * @param escherGroup The escher group to write the graphics calls into.
+ * @param workbook The workbook we are using.
+ * @param forecolor The foreground color to use as default.
+ * @param verticalPointsPerPixel The font multiplier. (See class description for information on how this works.).
+ */
+ public EscherGraphics(HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color forecolor, float verticalPointsPerPixel )
+ {
+ this.escherGroup = escherGroup;
+ this.workbook = workbook;
+ this.verticalPointsPerPixel = verticalPointsPerPixel;
+ this.verticalPixelsPerPoint = 1 / verticalPointsPerPixel;
+ this.font = new Font("Arial", 0, 10);
+ this.foreground = forecolor;
+// background = backcolor;
+ }
+
+ /**
+ * Constructs an escher graphics object.
+ *
+ * @param escherGroup The escher group to write the graphics calls into.
+ * @param workbook The workbook we are using.
+ * @param foreground The foreground color to use as default.
+ * @param verticalPointsPerPixel The font multiplier. (See class description for information on how this works.).
+ * @param font The font to use.
+ */
+ EscherGraphics( HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color foreground, Font font, float verticalPointsPerPixel )
+ {
+ this.escherGroup = escherGroup;
+ this.workbook = workbook;
+ this.foreground = foreground;
+// this.background = background;
+ this.font = font;
+ this.verticalPointsPerPixel = verticalPointsPerPixel;
+ this.verticalPixelsPerPoint = 1 / verticalPointsPerPixel;
+ }
+
+ /**
+ * Constructs an escher graphics object.
+ *
+ * @param escherGroup The escher group to write the graphics calls into.
+ * @param workbook The workbook we are using.
+ * @param forecolor The default foreground color.
+ */
+// public EscherGraphics( HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color forecolor)
+// {
+// this(escherGroup, workbook, forecolor, 1.0f);
+// }
+
+
+ public void clearRect(int x, int y, int width, int height)
+ {
+ Color color = foreground;
+ setColor(background);
+ fillRect(x,y,width,height);
+ setColor(color);
+ }
+
+ public void clipRect(int x, int y, int width, int height)
+ {
+ logger.log(POILogger.WARN,"clipRect not supported");
+ }
+
+ public void copyArea(int x, int y, int width, int height, int dx, int dy)
+ {
+ logger.log(POILogger.WARN,"copyArea not supported");
+ }
+
+ public Graphics create()
+ {
+ EscherGraphics g = new EscherGraphics(escherGroup, workbook,
+ foreground, font, verticalPointsPerPixel );
+ return g;
+ }
+
+ public void dispose()
+ {
+ }
+
+ public void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ logger.log(POILogger.WARN,"drawArc not supported");
+ }
+
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ Color bgcolor,
+ ImageObserver observer)
+ {
+ logger.log(POILogger.WARN,"drawImage not supported");
+
+ return true;
+ }
+
+ public boolean drawImage(Image img,
+ int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer)
+ {
+ logger.log(POILogger.WARN,"drawImage not supported");
+ return true;
+ }
+
+ public boolean drawImage(Image image, int i, int j, int k, int l, Color color, ImageObserver imageobserver)
+ {
+ return drawImage(image, i, j, i + k, j + l, 0, 0, image.getWidth(imageobserver), image.getHeight(imageobserver), color, imageobserver);
+ }
+
+ public boolean drawImage(Image image, int i, int j, int k, int l, ImageObserver imageobserver)
+ {
+ return drawImage(image, i, j, i + k, j + l, 0, 0, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver);
+ }
+
+ public boolean drawImage(Image image, int i, int j, Color color, ImageObserver imageobserver)
+ {
+ return drawImage(image, i, j, image.getWidth(imageobserver), image.getHeight(imageobserver), color, imageobserver);
+ }
+
+ public boolean drawImage(Image image, int i, int j, ImageObserver imageobserver)
+ {
+ return drawImage(image, i, j, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver);
+ }
+
+ public void drawLine(int x1, int y1, int x2, int y2)
+ {
+ HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor(x1, y1, x2, y2) );
+ shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ shape.setLineWidth(0);
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ }
+
+ public void drawOval(int x, int y, int width, int height)
+ {
+ HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor(x,y,x+width,y+height) );
+ shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
+ shape.setLineWidth(0);
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setNoFill(true);
+ }
+
+ public void drawPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ int right = findBiggest(xPoints);
+ int bottom = findBiggest(yPoints);
+ int left = findSmallest(xPoints);
+ int top = findSmallest(yPoints);
+ HSSFPolygon shape = escherGroup.createPolygon(new HSSFChildAnchor(left,top,right,bottom) );
+ shape.setPolygonDrawArea(right - left, bottom - top);
+ shape.setPoints(addToAll(xPoints, -left), addToAll(yPoints, -top));
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setLineWidth(0);
+ shape.setNoFill(true);
+ }
+
+ private int[] addToAll( int[] values, int amount )
+ {
+ int[] result = new int[values.length];
+ for ( int i = 0; i < values.length; i++ )
+ result[i] = values[i] + amount;
+ return result;
+ }
+
+ public void drawPolyline(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ logger.log(POILogger.WARN,"drawPolyline not supported");
+ }
+
+ public void drawRect(int x, int y, int width, int height)
+ {
+ logger.log(POILogger.WARN,"drawRect not supported");
+ }
+
+ public void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ logger.log(POILogger.WARN,"drawRoundRect not supported");
+ }
+
+ public void drawString(String str, int x, int y)
+ {
+ if (str == null || str.equals(""))
+ return;
+
+ Font excelFont = font;
+ if ( font.getName().equals( "SansSerif" ) )
+ {
+ excelFont = new Font( "Arial", font.getStyle(), (int) ( font.getSize() / verticalPixelsPerPoint ) );
+ }
+ else
+ {
+ excelFont = new Font( font.getName(), font.getStyle(), (int) ( font.getSize() / verticalPixelsPerPoint ));
+ }
+ FontDetails d = StaticFontMetrics.getFontDetails( excelFont );
+ int width = (int) ( (d.getStringWidth( str ) * 2.5) + 12 );
+ int height = (int) ( ( font.getSize() * 2.0 * verticalPixelsPerPoint ) + 6 );
+ y -= ( font.getSize() * verticalPixelsPerPoint ); // we want to draw the shape from the top-left
+ HSSFTextbox textbox = escherGroup.createTextbox( new HSSFChildAnchor( x, y, x + width, y + height ) );
+ textbox.setNoFill( true );
+ textbox.setLineStyle( HSSFShape.LINESTYLE_NONE );
+ HSSFRichTextString s = new HSSFRichTextString( str );
+ HSSFFont hssfFont = matchFont( excelFont );
+ s.applyFont( hssfFont );
+ textbox.setString( s );
+ }
+
+ private HSSFFont matchFont( Font font )
+ {
+ HSSFColor hssfColor = workbook.getCustomPalette()
+ .findColor((byte)foreground.getRed(), (byte)foreground.getGreen(), (byte)foreground.getBlue());
+ if (hssfColor == null)
+ hssfColor = workbook.getCustomPalette().findSimilarColor((byte)foreground.getRed(), (byte)foreground.getGreen(), (byte)foreground.getBlue());
+ boolean bold = (font.getStyle() & Font.BOLD) != 0;
+ boolean italic = (font.getStyle() & Font.ITALIC) != 0;
+ HSSFFont hssfFont = workbook.findFont(bold ? HSSFFont.BOLDWEIGHT_BOLD : 0,
+ hssfColor.getIndex(),
+ (short)(font.getSize() * 20),
+ font.getName(),
+ italic,
+ false,
+ (short)0,
+ (byte)0);
+ if (hssfFont == null)
+ {
+ hssfFont = workbook.createFont();
+ hssfFont.setBoldweight(bold ? HSSFFont.BOLDWEIGHT_BOLD : 0);
+ hssfFont.setColor(hssfColor.getIndex());
+ hssfFont.setFontHeight((short)(font.getSize() * 20));
+ hssfFont.setFontName(font.getName());
+ hssfFont.setItalic(italic);
+ hssfFont.setStrikeout(false);
+ hssfFont.setTypeOffset((short) 0);
+ hssfFont.setUnderline((byte) 0);
+ }
+
+ return hssfFont;
+ }
+
+
+ public void drawString(AttributedCharacterIterator iterator,
+ int x, int y)
+ {
+ logger.log(POILogger.WARN,"drawString not supported");
+ }
+
+ public void fillArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ logger.log(POILogger.WARN,"fillArc not supported");
+ }
+
+ public void fillOval(int x, int y, int width, int height)
+ {
+ HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor( x, y, x + width, y + height ) );
+ shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
+ shape.setLineStyle(HSSFShape.LINESTYLE_NONE);
+ shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ }
+
+ public void fillPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ int right = findBiggest(xPoints);
+ int bottom = findBiggest(yPoints);
+ int left = findSmallest(xPoints);
+ int top = findSmallest(yPoints);
+ HSSFPolygon shape = escherGroup.createPolygon(new HSSFChildAnchor(left,top,right,bottom) );
+ shape.setPolygonDrawArea(right - left, bottom - top);
+ shape.setPoints(addToAll(xPoints, -left), addToAll(yPoints, -top));
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ }
+
+ private int findBiggest( int[] values )
+ {
+ int result = Integer.MIN_VALUE;
+ for ( int i = 0; i < values.length; i++ )
+ {
+ if (values[i] > result)
+ result = values[i];
+ }
+ return result;
+ }
+
+ private int findSmallest( int[] values )
+ {
+ int result = Integer.MAX_VALUE;
+ for ( int i = 0; i < values.length; i++ )
+ {
+ if (values[i] < result)
+ result = values[i];
+ }
+ return result;
+ }
+
+ public void fillRect(int x, int y, int width, int height)
+ {
+ HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor( x, y, x + width, y + height ) );
+ shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE);
+ shape.setLineStyle(HSSFShape.LINESTYLE_NONE);
+ shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue());
+ }
+
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ logger.log(POILogger.WARN,"fillRoundRect not supported");
+ }
+
+ public Shape getClip()
+ {
+ return getClipBounds();
+ }
+
+ public Rectangle getClipBounds()
+ {
+ return null;
+ }
+
+ public Rectangle getClipRect()
+ {
+ return getClipBounds();
+ }
+
+ public Color getColor()
+ {
+ return foreground;
+ }
+
+ public Font getFont()
+ {
+ return font;
+ }
+
+ public FontMetrics getFontMetrics(Font f)
+ {
+ return Toolkit.getDefaultToolkit().getFontMetrics(f);
+ }
+
+ public void setClip(int x, int y, int width, int height)
+ {
+ setClip(((Shape) (new Rectangle(x,y,width,height))));
+ }
+
+ public void setClip(Shape shape)
+ {
+ // ignore... not implemented
+ }
+
+ public void setColor(Color color)
+ {
+ foreground = color;
+ }
+
+ public void setFont(Font f)
+ {
+ font = f;
+ }
+
+ public void setPaintMode()
+ {
+ logger.log(POILogger.WARN,"setPaintMode not supported");
+ }
+
+ public void setXORMode(Color color)
+ {
+ logger.log(POILogger.WARN,"setXORMode not supported");
+ }
+
+ public void translate(int x, int y)
+ {
+ logger.log(POILogger.WARN,"translate not supported");
+ }
+
+ public Color getBackground()
+ {
+ return background;
+ }
+
+ public void setBackground( Color background )
+ {
+ this.background = background;
+ }
+
+
+}
+
diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java
new file mode 100644
index 0000000000..f3298d79e6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java
@@ -0,0 +1,567 @@
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+import java.awt.*;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
+import java.awt.geom.GeneralPath;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.text.AttributedCharacterIterator;
+import java.util.Map;
+
+/**
+ * Translates Graphics2d calls into escher calls. The translation is lossy so
+ * many features are not supported and some just aren't implemented yet. If
+ * in doubt test the specific calls you wish to make. Graphics calls are
+ * always drawn into an EscherGroup so one will need to be created.
+ * <p>
+ * <b>Important:</b>
+ * <blockquote>
+ * One important concept worth considering is that of font size. One of the
+ * difficulties in converting Graphics calls into escher drawing calls is that
+ * Excel does not have the concept of absolute pixel positions. It measures
+ * it's cell widths in 'characters' and the cell heights in points.
+ * Unfortunately it's not defined exactly what a type of character it's
+ * measuring. Presumably this is due to the fact that the Excel will be
+ * using different fonts on different platforms or even within the same
+ * platform.
+ * <p>
+ * Because of this constraint you have to calculate the verticalPointsPerPixel.
+ * This the amount the font should be scaled by when
+ * you issue commands such as drawString(). A good way to calculate this
+ * is to use the follow formula:
+ * <p>
+ * <pre>
+ * multipler = groupHeightInPoints / heightOfGroup
+ * </pre>
+ * <p>
+ * The height of the group is calculated fairly simply by calculating the
+ * difference between the y coordinates of the bounding box of the shape. The
+ * height of the group can be calculated by using a convenience called
+ * <code>HSSFClientAnchor.getAnchorHeightInPoints()</code>.
+ * </blockquote>
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class EscherGraphics2d extends Graphics2D
+{
+ private EscherGraphics escherGraphics;
+ private BufferedImage img;
+ private AffineTransform trans;
+ private Stroke stroke;
+ private Paint paint;
+ private Shape deviceclip;
+ private POILogger logger = POILogFactory.getLogger(getClass());
+
+ /**
+ * Constructs one escher graphics object from an escher graphics object.
+ *
+ * @param escherGraphics the original EscherGraphics2d object to copy
+ */
+ public EscherGraphics2d(EscherGraphics escherGraphics)
+ {
+ this.escherGraphics = escherGraphics;
+ setImg( new BufferedImage(1, 1, 2) );
+ setColor(Color.black);
+ }
+
+ public void addRenderingHints(Map map)
+ {
+ getG2D().addRenderingHints(map);
+ }
+
+ public void clearRect(int i, int j, int k, int l)
+ {
+ Paint paint1 = getPaint();
+ setColor(getBackground());
+ fillRect(i, j, k, l);
+ setPaint(paint1);
+ }
+
+ public void clip(Shape shape)
+ {
+ if(getDeviceclip() != null)
+ {
+ Area area = new Area(getClip());
+ if(shape != null)
+ area.intersect(new Area(shape));
+ shape = area;
+ }
+ setClip(shape);
+ }
+
+ public void clipRect(int x, int y, int width, int height)
+ {
+ clip(new Rectangle(x,y,width,height));
+ }
+
+ public void copyArea(int x, int y, int width, int height,
+ int dx, int dy)
+ {
+ getG2D().copyArea(x,y,width,height,dx,dy);
+ }
+
+ public Graphics create()
+ {
+ EscherGraphics2d g2d = new EscherGraphics2d(escherGraphics);
+ return g2d;
+ }
+
+ public void dispose()
+ {
+ getEscherGraphics().dispose();
+ getG2D().dispose();
+ getImg().flush();
+ }
+
+ public void draw(Shape shape)
+ {
+ logger.log(POILogger.WARN,"copyArea not supported");
+ }
+
+ public void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ draw(new java.awt.geom.Arc2D.Float(x, y, width, height, startAngle, arcAngle, 0));
+ }
+
+ public void drawGlyphVector(GlyphVector g, float x, float y)
+ {
+ fill(g.getOutline(x, y));
+ }
+
+ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1,
+ int sx2, int sy2, Color bgColor, ImageObserver imageobserver)
+ {
+ logger.log(POILogger.WARN,"drawImage() not supported");
+ return true;
+ }
+
+ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1,
+ int sx2, int sy2, ImageObserver imageobserver)
+ {
+ logger.log(POILogger.WARN,"drawImage() not supported");
+ return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, imageobserver);
+ }
+ public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, Color bgColor, ImageObserver imageobserver)
+ {
+ logger.log(POILogger.WARN,"drawImage() not supported");
+ return true;
+ }
+
+ public boolean drawImage(Image img, int x, int y,
+ int width, int height,
+ ImageObserver observer)
+ {
+ return drawImage(img, x,y,width,height, null, observer);
+ }
+
+ public boolean drawImage(Image image, int x, int y, Color bgColor, ImageObserver imageobserver)
+ {
+ return drawImage(image, x, y, image.getWidth(imageobserver), image.getHeight(imageobserver), bgColor, imageobserver);
+ }
+
+ public boolean drawImage(Image image, int x, int y, ImageObserver imageobserver)
+ {
+ return drawImage(image, x, y, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver);
+ }
+
+ public boolean drawImage(Image image, AffineTransform affinetransform, ImageObserver imageobserver)
+ {
+ AffineTransform affinetransform1 = (AffineTransform)getTrans().clone();
+ getTrans().concatenate(affinetransform);
+ drawImage(image, 0, 0, imageobserver);
+ setTrans( affinetransform1 );
+ return true;
+ }
+
+ public void drawImage(BufferedImage bufferedimage, BufferedImageOp op, int x, int y)
+ {
+ BufferedImage img = op.filter(bufferedimage, null);
+ drawImage(((Image) (img)), new AffineTransform(1.0F, 0.0F, 0.0F, 1.0F, x, y), null);
+ }
+
+ public void drawLine(int x1, int y1, int x2, int y2)
+ {
+ getEscherGraphics().drawLine(x1,y1,x2,y2);
+// draw(new GeneralPath(new java.awt.geom.Line2D.Float(x1, y1, x2, y2)));
+ }
+
+ public void drawOval(int x, int y, int width, int height)
+ {
+ getEscherGraphics().drawOval(x,y,width,height);
+// draw(new java.awt.geom.Ellipse2D.Float(x, y, width, height));
+ }
+
+ public void drawPolygon(int xPoints[], int yPoints[],
+ int nPoints)
+ {
+ getEscherGraphics().drawPolygon(xPoints, yPoints, nPoints);
+ }
+
+ public void drawPolyline(int xPoints[], int yPoints[], int nPoints)
+ {
+ if(nPoints > 0)
+ {
+ GeneralPath generalpath = new GeneralPath();
+ generalpath.moveTo(xPoints[0], yPoints[0]);
+ for(int j = 1; j < nPoints; j++)
+ generalpath.lineTo(xPoints[j], yPoints[j]);
+
+ draw(generalpath);
+ }
+ }
+
+ public void drawRect(int x, int y, int width, int height)
+ {
+ escherGraphics.drawRect(x,y,width,height);
+ }
+
+ public void drawRenderableImage(RenderableImage renderableimage, AffineTransform affinetransform)
+ {
+ drawRenderedImage(renderableimage.createDefaultRendering(), affinetransform);
+ }
+
+ public void drawRenderedImage(RenderedImage renderedimage, AffineTransform affinetransform)
+ {
+ BufferedImage bufferedimage = new BufferedImage(renderedimage.getColorModel(), renderedimage.getData().createCompatibleWritableRaster(), false, null);
+ bufferedimage.setData(renderedimage.getData());
+ drawImage(bufferedimage, affinetransform, null);
+ }
+
+ public void drawRoundRect(int i, int j, int k, int l, int i1, int j1)
+ {
+ draw(new java.awt.geom.RoundRectangle2D.Float(i, j, k, l, i1, j1));
+ }
+
+ public void drawString(String string, float x, float y)
+ {
+ getEscherGraphics().drawString(string, (int)x, (int)y);
+ }
+
+ public void drawString(String string, int x, int y)
+ {
+ getEscherGraphics().drawString(string, x, y);
+ }
+
+ public void drawString(AttributedCharacterIterator attributedcharacteriterator, float x, float y)
+ {
+ TextLayout textlayout = new TextLayout(attributedcharacteriterator, getFontRenderContext());
+ Paint paint1 = getPaint();
+ setColor(getColor());
+ fill(textlayout.getOutline(AffineTransform.getTranslateInstance(x, y)));
+ setPaint(paint1);
+ }
+
+ public void drawString(AttributedCharacterIterator attributedcharacteriterator, int x, int y)
+ {
+ drawString(attributedcharacteriterator, x, y);
+ }
+
+ public void fill(Shape shape)
+ {
+ logger.log(POILogger.WARN,"fill(Shape) not supported");
+ }
+
+ public void fillArc(int i, int j, int k, int l, int i1, int j1)
+ {
+ fill(new java.awt.geom.Arc2D.Float(i, j, k, l, i1, j1, 2));
+ }
+
+ public void fillOval(int x, int y, int width, int height)
+ {
+ escherGraphics.fillOval(x,y,width,height);
+ }
+
+ /**
+ * Fills a closed polygon defined by
+ * arrays of <i>x</i> and <i>y</i> coordinates.
+ * <p>
+ * This method draws the polygon defined by <code>nPoint</code> line
+ * segments, where the first <code>nPoint&nbsp;-&nbsp;1</code>
+ * line segments are line segments from
+ * <code>(xPoints[i&nbsp;-&nbsp;1],&nbsp;yPoints[i&nbsp;-&nbsp;1])</code>
+ * to <code>(xPoints[i],&nbsp;yPoints[i])</code>, for
+ * 1&nbsp;&le;&nbsp;<i>i</i>&nbsp;&le;&nbsp;<code>nPoints</code>.
+ * The figure is automatically closed by drawing a line connecting
+ * the final point to the first point, if those points are different.
+ * <p>
+ * The area inside the polygon is defined using an
+ * even-odd fill rule, also known as the alternating rule.
+ * @param xPoints a an array of <code>x</code> coordinates.
+ * @param yPoints a an array of <code>y</code> coordinates.
+ * @param nPoints a the total number of points.
+ * @see java.awt.Graphics#drawPolygon(int[], int[], int)
+ */
+ public void fillPolygon(int xPoints[], int yPoints[], int nPoints)
+ {
+ escherGraphics.fillPolygon(xPoints, yPoints, nPoints);
+ }
+
+ public void fillRect(int x, int y, int width, int height)
+ {
+ getEscherGraphics().fillRect(x,y,width,height);
+ }
+
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ fill(new java.awt.geom.RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight));
+ }
+
+ public Color getBackground()
+ {
+ return getEscherGraphics().getBackground();
+ }
+
+ public Shape getClip()
+ {
+ try
+ {
+ return getTrans().createInverse().createTransformedShape(getDeviceclip());
+ }
+ catch(Exception _ex)
+ {
+ return null;
+ }
+ }
+
+ public Rectangle getClipBounds()
+ {
+ if(getDeviceclip() != null)
+ return getClip().getBounds();
+ else
+ return null;
+ }
+
+ public Color getColor()
+ {
+ return escherGraphics.getColor();
+ }
+
+ public Composite getComposite()
+ {
+ return getG2D().getComposite();
+ }
+
+ public GraphicsConfiguration getDeviceConfiguration()
+ {
+ return getG2D().getDeviceConfiguration();
+ }
+
+ public Font getFont()
+ {
+ return getEscherGraphics().getFont();
+ }
+
+ public FontMetrics getFontMetrics(Font font)
+ {
+ return getEscherGraphics().getFontMetrics(font);
+ }
+
+ public FontRenderContext getFontRenderContext()
+ {
+ getG2D().setTransform(getTrans());
+ return getG2D().getFontRenderContext();
+ }
+
+ public Paint getPaint()
+ {
+ return paint;
+ }
+
+ public Object getRenderingHint(java.awt.RenderingHints.Key key)
+ {
+ return getG2D().getRenderingHint(key);
+ }
+
+ public RenderingHints getRenderingHints()
+ {
+ return getG2D().getRenderingHints();
+ }
+
+ public Stroke getStroke()
+ {
+ return stroke;
+ }
+
+ public AffineTransform getTransform()
+ {
+ return (AffineTransform)getTrans().clone();
+ }
+
+ public boolean hit(Rectangle rectangle, Shape shape, boolean flag)
+ {
+ getG2D().setTransform(getTrans());
+ getG2D().setStroke(getStroke());
+ getG2D().setClip(getClip());
+ return getG2D().hit(rectangle, shape, flag);
+ }
+
+ public void rotate(double d)
+ {
+ getTrans().rotate(d);
+ }
+
+ public void rotate(double d, double d1, double d2)
+ {
+ getTrans().rotate(d, d1, d2);
+ }
+
+ public void scale(double d, double d1)
+ {
+ getTrans().scale(d, d1);
+ }
+
+ public void setBackground(Color c)
+ {
+ getEscherGraphics().setBackground(c);
+ }
+
+ public void setClip(int i, int j, int k, int l)
+ {
+ setClip(((Shape) (new Rectangle(i, j, k, l))));
+ }
+
+ public void setClip(Shape shape)
+ {
+ setDeviceclip( getTrans().createTransformedShape(shape) );
+ }
+
+ public void setColor(Color c)
+ {
+ escherGraphics.setColor(c);
+ }
+
+ public void setComposite(Composite composite)
+ {
+ getG2D().setComposite(composite);
+ }
+
+ public void setFont(Font font)
+ {
+ getEscherGraphics().setFont(font);
+ }
+
+ public void setPaint(Paint paint1)
+ {
+ if(paint1 != null)
+ {
+ paint = paint1;
+ if(paint1 instanceof Color)
+ setColor( (Color)paint1 );
+ }
+ }
+
+ public void setPaintMode()
+ {
+ getEscherGraphics().setPaintMode();
+ }
+
+ public void setRenderingHint(java.awt.RenderingHints.Key key, Object obj)
+ {
+ getG2D().setRenderingHint(key, obj);
+ }
+
+ public void setRenderingHints(Map map)
+ {
+ getG2D().setRenderingHints(map);
+ }
+
+ public void setStroke(Stroke s)
+ {
+ stroke = s;
+ }
+
+ public void setTransform(AffineTransform affinetransform)
+ {
+ setTrans( (AffineTransform)affinetransform.clone() );
+ }
+
+ public void setXORMode(Color color1)
+ {
+ getEscherGraphics().setXORMode(color1);
+ }
+
+ public void shear(double d, double d1)
+ {
+ getTrans().shear(d, d1);
+ }
+
+ public void transform(AffineTransform affinetransform)
+ {
+ getTrans().concatenate(affinetransform);
+ }
+
+// Image transformImage(Image image, Rectangle rectangle, Rectangle rectangle1, ImageObserver imageobserver, Color color1)
+// {
+// logger.log(POILogger.WARN,"transformImage() not supported");
+// return null;
+// }
+//
+// Image transformImage(Image image, int ai[], Rectangle rectangle, ImageObserver imageobserver, Color color1)
+// {
+// logger.log(POILogger.WARN,"transformImage() not supported");
+// return null;
+// }
+
+ public void translate(double d, double d1)
+ {
+ getTrans().translate(d, d1);
+ }
+
+ public void translate(int i, int j)
+ {
+ getTrans().translate(i, j);
+ }
+
+ private EscherGraphics getEscherGraphics()
+ {
+ return escherGraphics;
+ }
+
+ private BufferedImage getImg()
+ {
+ return img;
+ }
+
+ private void setImg( BufferedImage img )
+ {
+ this.img = img;
+ }
+
+ private Graphics2D getG2D()
+ {
+ return (Graphics2D) img.getGraphics();
+ }
+
+ private AffineTransform getTrans()
+ {
+ return trans;
+ }
+
+ private void setTrans( AffineTransform trans )
+ {
+ this.trans = trans;
+ }
+
+ private Shape getDeviceclip()
+ {
+ return deviceclip;
+ }
+
+ private void setDeviceclip( Shape deviceclip )
+ {
+ this.deviceclip = deviceclip;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/FontDetails.java b/src/java/org/apache/poi/hssf/usermodel/FontDetails.java
new file mode 100644
index 0000000000..96b0d87a86
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/FontDetails.java
@@ -0,0 +1,143 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * Stores width and height details about a font.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class FontDetails
+{
+ private String fontName;
+ private int height;
+ private Map charWidths = new HashMap();
+
+ /**
+ * Construct the font details with the given name and height.
+ *
+ * @param fontName The font name.
+ * @param height The height of the font.
+ */
+ public FontDetails( String fontName, int height )
+ {
+ this.fontName = fontName;
+ this.height = height;
+ }
+
+ public String getFontName()
+ {
+ return fontName;
+ }
+
+ public int getHeight()
+ {
+ return height;
+ }
+
+ public void addChar( char c, int width )
+ {
+ charWidths.put(new Character(c), new Integer(width));
+ }
+
+ /**
+ * Retrieves the width of the specified character. If the metrics for
+ * a particular character are not available it defaults to returning the
+ * width for the 'W' character.
+ */
+ public int getCharWidth( char c )
+ {
+ Integer widthInteger = (Integer)(charWidths.get(new Character(c)));
+ if (widthInteger == null && c != 'W')
+ return getCharWidth('W');
+ else
+ return widthInteger.intValue();
+ }
+
+ public void addChars( char[] characters, int[] widths )
+ {
+ for ( int i = 0; i < characters.length; i++ )
+ {
+ charWidths.put( new Character(characters[i]), new Integer(widths[i]));
+ }
+ }
+
+ /**
+ * Create an instance of <code>FontDetails</code> by loading them from the
+ * provided property object.
+ * @param fontName the font name
+ * @param fontMetricsProps the property object holding the details of this
+ * particular font.
+ * @return a new FontDetails instance.
+ */
+ public static FontDetails create( String fontName, Properties fontMetricsProps )
+ {
+ String heightStr = fontMetricsProps.getProperty( "font." + fontName + ".height");
+ String widthsStr = fontMetricsProps.getProperty( "font." + fontName + ".widths");
+ String charactersStr = fontMetricsProps.getProperty( "font." + fontName + ".characters");
+ int height = Integer.parseInt(heightStr);
+ FontDetails d = new FontDetails(fontName, height);
+ String[] charactersStrArray = split(charactersStr, ",", -1);
+ String[] widthsStrArray = split(widthsStr, ",", -1);
+ if (charactersStrArray.length != widthsStrArray.length)
+ throw new RuntimeException("Number of characters does not number of widths for font " + fontName);
+ for ( int i = 0; i < widthsStrArray.length; i++ )
+ {
+ if (charactersStrArray[i].length() != 0)
+ d.addChar(charactersStrArray[i].charAt(0), Integer.parseInt(widthsStrArray[i]));
+ }
+ return d;
+ }
+
+ /**
+ * Gets the width of all characters in a string.
+ *
+ * @param str The string to measure.
+ * @return The width of the string for a 10 point font.
+ */
+ public int getStringWidth(String str)
+ {
+ int width = 0;
+ for (int i = 0; i < str.length(); i++)
+ {
+ width += getCharWidth(str.charAt(i));
+ }
+ return width;
+ }
+
+ /**
+ * Split the given string into an array of strings using the given
+ * delimiter.
+ */
+ private static String[] split(String text, String separator, int max)
+ {
+ StringTokenizer tok = new StringTokenizer(text, separator);
+ int listSize = tok.countTokens();
+ if(max != -1 && listSize > max)
+ listSize = max;
+ String list[] = new String[listSize];
+ for(int i = 0; tok.hasMoreTokens(); i++)
+ {
+ if(max != -1 && i == listSize - 1)
+ {
+ StringBuffer buf = new StringBuffer((text.length() * (listSize - i)) / listSize);
+ while(tok.hasMoreTokens())
+ {
+ buf.append(tok.nextToken());
+ if(tok.hasMoreTokens())
+ buf.append(separator);
+ }
+ list[i] = buf.toString().trim();
+ break;
+ }
+ list[i] = tok.nextToken().trim();
+ }
+
+ return list;
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java
new file mode 100644
index 0000000000..b9db3c78eb
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java
@@ -0,0 +1,40 @@
+package org.apache.poi.hssf.usermodel;
+
+
+/**
+ * An anchor is what specifics the position of a shape within a client object
+ * or within another containing shape.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public abstract class HSSFAnchor
+{
+ int dx1;
+ int dy1;
+ int dx2;
+ int dy2;
+
+ public HSSFAnchor()
+ {
+ }
+
+ public HSSFAnchor( int dx1, int dy1, int dx2, int dy2 )
+ {
+ this.dx1 = dx1;
+ this.dy1 = dy1;
+ this.dx2 = dx2;
+ this.dy2 = dy2;
+ }
+
+ public int getDx1(){ return dx1; }
+ public void setDx1( int dx1 ){ this.dx1 = dx1; }
+ public int getDy1(){ return dy1; }
+ public void setDy1( int dy1 ){ this.dy1 = dy1; }
+ public int getDy2(){ return dy2; }
+ public void setDy2( int dy2 ){ this.dy2 = dy2; }
+ public int getDx2(){ return dx2; }
+ public void setDx2( int dx2 ){ this.dx2 = dx2; }
+
+ public abstract boolean isHorizontallyFlipped();
+ public abstract boolean isVerticallyFlipped();
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java
new file mode 100644
index 0000000000..fd69f48cde
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java
@@ -0,0 +1,37 @@
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherClientAnchorRecord;
+import org.apache.poi.ddf.EscherChildAnchorRecord;
+
+public class HSSFChildAnchor
+ extends HSSFAnchor
+{
+ public HSSFChildAnchor()
+ {
+ }
+
+ public HSSFChildAnchor( int dx1, int dy1, int dx2, int dy2 )
+ {
+ super( dx1, dy1, dx2, dy2 );
+ }
+
+ public void setAnchor(int dx1, int dy1, int dx2, int dy2)
+ {
+ this.dx1 = dx1;
+ this.dy1 = dy1;
+ this.dx2 = dx2;
+ this.dy2 = dy2;
+ }
+
+ public boolean isHorizontallyFlipped()
+ {
+ return dx1 > dx2;
+ }
+
+ public boolean isVerticallyFlipped()
+ {
+ return dy1 > dy2;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java
new file mode 100644
index 0000000000..cbbaf167f2
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java
@@ -0,0 +1,207 @@
+package org.apache.poi.hssf.usermodel;
+
+import org.apache.poi.ddf.EscherClientAnchorRecord;
+import org.apache.poi.ddf.EscherRecord;
+
+
+/**
+ * A client anchor is attached to an excel worksheet. It anchors against a
+ * top-left and buttom-right cell.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFClientAnchor
+ extends HSSFAnchor
+{
+ short col1;
+ int row1;
+ short col2;
+ int row2;
+
+ /**
+ * Creates a new client anchor and defaults all the anchor positions to 0.
+ */
+ public HSSFClientAnchor()
+ {
+ }
+
+ /**
+ * Creates a new client anchor and sets the top-left and bottom-right
+ * coordinates of the anchor.
+ *
+ * @param dx1 the x coordinate within the first cell.
+ * @param dy1 the y coordinate within the first cell.
+ * @param dx2 the x coordinate within the second cell.
+ * @param dy2 the y coordinate within the second cell.
+ * @param col1 the column (0 based) of the first cell.
+ * @param row1 the row (0 based) of the first cell.
+ * @param col2 the column (0 based) of the second cell.
+ * @param row2 the row (0 based) of the second cell.
+ */
+ public HSSFClientAnchor( int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2 )
+ {
+ super( dx1, dy1, dx2, dy2 );
+
+ checkRange(dx1, 0, 1023, "dx1");
+ checkRange(dx2, 0, 1023, "dx2");
+ checkRange(dy1, 0, 255, "dy1");
+ checkRange(dy2, 0, 255, "dy2");
+ checkRange(col1, 0, 255, "col1");
+ checkRange(col2, 0, 255, "col2");
+ checkRange(row1, 0, 255 * 256, "row1");
+ checkRange(row2, 0, 255 * 256, "row2");
+
+ this.col1 = col1;
+ this.row1 = row1;
+ this.col2 = col2;
+ this.row2 = row2;
+ }
+
+ /**
+ * Calculates the height of a client anchor in points.
+ *
+ * @param sheet the sheet the anchor will be attached to
+ * @return the shape height.
+ */
+ public float getAnchorHeightInPoints(HSSFSheet sheet )
+ {
+ int y1 = Math.min( getDy1(), getDy2() );
+ int y2 = Math.max( getDy1(), getDy2() );
+ int row1 = Math.min( getRow1(), getRow2() );
+ int row2 = Math.max( getRow1(), getRow2() );
+
+ float points = 0;
+ if (row1 == row2)
+ {
+ points = ((y2 - y1) / 256.0f) * getRowHeightInPoints(sheet, row2);
+ }
+ else
+ {
+ points += ((256.0f - y1) / 256.0f) * getRowHeightInPoints(sheet, row1);
+ for (int i = row1 + 1; i < row2; i++)
+ {
+ points += getRowHeightInPoints(sheet, i);
+ }
+ points += (y2 / 256.0f) * getRowHeightInPoints(sheet, row2);
+ }
+
+ return points;
+ }
+
+ private float getRowHeightInPoints(HSSFSheet sheet, int rowNum)
+ {
+ HSSFRow row = sheet.getRow(rowNum);
+ if (row == null)
+ return sheet.getDefaultRowHeightInPoints();
+ else
+ return row.getHeightInPoints();
+ }
+
+ public short getCol1()
+ {
+ return col1;
+ }
+
+ public void setCol1( short col1 )
+ {
+ checkRange(col1, 0, 255, "col1");
+ this.col1 = col1;
+ }
+
+ public short getCol2()
+ {
+ return col2;
+ }
+
+ public void setCol2( short col2 )
+ {
+ checkRange(col2, 0, 255, "col2");
+ this.col2 = col2;
+ }
+
+ public int getRow1()
+ {
+ return row1;
+ }
+
+ public void setRow1( int row1 )
+ {
+ checkRange(row1, 0, 256 * 256, "row1");
+ this.row1 = row1;
+ }
+
+ public int getRow2()
+ {
+ return row2;
+ }
+
+ public void setRow2( int row2 )
+ {
+ checkRange(row2, 0, 256 * 256, "row2");
+ this.row2 = row2;
+ }
+
+ /**
+ * Dets the top-left and bottom-right
+ * coordinates of the anchor.
+ *
+ * @param x1 the x coordinate within the first cell.
+ * @param y1 the y coordinate within the first cell.
+ * @param x2 the x coordinate within the second cell.
+ * @param y2 the y coordinate within the second cell.
+ * @param col1 the column (0 based) of the first cell.
+ * @param row1 the row (0 based) of the first cell.
+ * @param col2 the column (0 based) of the second cell.
+ * @param row2 the row (0 based) of the second cell.
+ */
+ public void setAnchor( short col1, int row1, int x1, int y1, short col2, int row2, int x2, int y2 )
+ {
+ checkRange(dx1, 0, 1023, "dx1");
+ checkRange(dx2, 0, 1023, "dx2");
+ checkRange(dy1, 0, 255, "dy1");
+ checkRange(dy2, 0, 255, "dy2");
+ checkRange(col1, 0, 255, "col1");
+ checkRange(col2, 0, 255, "col2");
+ checkRange(row1, 0, 255 * 256, "row1");
+ checkRange(row2, 0, 255 * 256, "row2");
+
+ this.col1 = col1;
+ this.row1 = row1;
+ this.dx1 = x1;
+ this.dy1 = y1;
+ this.col2 = col2;
+ this.row2 = row2;
+ this.dx2 = x2;
+ this.dy2 = y2;
+ }
+
+ /**
+ * @return true if the anchor goes from right to left.
+ */
+ public boolean isHorizontallyFlipped()
+ {
+ if (col1 == col2)
+ return dx1 > dx2;
+ else
+ return col1 > col2;
+ }
+
+ /**
+ * @return true if the anchor goes from bottom to top.
+ */
+ public boolean isVerticallyFlipped()
+ {
+ if (row1 == row2)
+ return dy1 > dy2;
+ else
+ return row1 > row2;
+ }
+
+ private void checkRange( int value, int minRange, int maxRange, String varName )
+ {
+ if (value < minRange || value > maxRange)
+ throw new IllegalArgumentException(varName + " must be between " + minRange + " and " + maxRange);
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
index 016c0d42f1..1bc777fe8c 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java
@@ -386,4 +386,12 @@ public class HSSFFont
{
return font.getUnderline();
}
+
+ public String toString()
+ {
+ return "org.apache.poi.hssf.usermodel.HSSFFont{" +
+ font +
+ "}";
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
index 863533d455..d1eb55a08d 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java
@@ -110,7 +110,60 @@ public class HSSFPalette
}
return null;
}
-
+
+ /**
+ * Finds the closest matching color in the custom palette. The
+ * method for finding the distance between the colors is fairly
+ * primative.
+ *
+ * @param red The red component of the color to match.
+ * @param green The green component of the color to match.
+ * @param blue The blue component of the color to match.
+ * @return The closest color or null if there are no custom
+ * colors currently defined.
+ */
+ public HSSFColor findSimilarColor(byte red, byte green, byte blue)
+ {
+ HSSFColor result = null;
+ int minColorDistance = Integer.MAX_VALUE;
+ byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX);
+ for (short i = (short) PaletteRecord.FIRST_COLOR_INDEX; b != null;
+ b = palette.getColor(++i))
+ {
+ int colorDistance = red - b[0] + green - b[1] + blue - b[2];
+ if (colorDistance < minColorDistance)
+ {
+ result = getColor(i);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Adds a new color into an empty color slot.
+ * @param red The red component
+ * @param green The green component
+ * @param blue The blue component
+ *
+ * @return The new custom color.
+ *
+ * @throws RuntimeException if there are more more free color indexes.
+ */
+ public HSSFColor addColor( byte red, byte green, byte blue )
+ {
+ byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX);
+ short i;
+ for (i = (short) PaletteRecord.FIRST_COLOR_INDEX; i < PaletteRecord.STANDARD_PALETTE_SIZE + PaletteRecord.FIRST_COLOR_INDEX; b = palette.getColor(++i))
+ {
+ if (b == null)
+ {
+ setColorAtIndex( i, red, green, blue );
+ return getColor(i);
+ }
+ }
+ throw new RuntimeException("Could not find free color index");
+ }
+
/**
* Sets the color at the given offset
*
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
new file mode 100644
index 0000000000..d0a376c0ec
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java
@@ -0,0 +1,159 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The patriarch is the toplevel container for shapes in a sheet. It does
+ * little other than act as a container for other shapes and groups.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFPatriarch
+ implements HSSFShapeContainer
+{
+ List shapes = new ArrayList();
+ HSSFSheet sheet;
+ int x1 = 0;
+ int y1 = 0 ;
+ int x2 = 1023;
+ int y2 = 255;
+
+ /**
+ * Creates the patriarch.
+ *
+ * @param sheet the sheet this patriarch is stored in.
+ */
+ HSSFPatriarch(HSSFSheet sheet)
+ {
+ this.sheet = sheet;
+ }
+
+ /**
+ * Creates a new group record stored under this patriarch.
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created group.
+ */
+ public HSSFShapeGroup createGroup(HSSFClientAnchor anchor)
+ {
+ HSSFShapeGroup group = new HSSFShapeGroup(null, anchor);
+ group.anchor = anchor;
+ shapes.add(group);
+ return group;
+ }
+
+ /**
+ * Creates a simple shape. This includes such shapes as lines, rectangles,
+ * and ovals.
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created shape.
+ */
+ public HSSFSimpleShape createSimpleShape(HSSFClientAnchor anchor)
+ {
+ HSSFSimpleShape shape = new HSSFSimpleShape(null, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Creates a polygon
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created shape.
+ */
+ public HSSFPolygon createPolygon(HSSFClientAnchor anchor)
+ {
+ HSSFPolygon shape = new HSSFPolygon(null, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Constructs a textbox under the patriarch.
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created textbox.
+ */
+ public HSSFTextbox createTextbox(HSSFClientAnchor anchor)
+ {
+ HSSFTextbox shape = new HSSFTextbox(null, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Returns a list of all shapes contained by the patriarch.
+ */
+ public List getChildren()
+ {
+ return shapes;
+ }
+
+ /**
+ * Total count of all children and their children's children.
+ */
+ public int countOfAllChildren()
+ {
+ int count = shapes.size();
+ for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); )
+ {
+ HSSFShape shape = (HSSFShape) iterator.next();
+ count += shape.countOfAllChildren();
+ }
+ return count;
+ }
+ /**
+ * Sets the coordinate space of this group. All children are contrained
+ * to these coordinates.
+ */
+ public void setCoordinates( int x1, int y1, int x2, int y2 )
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+ /**
+ * The top left x coordinate of this group.
+ */
+ public int getX1()
+ {
+ return x1;
+ }
+
+ /**
+ * The top left y coordinate of this group.
+ */
+ public int getY1()
+ {
+ return y1;
+ }
+
+ /**
+ * The bottom right x coordinate of this group.
+ */
+ public int getX2()
+ {
+ return x2;
+ }
+
+ /**
+ * The bottom right y coordinate of this group.
+ */
+ public int getY2()
+ {
+ return y2;
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java
new file mode 100644
index 0000000000..67f4532658
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java
@@ -0,0 +1,66 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * @author Glen Stampoultzis (glens at superlinksoftware.com)
+ */
+public class HSSFPolygon
+ extends HSSFShape
+{
+ int[] xPoints;
+ int[] yPoints;
+ int drawAreaWidth = 100;
+ int drawAreaHeight = 100;
+
+ HSSFPolygon( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ }
+
+ public int[] getXPoints()
+ {
+ return xPoints;
+ }
+
+ public int[] getYPoints()
+ {
+ return yPoints;
+ }
+
+ public void setPoints(int[] xPoints, int[] yPoints)
+ {
+ this.xPoints = cloneArray(xPoints);
+ this.yPoints = cloneArray(yPoints);
+ }
+
+ private int[] cloneArray( int[] a )
+ {
+ int[] result = new int[a.length];
+ for ( int i = 0; i < a.length; i++ )
+ result[i] = a[i];
+
+ return result;
+ }
+
+ /**
+ * Defines the width and height of the points in the polygon
+ * @param width
+ * @param height
+ */
+ public void setPolygonDrawArea( int width, int height )
+ {
+ this.drawAreaWidth = width;
+ this.drawAreaHeight = height;
+ }
+
+ public int getDrawAreaWidth()
+ {
+ return drawAreaWidth;
+ }
+
+ public int getDrawAreaHeight()
+ {
+ return drawAreaHeight;
+ }
+
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java
new file mode 100644
index 0000000000..59edf61d64
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java
@@ -0,0 +1,179 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Rich text unicode string. These strings can have fonts applied to
+ * arbitary parts of the string.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFRichTextString
+ implements Comparable
+{
+ /** Place holder for indicating that NO_FONT has been applied here */
+ public static final short NO_FONT = -1;
+
+ String string;
+ SortedMap formattingRuns = new TreeMap();
+
+ public HSSFRichTextString()
+ {
+ this("");
+ }
+
+ public HSSFRichTextString( String string )
+ {
+ this.string = string;
+ this.formattingRuns.put(new Integer(0), new Short(NO_FONT));
+ }
+
+ /**
+ * Applies a font to the specified characters of a string.
+ *
+ * @param startIndex The start index to apply the font to (inclusive)
+ * @param endIndex The end index to apply the font to (exclusive)
+ * @param fontIndex The font to use.
+ */
+ public void applyFont(int startIndex, int endIndex, short fontIndex)
+ {
+ if (startIndex > endIndex)
+ throw new IllegalArgumentException("Start index must be less than end index.");
+ if (startIndex < 0 || endIndex > length())
+ throw new IllegalArgumentException("Start and end index not in range.");
+ if (startIndex == endIndex)
+ return;
+
+ Integer from = new Integer(startIndex);
+ Integer to = new Integer(endIndex);
+ short fontAtIndex = NO_FONT;
+ if (endIndex != length())
+ fontAtIndex = getFontAtIndex(endIndex);
+ formattingRuns.subMap(from, to).clear();
+ formattingRuns.put(from, new Short(fontIndex));
+ if (endIndex != length())
+ {
+ if (fontIndex != fontAtIndex)
+ formattingRuns.put(to, new Short(fontAtIndex));
+ }
+ }
+
+ /**
+ * Applies a font to the specified characters of a string.
+ *
+ * @param startIndex The start index to apply the font to (inclusive)
+ * @param endIndex The end index to apply to font to (exclusive)
+ * @param font The index of the font to use.
+ */
+ public void applyFont(int startIndex, int endIndex, HSSFFont font)
+ {
+ applyFont(startIndex, endIndex, font.getIndex());
+ }
+
+ /**
+ * Sets the font of the entire string.
+ * @param font The font to use.
+ */
+ public void applyFont(HSSFFont font)
+ {
+ applyFont(0, string.length(), font);
+ }
+
+ /**
+ * Returns the plain string representation.
+ */
+ public String getString()
+ {
+ return string;
+ }
+
+ /**
+ * @return the number of characters in the font.
+ */
+ public int length()
+ {
+ return string.length();
+ }
+
+ /**
+ * Returns the font in use at a particular index.
+ *
+ * @param index The index.
+ * @return The font that's currently being applied at that
+ * index or null if no font is being applied or the
+ * index is out of range.
+ */
+ public short getFontAtIndex( int index )
+ {
+ if (index < 0 || index >= string.length())
+ throw new ArrayIndexOutOfBoundsException("Font index " + index + " out of bounds of string");
+ Integer key = new Integer(index + 1);
+ SortedMap head = formattingRuns.headMap(key);
+ if (head.isEmpty())
+ throw new IllegalStateException("Should not reach here. No font found.");
+ else
+ return ((Short) head.get(head.lastKey())).shortValue();
+ }
+
+ /**
+ * @return The number of formatting runs used. There will always be at
+ * least one of font NO_FONT.
+ *
+ * @see #NO_FONT
+ */
+ public int numFormattingRuns()
+ {
+ return formattingRuns.size();
+ }
+
+ /**
+ * The index within the string to which the specified formatting run applies.
+ * @param index the index of the formatting run
+ * @return the index within the string.
+ */
+ public int getIndexOfFormattingRun(int index)
+ {
+ Map.Entry[] runs = (Map.Entry[]) formattingRuns.entrySet().toArray(new Map.Entry[formattingRuns.size()] );
+ return ((Integer)runs[index].getKey()).intValue();
+ }
+
+ /**
+ * Gets the font used in a particular formatting run.
+ *
+ * @param index the index of the formatting run
+ * @return the font number used.
+ */
+ public short getFontOfFormattingRun(int index)
+ {
+ Map.Entry[] runs = (Map.Entry[]) formattingRuns.entrySet().toArray(new Map.Entry[formattingRuns.size()] );
+ return ((Short)(runs[index].getValue())).shortValue();
+ }
+
+ /**
+ * Compares one rich text string to another.
+ */
+ public int compareTo( Object o )
+ {
+ return 0; // todo
+ }
+
+ /**
+ * @return the plain text representation of this string.
+ */
+ public String toString()
+ {
+ return string;
+ }
+
+ /**
+ * Applies the specified font to the entire string.
+ *
+ * @param fontIndex the font to apply.
+ */
+ public void applyFont( short fontIndex )
+ {
+ applyFont(0, string.length(), fontIndex);
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java
new file mode 100644
index 0000000000..61620f2351
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java
@@ -0,0 +1,195 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * An abstract shape.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public abstract class HSSFShape
+{
+ public static final int LINEWIDTH_ONE_PT = 12700;
+ public static final int LINEWIDTH_DEFAULT = 9525;
+
+ public static final int LINESTYLE_SOLID = 0; // Solid (continuous) pen
+ public static final int LINESTYLE_DASHSYS = 1; // PS_DASH system dash style
+ public static final int LINESTYLE_DOTSYS = 2; // PS_DOT system dash style
+ public static final int LINESTYLE_DASHDOTSYS = 3; // PS_DASHDOT system dash style
+ public static final int LINESTYLE_DASHDOTDOTSYS = 4; // PS_DASHDOTDOT system dash style
+ public static final int LINESTYLE_DOTGEL = 5; // square dot style
+ public static final int LINESTYLE_DASHGEL = 6; // dash style
+ public static final int LINESTYLE_LONGDASHGEL = 7; // long dash style
+ public static final int LINESTYLE_DASHDOTGEL = 8; // dash short dash
+ public static final int LINESTYLE_LONGDASHDOTGEL = 9; // long dash short dash
+ public static final int LINESTYLE_LONGDASHDOTDOTGEL = 10; // long dash short dash short dash
+ public static final int LINESTYLE_NONE = -1;
+
+ HSSFShape parent;
+ HSSFAnchor anchor;
+ int lineStyleColor = 0x08000040;
+ int fillColor = 0x08000009;
+ int lineWidth = LINEWIDTH_DEFAULT; // 12700 = 1pt
+ int lineStyle = LINESTYLE_SOLID;
+ boolean noFill = false;
+
+ /**
+ * Create a new shape with the specified parent and anchor.
+ */
+ HSSFShape( HSSFShape parent, HSSFAnchor anchor )
+ {
+ this.parent = parent;
+ this.anchor = anchor;
+ }
+
+ /**
+ * Gets the parent shape.
+ */
+ public HSSFShape getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * @return the anchor that is used by this shape.
+ */
+ public HSSFAnchor getAnchor()
+ {
+ return anchor;
+ }
+
+ /**
+ * Sets a particular anchor. A top-level shape must have an anchor of
+ * HSSFClientAnchor. A child anchor must have an anchor of HSSFChildAnchor
+ *
+ * @param anchor the anchor to use.
+ * @throws IllegalArgumentException when the wrong anchor is used for
+ * this particular shape.
+ *
+ * @see HSSFChildAnchor
+ * @see HSSFClientAnchor
+ */
+ public void setAnchor( HSSFAnchor anchor )
+ {
+ if ( parent == null )
+ {
+ if ( anchor instanceof HSSFChildAnchor )
+ throw new IllegalArgumentException( "Must use client anchors for shapes directly attached to sheet." );
+ }
+ else
+ {
+ if ( anchor instanceof HSSFClientAnchor )
+ throw new IllegalArgumentException( "Must use child anchors for shapes attached to groups." );
+ }
+
+ this.anchor = anchor;
+ }
+
+ /**
+ * The color applied to the lines of this shape.
+ */
+ public int getLineStyleColor()
+ {
+ return lineStyleColor;
+ }
+
+ /**
+ * The color applied to the lines of this shape.
+ */
+ public void setLineStyleColor( int lineStyleColor )
+ {
+ this.lineStyleColor = lineStyleColor;
+ }
+
+ /**
+ * The color applied to the lines of this shape.
+ */
+ public void setLineStyleColor( int red, int green, int blue )
+ {
+ this.lineStyleColor = ((blue) << 16) | ((green) << 8) | red;
+ }
+
+ /**
+ * The color used to fill this shape.
+ */
+ public int getFillColor()
+ {
+ return fillColor;
+ }
+
+ /**
+ * The color used to fill this shape.
+ */
+ public void setFillColor( int fillColor )
+ {
+ this.fillColor = fillColor;
+ }
+
+ /**
+ * The color used to fill this shape.
+ */
+ public void setFillColor( int red, int green, int blue )
+ {
+ this.fillColor = ((blue) << 16) | ((green) << 8) | red;
+ }
+
+ /**
+ * @return returns with width of the line in EMUs. 12700 = 1 pt.
+ */
+ public int getLineWidth()
+ {
+ return lineWidth;
+ }
+
+ /**
+ * Sets the width of the line. 12700 = 1 pt.
+ *
+ * @param lineWidth width in EMU's. 12700EMU's = 1 pt
+ *
+ * @see HSSFShape#LINEWIDTH_ONE_PT
+ */
+ public void setLineWidth( int lineWidth )
+ {
+ this.lineWidth = lineWidth;
+ }
+
+ /**
+ * @return One of the constants in LINESTYLE_*
+ */
+ public int getLineStyle()
+ {
+ return lineStyle;
+ }
+
+ /**
+ * Sets the line style.
+ *
+ * @param lineStyle One of the constants in LINESTYLE_*
+ */
+ public void setLineStyle( int lineStyle )
+ {
+ this.lineStyle = lineStyle;
+ }
+
+ /**
+ * @return true if this shape is not filled with a color.
+ */
+ public boolean isNoFill()
+ {
+ return noFill;
+ }
+
+ /**
+ * Sets whether this shape is filled or transparent.
+ */
+ public void setNoFill( boolean noFill )
+ {
+ this.noFill = noFill;
+ }
+
+ /**
+ * Count of all children and their childrens children.
+ */
+ public int countOfAllChildren()
+ {
+ return 1;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java
new file mode 100644
index 0000000000..f641fe9c0e
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java
@@ -0,0 +1,17 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.List;
+
+/**
+ * An interface that indicates whether a class can contain children.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public interface HSSFShapeContainer
+{
+ /**
+ * @return Any children contained by this shape.
+ */
+ List getChildren();
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java
new file mode 100644
index 0000000000..c33c8c6d34
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java
@@ -0,0 +1,148 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * A shape group may contain other shapes. It was no actual form on the
+ * sheet.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFShapeGroup
+ extends HSSFShape
+ implements HSSFShapeContainer
+{
+ List shapes = new ArrayList();
+ int x1 = 0;
+ int y1 = 0 ;
+ int x2 = 1023;
+ int y2 = 255;
+
+
+ public HSSFShapeGroup( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ }
+
+ /**
+ * Create another group under this group.
+ * @param anchor the position of the new group.
+ * @return the group
+ */
+ public HSSFShapeGroup createGroup(HSSFChildAnchor anchor)
+ {
+ HSSFShapeGroup group = new HSSFShapeGroup(this, anchor);
+ group.anchor = anchor;
+ shapes.add(group);
+ return group;
+ }
+
+ /**
+ * Create a new simple shape under this group.
+ * @param anchor the position of the shape.
+ * @return the shape
+ */
+ public HSSFSimpleShape createShape(HSSFChildAnchor anchor)
+ {
+ HSSFSimpleShape shape = new HSSFSimpleShape(this, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Create a new textbox under this group.
+ * @param anchor the position of the shape.
+ * @return the textbox
+ */
+ public HSSFTextbox createTextbox(HSSFChildAnchor anchor)
+ {
+ HSSFTextbox shape = new HSSFTextbox(this, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Creates a polygon
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created shape.
+ */
+ public HSSFPolygon createPolygon(HSSFChildAnchor anchor)
+ {
+ HSSFPolygon shape = new HSSFPolygon(this, anchor);
+ shape.anchor = anchor;
+ shapes.add(shape);
+ return shape;
+ }
+
+ /**
+ * Return all children contained by this shape.
+ */
+ public List getChildren()
+ {
+ return shapes;
+ }
+
+ /**
+ * Sets the coordinate space of this group. All children are contrained
+ * to these coordinates.
+ */
+ public void setCoordinates( int x1, int y1, int x2, int y2 )
+ {
+ this.x1 = x1;
+ this.y1 = y1;
+ this.x2 = x2;
+ this.y2 = y2;
+ }
+
+ /**
+ * The top left x coordinate of this group.
+ */
+ public int getX1()
+ {
+ return x1;
+ }
+
+ /**
+ * The top left y coordinate of this group.
+ */
+ public int getY1()
+ {
+ return y1;
+ }
+
+ /**
+ * The bottom right x coordinate of this group.
+ */
+ public int getX2()
+ {
+ return x2;
+ }
+
+ /**
+ * The bottom right y coordinate of this group.
+ */
+ public int getY2()
+ {
+ return y2;
+ }
+
+ /**
+ * Count of all children and their childrens children.
+ */
+ public int countOfAllChildren()
+ {
+ int count = shapes.size();
+ for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); )
+ {
+ HSSFShape shape = (HSSFShape) iterator.next();
+ count += shape.countOfAllChildren();
+ }
+ return count;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index c0fcfe0aa8..a48c87d43f 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -62,19 +62,15 @@ package org.apache.poi.hssf.usermodel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.io.PrintWriter;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
-import org.apache.poi.hssf.record.CellValueRecordInterface;
-import org.apache.poi.hssf.record.HCenterRecord;
-import org.apache.poi.hssf.record.RowRecord;
-import org.apache.poi.hssf.record.SCLRecord;
-import org.apache.poi.hssf.record.VCenterRecord;
-import org.apache.poi.hssf.record.WSBoolRecord;
-import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.util.Region;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
+import org.apache.poi.ddf.EscherRecord;
/**
* High level representation of a worksheet.
@@ -979,6 +975,9 @@ public class HSSFSheet
* <p>
* Additionally shifts merged regions that are completely defined in these
* rows (ie. merged 2 cells on a row to be shifted).
+ * <p>
+ * TODO Might want to add bounds checking here
+ *
* @param startRow the row to start shifting
* @param endRow the row to end shifting
* @param n the number of rows to shift
@@ -1001,8 +1000,9 @@ public class HSSFSheet
inc = -1;
}
- shiftMerged(startRow, endRow, n, true);
-
+ shiftMerged(startRow, endRow, n, true);
+ sheet.shiftRowBreaks(startRow, endRow, n);
+
for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc )
{
HSSFRow row = getRow( rowNum );
@@ -1146,6 +1146,149 @@ public class HSSFSheet
public boolean isDisplayRowColHeadings() {
return sheet.isDisplayRowColHeadings();
}
+
+ /**
+ * Sets a page break at the indicated row
+ * @param row
+ */
+ public void setRowBreak(int row) {
+ validateRow(row);
+ sheet.setRowBreak(row, (short)0, (short)255);
+ }
+
+ /**
+ * Determines if there is a page break at the indicated row
+ * @param row
+ * @return
+ */
+ public boolean isRowBroken(int row) {
+ return sheet.isRowBroken(row);
+ }
+
+ /**
+ * Removes the page break at the indicated row
+ * @param row
+ */
+ public void removeRowBreak(int row) {
+ sheet.removeRowBreak(row);
+ }
+
+ /**
+ * Retrieves all the horizontal page breaks
+ * @return
+ */
+ public int[] getRowBreaks(){
+ //we can probably cache this information, but this should be a sparsely used function
+ int[] returnValue = new int[sheet.getNumRowBreaks()];
+ Iterator iterator = sheet.getRowBreaks();
+ int i = 0;
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ returnValue[i++] = (int)breakItem.main;
+ }
+ return returnValue;
+ }
+
+ /**
+ * Retrieves all the vertical page breaks
+ * @return
+ */
+ public short[] getColumnBreaks(){
+ //we can probably cache this information, but this should be a sparsely used function
+ short[] returnValue = new short[sheet.getNumColumnBreaks()];
+ Iterator iterator = sheet.getColumnBreaks();
+ int i = 0;
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ returnValue[i++] = breakItem.main;
+ }
+ return returnValue;
+ }
+
+
+ /**
+ * Sets a page break at the indicated column
+ * @param column
+ */
+ public void setColumnBreak(short column) {
+ validateColumn(column);
+ sheet.setColumnBreak(column, (short)0, (short)65535);
+ }
+
+ /**
+ * Determines if there is a page break at the indicated column
+ * @param column
+ * @return
+ */
+ public boolean isColumnBroken(short column) {
+ return sheet.isColumnBroken(column);
+ }
+
+ /**
+ * Removes a page break at the indicated column
+ * @param column
+ */
+ public void removeColumnBreak(short column) {
+ sheet.removeColumnBreak(column);
+ }
+
+ /**
+ * Runs a bounds check for row numbers
+ * @param row
+ */
+ protected void validateRow(int row) {
+ if (row > 65535) throw new IllegalArgumentException("Maximum row number is 65535");
+ if (row < 0) throw new IllegalArgumentException("Minumum row number is 0");
+ }
+
+ /**
+ * Runs a bounds check for column numbers
+ * @param column
+ */
+ protected void validateColumn(short column) {
+ if (column > 255) throw new IllegalArgumentException("Maximum column number is 255");
+ if (column < 0) throw new IllegalArgumentException("Minimum column number is 0");
+ }
+
+ /**
+ * Aggregates the drawing records and dumps the escher record hierarchy
+ * to the standard output.
+ */
+ public void dumpDrawingRecords()
+ {
+ sheet.aggregateDrawingRecords(book.getDrawingManager());
+
+ EscherAggregate r = (EscherAggregate) getSheet().findFirstRecordBySid(EscherAggregate.sid);
+ List escherRecords = r.getEscherRecords();
+ for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
+ {
+ EscherRecord escherRecord = (EscherRecord) iterator.next();
+ PrintWriter w = new PrintWriter(System.out);
+ escherRecord.display(w, 0);
+ w.close();
+ }
+ }
+
+ /**
+ * Creates the toplevel drawing patriarch. This will have the effect of
+ * removing any existing drawings on this sheet.
+ *
+ * @return The new patriarch.
+ */
+ public HSSFPatriarch createDrawingPatriarch()
+ {
+ // Create the drawing group if it doesn't already exist.
+ book.createDrawingGroup();
+
+ sheet.aggregateDrawingRecords(book.getDrawingManager());
+ EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
+ HSSFPatriarch patriarch = new HSSFPatriarch(this);
+ agg.clear(); // Initially the behaviour will be to clear out any existing shapes in the sheet when
+ // creating a new patriarch.
+ agg.setPatriarch(patriarch);
+ return patriarch;
+ }
+
}
class SheetRowIterator implements Iterator {
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java
new file mode 100644
index 0000000000..c90fe6bb8c
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java
@@ -0,0 +1,64 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * Represents a simple shape such as a line, rectangle or oval.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFSimpleShape
+ extends HSSFShape
+{
+ // The commented out ones haven't been tested yet or aren't supported
+ // by HSSFSimpleShape.
+
+ public final static short OBJECT_TYPE_LINE = 1;
+ public final static short OBJECT_TYPE_RECTANGLE = 2;
+ public final static short OBJECT_TYPE_OVAL = 3;
+// public final static short OBJECT_TYPE_ARC = 4;
+// public final static short OBJECT_TYPE_CHART = 5;
+// public final static short OBJECT_TYPE_TEXT = 6;
+// public final static short OBJECT_TYPE_BUTTON = 7;
+// public final static short OBJECT_TYPE_PICTURE = 8;
+// public final static short OBJECT_TYPE_POLYGON = 9;
+// public final static short OBJECT_TYPE_CHECKBOX = 11;
+// public final static short OBJECT_TYPE_OPTION_BUTTON = 12;
+// public final static short OBJECT_TYPE_EDIT_BOX = 13;
+// public final static short OBJECT_TYPE_LABEL = 14;
+// public final static short OBJECT_TYPE_DIALOG_BOX = 15;
+// public final static short OBJECT_TYPE_SPINNER = 16;
+// public final static short OBJECT_TYPE_SCROLL_BAR = 17;
+// public final static short OBJECT_TYPE_LIST_BOX = 18;
+// public final static short OBJECT_TYPE_GROUP_BOX = 19;
+// public final static short OBJECT_TYPE_COMBO_BOX = 20;
+// public final static short OBJECT_TYPE_COMMENT = 25;
+// public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30;
+
+ int shapeType = OBJECT_TYPE_LINE;
+
+ HSSFSimpleShape( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ }
+
+ /**
+ * Gets the shape type.
+ * @return One of the OBJECT_TYPE_* constants.
+ *
+ * @see #OBJECT_TYPE_LINE
+ * @see #OBJECT_TYPE_OVAL
+ * @see #OBJECT_TYPE_RECTANGLE
+ */
+ public int getShapeType() { return shapeType; }
+
+ /**
+ * Sets the shape types.
+ *
+ * @param shapeType One of the OBJECT_TYPE_* constants.
+ *
+ * @see #OBJECT_TYPE_LINE
+ * @see #OBJECT_TYPE_OVAL
+ * @see #OBJECT_TYPE_RECTANGLE
+ */
+ public void setShapeType( int shapeType ){ this.shapeType = shapeType; }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java b/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java
new file mode 100644
index 0000000000..423e256fac
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java
@@ -0,0 +1,107 @@
+package org.apache.poi.hssf.usermodel;
+
+/**
+ * A textbox is a shape that may hold a rich text string.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class HSSFTextbox
+ extends HSSFSimpleShape
+{
+ public final static short OBJECT_TYPE_TEXT = 6;
+
+ int marginLeft, marginRight, marginTop, marginBottom;
+
+ HSSFRichTextString string = new HSSFRichTextString("");
+
+ /**
+ * Construct a new textbox with the given parent and anchor.
+ * @param parent
+ * @param anchor One of HSSFClientAnchor or HSSFChildAnchor
+ */
+ public HSSFTextbox( HSSFShape parent, HSSFAnchor anchor )
+ {
+ super( parent, anchor );
+ setShapeType(OBJECT_TYPE_TEXT);
+ }
+
+ /**
+ * @return the rich text string for this textbox.
+ */
+ public HSSFRichTextString getString()
+ {
+ return string;
+ }
+
+ /**
+ * @param string Sets the rich text string used by this object.
+ */
+ public void setString( HSSFRichTextString string )
+ {
+ this.string = string;
+ }
+
+ /**
+ * @return Returns the left margin within the textbox.
+ */
+ public int getMarginLeft()
+ {
+ return marginLeft;
+ }
+
+ /**
+ * Sets the left margin within the textbox.
+ */
+ public void setMarginLeft( int marginLeft )
+ {
+ this.marginLeft = marginLeft;
+ }
+
+ /**
+ * @return returns the right margin within the textbox.
+ */
+ public int getMarginRight()
+ {
+ return marginRight;
+ }
+
+ /**
+ * Sets the right margin within the textbox.
+ */
+ public void setMarginRight( int marginRight )
+ {
+ this.marginRight = marginRight;
+ }
+
+ /**
+ * @return returns the top margin within the textbox.
+ */
+ public int getMarginTop()
+ {
+ return marginTop;
+ }
+
+ /**
+ * Sets the top margin within the textbox.
+ */
+ public void setMarginTop( int marginTop )
+ {
+ this.marginTop = marginTop;
+ }
+
+ /**
+ * Gets the bottom margin within the textbox.
+ */
+ public int getMarginBottom()
+ {
+ return marginBottom;
+ }
+
+ /**
+ * Sets the bottom margin within the textbox.
+ */
+ public void setMarginBottom( int marginBottom )
+ {
+ this.marginBottom = marginBottom;
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index 02bdc81167..9fa70e7471 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -653,6 +653,40 @@ public class HSSFWorkbook
}
/**
+ * Finds a font that matches the one with the supplied attributes
+ */
+ public HSSFFont findFont(short boldWeight, short color, short fontHeight,
+ String name, boolean italic, boolean strikeout,
+ short typeOffset, byte underline)
+ {
+// System.out.println( boldWeight + ", " + color + ", " + fontHeight + ", " + name + ", " + italic + ", " + strikeout + ", " + typeOffset + ", " + underline );
+ for (short i = 0; i < workbook.getNumberOfFontRecords(); i++)
+ {
+ if (i == 4)
+ continue;
+
+ FontRecord font = workbook.getFontRecordAt(i);
+ HSSFFont hssfFont = new HSSFFont(i, font);
+// System.out.println( hssfFont.getBoldweight() + ", " + hssfFont.getColor() + ", " + hssfFont.getFontHeight() + ", " + hssfFont.getFontName() + ", " + hssfFont.getItalic() + ", " + hssfFont.getStrikeout() + ", " + hssfFont.getTypeOffset() + ", " + hssfFont.getUnderline() );
+ if (hssfFont.getBoldweight() == boldWeight
+ && hssfFont.getColor() == color
+ && hssfFont.getFontHeight() == fontHeight
+ && hssfFont.getFontName().equals(name)
+ && hssfFont.getItalic() == italic
+ && hssfFont.getStrikeout() == strikeout
+ && hssfFont.getTypeOffset() == typeOffset
+ && hssfFont.getUnderline() == underline)
+ {
+// System.out.println( "Found font" );
+ return hssfFont;
+ }
+ }
+
+// System.out.println( "No font found" );
+ return null;
+ }
+
+ /**
* get the number of fonts in the font table
* @return number of fonts
*/
diff --git a/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java b/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java
new file mode 100644
index 0000000000..1c7b330cc3
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java
@@ -0,0 +1,81 @@
+package org.apache.poi.hssf.usermodel;
+
+import java.util.*;
+import java.awt.*;
+import java.io.*;
+
+/**
+ * Allows the user to lookup the font metrics for a particular font without
+ * actually having the font on the system. The font details are loaded
+ * as a resource from the POI jar file (or classpath) and should be contained
+ * in path "/font_metrics.properties". The font widths are for a 10 point
+ * version of the font. Use a multiplier for other sizes.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+class StaticFontMetrics
+{
+ private static Properties fontMetricsProps;
+ private static Map fontDetailsMap = new HashMap();
+
+ /**
+ * Retrieves the fake font details for a given font.
+ * @param font the font to lookup.
+ * @return the fake font.
+ */
+ public static FontDetails getFontDetails(Font font)
+ {
+ if (fontMetricsProps == null)
+ {
+ InputStream metricsIn = null;
+ try
+ {
+ fontMetricsProps = new Properties();
+ if (System.getProperty("font.metrics.filename") != null)
+ {
+ String filename = System.getProperty("font.metrics.filename");
+ File file = new File(filename);
+ if (!file.exists())
+ throw new FileNotFoundException("font_metrics.properties not found at path " + file.getAbsolutePath());
+ metricsIn = new FileInputStream(file);
+ }
+ else
+ {
+ metricsIn = FontDetails.class.getResourceAsStream("/font_metrics.properties");
+ if (metricsIn == null)
+ throw new FileNotFoundException("font_metrics.properties not found in classpath");
+ }
+ fontMetricsProps.load(metricsIn);
+ }
+ catch ( IOException e )
+ {
+ throw new RuntimeException("Could not load font metrics: " + e.getMessage());
+ }
+ finally
+ {
+ if (metricsIn != null)
+ {
+ try
+ {
+ metricsIn.close();
+ }
+ catch ( IOException ignore ) { }
+ }
+ }
+ }
+
+ String fontName = font.getName();
+
+ if (fontDetailsMap.get(fontName) == null)
+ {
+ FontDetails fontDetails = FontDetails.create(fontName, fontMetricsProps);
+ fontDetailsMap.put( fontName, fontDetails );
+ return fontDetails;
+ }
+ else
+ {
+ return (FontDetails) fontDetailsMap.get(fontName);
+ }
+
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics.java b/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics.java
new file mode 100644
index 0000000000..336b961f42
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics.java
@@ -0,0 +1,71 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+import java.awt.*;
+
+/**
+ * Tests the capabilities of the EscherGraphics class.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestEscherGraphics extends TestCase
+{
+ private HSSFShapeGroup escherGroup;
+ private EscherGraphics graphics;
+
+ protected void setUp() throws Exception
+ {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("test");
+ escherGroup = sheet.createDrawingPatriarch().createGroup(new HSSFClientAnchor(0,0,1023,255,(short)0,0,(short) 0,0));
+ escherGroup = new HSSFShapeGroup(null, new HSSFChildAnchor());
+ graphics = new EscherGraphics(this.escherGroup, workbook, Color.black, 1.0f);
+ super.setUp();
+ }
+
+ public void testGetFont() throws Exception
+ {
+ Font f = graphics.getFont();
+ assertEquals("java.awt.Font[family=Arial,name=Arial,style=plain,size=10]", f.toString());
+ }
+
+ public void testGetFontMetrics() throws Exception
+ {
+ FontMetrics fontMetrics = graphics.getFontMetrics(graphics.getFont());
+ assertEquals(7, fontMetrics.charWidth('X'));
+ assertEquals("java.awt.Font[family=Arial,name=Arial,style=plain,size=10]", fontMetrics.getFont().toString());
+ }
+
+ public void testSetFont() throws Exception
+ {
+ Font f = new Font("Helvetica", 0, 12);
+ graphics.setFont(f);
+ assertEquals(f, graphics.getFont());
+ }
+
+ public void testSetColor() throws Exception
+ {
+ graphics.setColor(Color.red);
+ assertEquals(Color.red, graphics.getColor());
+ }
+
+ public void testFillRect() throws Exception
+ {
+ graphics.fillRect( 10, 10, 20, 20 );
+ HSSFSimpleShape s = (HSSFSimpleShape) escherGroup.getChildren().get(0);
+ assertEquals(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE, s.getShapeType());
+ assertEquals(10, s.getAnchor().getDx1());
+ assertEquals(10, s.getAnchor().getDy1());
+ assertEquals(30, s.getAnchor().getDy2());
+ assertEquals(30, s.getAnchor().getDx2());
+ }
+
+ public void testDrawString() throws Exception
+ {
+ graphics.drawString("This is a test", 10, 10);
+ HSSFTextbox t = (HSSFTextbox) escherGroup.getChildren().get(0);
+ assertEquals("This is a test", t.getString().toString());
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics2d.java
new file mode 100644
index 0000000000..22f0e28c45
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestEscherGraphics2d.java
@@ -0,0 +1,74 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+import java.awt.*;
+import java.io.FileOutputStream;
+
+/**
+ * Tests the Graphics2d drawing capability.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestEscherGraphics2d extends TestCase
+{
+ private HSSFShapeGroup escherGroup;
+ private EscherGraphics2d graphics;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("test");
+ escherGroup = sheet.createDrawingPatriarch().createGroup(new HSSFClientAnchor(0,0,1023,255,(short)0,0,(short) 0,0));
+ escherGroup = new HSSFShapeGroup(null, new HSSFChildAnchor());
+ EscherGraphics g = new EscherGraphics(this.escherGroup, workbook, Color.black, 1.0f);
+ graphics = new EscherGraphics2d(g);
+
+ }
+
+ public void testDrawString() throws Exception
+ {
+ graphics.drawString("This is a test", 10, 10);
+ HSSFTextbox t = (HSSFTextbox) escherGroup.getChildren().get(0);
+ assertEquals("This is a test", t.getString().toString());
+ }
+
+ public void testFillRect() throws Exception
+ {
+ graphics.fillRect( 10, 10, 20, 20 );
+ HSSFSimpleShape s = (HSSFSimpleShape) escherGroup.getChildren().get(0);
+ assertEquals(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE, s.getShapeType());
+ assertEquals(10, s.getAnchor().getDx1());
+ assertEquals(10, s.getAnchor().getDy1());
+ assertEquals(30, s.getAnchor().getDy2());
+ assertEquals(30, s.getAnchor().getDx2());
+ }
+
+ public void testGetFontMetrics() throws Exception
+ {
+ FontMetrics fontMetrics = graphics.getFontMetrics(graphics.getFont());
+ assertEquals(7, fontMetrics.charWidth('X'));
+ assertEquals("java.awt.Font[family=Arial,name=Arial,style=plain,size=10]", fontMetrics.getFont().toString());
+ }
+
+ public void testSetFont() throws Exception
+ {
+ Font f = new Font("Helvetica", 0, 12);
+ graphics.setFont(f);
+ assertEquals(f, graphics.getFont());
+ }
+
+ public void testSetColor() throws Exception
+ {
+ graphics.setColor(Color.red);
+ assertEquals(Color.red, graphics.getColor());
+ }
+
+ public void testGetFont() throws Exception
+ {
+ Font f = graphics.getFont();
+ assertEquals("java.awt.Font[family=Arial,name=Arial,style=plain,size=10]", f.toString());
+ }
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestFontDetails.java b/src/java/org/apache/poi/hssf/usermodel/TestFontDetails.java
new file mode 100644
index 0000000000..532e62568b
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestFontDetails.java
@@ -0,0 +1,45 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+import java.util.Properties;
+
+/**
+ * Tests the implementation of the FontDetails class.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestFontDetails extends TestCase
+{
+ private Properties properties;
+ private FontDetails fontDetails;
+
+ protected void setUp() throws Exception
+ {
+ properties = new Properties();
+ properties.setProperty("font.Arial.height", "13");
+ properties.setProperty("font.Arial.characters", "a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ");
+ properties.setProperty("font.Arial.widths", "6, 6, 6, 6, 6, 3, 6, 6, 3, 4, 6, 3, 9, 6, 6, 6, 6, 4, 6, 3, 6, 7, 9, 6, 5, 5, 7, 7, 7, 7, 7, 6, 8, 7, 3, 6, 7, 6, 9, 7, 8, 7, 8, 7, 7, 5, 7, 7, 9, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ");
+ fontDetails = FontDetails.create("Arial", properties);
+
+ }
+
+ public void testCreate() throws Exception
+ {
+ assertEquals(13, fontDetails.getHeight());
+ assertEquals(6, fontDetails.getCharWidth('a'));
+ assertEquals(3, fontDetails.getCharWidth('f'));
+ }
+
+ public void testGetStringWidth() throws Exception
+ {
+ assertEquals(9, fontDetails.getStringWidth("af"));
+ }
+
+ public void testGetCharWidth() throws Exception
+ {
+ assertEquals(6, fontDetails.getCharWidth('a'));
+ assertEquals(9, fontDetails.getCharWidth('='));
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestHSSFClientAnchor.java b/src/java/org/apache/poi/hssf/usermodel/TestHSSFClientAnchor.java
new file mode 100644
index 0000000000..ab3e06a3d6
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestHSSFClientAnchor.java
@@ -0,0 +1,44 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+/**
+ * Various tests for HSSFClientAnchor.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class TestHSSFClientAnchor extends TestCase
+{
+ public void testGetAnchorHeightInPoints() throws Exception
+ {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet("test");
+ HSSFClientAnchor a = new HSSFClientAnchor(0,0,1023,255,(short)0,0,(short)0,0);
+ float p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(11.953,p,0.001);
+
+ sheet.createRow(0).setHeightInPoints(14);
+ a = new HSSFClientAnchor(0,0,1023,255,(short)0,0,(short)0,0);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(13.945,p,0.001);
+
+ a = new HSSFClientAnchor(0,0,1023,127,(short)0,0,(short)0,0);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(6.945,p,0.001);
+
+ a = new HSSFClientAnchor(0,126,1023,127,(short)0,0,(short)0,0);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(0.054,p,0.001);
+
+ a = new HSSFClientAnchor(0,0,1023,0,(short)0,0,(short)0,1);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(14.0,p,0.001);
+
+ sheet.createRow(0).setHeightInPoints(12);
+ a = new HSSFClientAnchor(0,127,1023,127,(short)0,0,(short)0,1);
+ p = a.getAnchorHeightInPoints(sheet);
+ assertEquals(12.0,p,0.001);
+
+ }
+
+}
diff --git a/src/java/org/apache/poi/hssf/usermodel/TestHSSFRichTextString.java b/src/java/org/apache/poi/hssf/usermodel/TestHSSFRichTextString.java
new file mode 100644
index 0000000000..69dcb78e7e
--- /dev/null
+++ b/src/java/org/apache/poi/hssf/usermodel/TestHSSFRichTextString.java
@@ -0,0 +1,50 @@
+package org.apache.poi.hssf.usermodel;
+
+import junit.framework.TestCase;
+
+public class TestHSSFRichTextString extends TestCase
+{
+ public void testApplyFont() throws Exception
+ {
+
+ HSSFRichTextString r = new HSSFRichTextString("testing");
+ assertEquals(1,r.numFormattingRuns());
+ r.applyFont(2,4, new HSSFFont((short)1, null));
+ assertEquals(3,r.numFormattingRuns());
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(0));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(1));
+ assertEquals(1, r.getFontAtIndex(2));
+ assertEquals(1, r.getFontAtIndex(3));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(4));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(5));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(6));
+
+ r.applyFont(6,7, new HSSFFont((short)2, null));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(0));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(1));
+ assertEquals(1, r.getFontAtIndex(2));
+ assertEquals(1, r.getFontAtIndex(3));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(4));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(5));
+ assertEquals(2, r.getFontAtIndex(6));
+
+ r.applyFont(HSSFRichTextString.NO_FONT);
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(0));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(1));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(2));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(3));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(4));
+ assertEquals(HSSFRichTextString.NO_FONT, r.getFontAtIndex(5));
+
+ r.applyFont(new HSSFFont((short)1, null));
+ assertEquals(1, r.getFontAtIndex(0));
+ assertEquals(1, r.getFontAtIndex(1));
+ assertEquals(1, r.getFontAtIndex(2));
+ assertEquals(1, r.getFontAtIndex(3));
+ assertEquals(1, r.getFontAtIndex(4));
+ assertEquals(1, r.getFontAtIndex(5));
+ assertEquals(1, r.getFontAtIndex(6));
+
+ }
+
+}
diff --git a/src/java/org/apache/poi/util/DrawingDump.java b/src/java/org/apache/poi/util/DrawingDump.java
new file mode 100644
index 0000000000..3c62f67368
--- /dev/null
+++ b/src/java/org/apache/poi/util/DrawingDump.java
@@ -0,0 +1,24 @@
+package org.apache.poi.util;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Dump out the aggregated escher records
+ */
+public class DrawingDump
+{
+ public static void main( String[] args ) throws IOException
+ {
+ POIFSFileSystem fs =
+ new POIFSFileSystem(new FileInputStream(args[0]));
+ HSSFWorkbook wb = new HSSFWorkbook(fs);
+ HSSFSheet sheet = wb.getSheetAt(0);
+ sheet.dumpDrawingRecords();
+ }
+}
diff --git a/src/records/definitions/common_object_data_record.xml b/src/records/definitions/common_object_data_record.xml
new file mode 100644
index 0000000000..5e5046206d
--- /dev/null
+++ b/src/records/definitions/common_object_data_record.xml
@@ -0,0 +1,52 @@
+<record id="0x15" name="CommonObjectData" excel-record-id="ftCmo"
+ package="org.apache.poi.hssf.record">
+ <suffix>SubRecord</suffix>
+ <extends>SubRecord</extends>
+ <description>The common object data record is used to store all common preferences for an excel object.</description>
+ <author>Glen Stampoultzis (glens at apache.org)</author>
+ <fields>
+ <field type="int" size="2" name="object type">
+ <const value="0" name="group" description="group"/>
+ <const value="1" name="line" description="line"/>
+ <const value="2" name="rectangle" description="rectangle"/>
+ <const value="3" name="oval" description="oval"/>
+ <const value="4" name="arc" description="arc"/>
+ <const value="5" name="chart" description="chart"/>
+ <const value="6" name="text" description="text"/>
+ <const value="7" name="button" description="button"/>
+ <const value="8" name="picture" description="picture"/>
+ <const value="9" name="polygon" description="polygon"/>
+ <const value="10" name="reserved1" description="reserved1"/>
+ <const value="11" name="checkbox" description="checkbox"/>
+ <const value="12" name="option button" description="option button"/>
+ <const value="13" name="edit box" description="edit box"/>
+ <const value="14" name="label" description="label"/>
+ <const value="15" name="dialog box" description="dialog box"/>
+ <const value="16" name="spinner" description="spinner"/>
+ <const value="17" name="scroll bar" description="scroll bar"/>
+ <const value="18" name="list box" description="list box"/>
+ <const value="19" name="group box" description="group box"/>
+ <const value="20" name="combo box" description="combo box"/>
+ <const value="21" name="reserved2" description="reserved2"/>
+ <const value="22" name="reserved3" description="reserved3"/>
+ <const value="23" name="reserved4" description="reserved4"/>
+ <const value="24" name="reserved5" description="reserved5"/>
+ <const value="25" name="comment" description="comment"/>
+ <const value="26" name="reserved6" description="reserved6"/>
+ <const value="27" name="reserved7" description="reserved7"/>
+ <const value="28" name="reserved8" description="reserved8"/>
+ <const value="29" name="reserved9" description="reserved9"/>
+ <const value="30" name="microsoft office drawing" description="microsoft office drawing"/>
+ </field>
+ <field type="int" size="2" name="object id"/>
+ <field type="int" size="2" name="option">
+ <bit number="0" name="locked" description="true if object is locked when sheet has been protected"/>
+ <bit number="4" name="printable" description="object appears when printed"/>
+ <bit number="13" name="autofill" description="whether object uses an automatic fill style"/>
+ <bit number="14" name="autoline" description="whether object uses an automatic line style"/>
+ </field>
+ <field type="int" size="4" name="reserved1" description="Should always be 0"/>
+ <field type="int" size="4" name="reserved2" description="Should always be 0"/>
+ <field type="int" size="4" name="reserved3" description="Should always be 0"/>
+ </fields>
+</record>
diff --git a/src/records/definitions/ftend_record.xml b/src/records/definitions/ftend_record.xml
new file mode 100644
index 0000000000..d516890e3c
--- /dev/null
+++ b/src/records/definitions/ftend_record.xml
@@ -0,0 +1,9 @@
+<record id="0x00" name="End" excel-record-id="ftEnd"
+ package="org.apache.poi.hssf.record">
+ <suffix>SubRecord</suffix>
+ <extends>SubRecord</extends>
+ <description>The end data record is used to denote the end of the subrecords.</description>
+ <author>Glen Stampoultzis (glens at apache.org)</author>
+ <fields>
+ </fields>
+</record>
diff --git a/src/records/definitions/subrecord.xsl b/src/records/definitions/subrecord.xsl
new file mode 100644
index 0000000000..a925b27281
--- /dev/null
+++ b/src/records/definitions/subrecord.xsl
@@ -0,0 +1,326 @@
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:recutil="org.apache.poi.generator.RecordUtil"
+ xmlns:field="org.apache.poi.generator.FieldIterator"
+ xmlns:java="java" >
+
+<xsl:template match="record">
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * &quot;This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/).&quot;
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names &quot;Apache&quot; and &quot;Apache Software Foundation&quot; and
+ * &quot;Apache POI&quot; must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called &quot;Apache&quot;,
+ * &quot;Apache POI&quot;, nor may &quot;Apache&quot; appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * &lt;http://www.apache.org/&gt;.
+ */
+
+<xsl:if test="@package">
+package <xsl:value-of select="@package"/>;
+</xsl:if>
+
+
+import org.apache.poi.util.*;
+
+/**
+ * <xsl:value-of select="/record/description"/>
+ * NOTE: This source is automatically generated please do not modify this file. Either subclass or
+ * remove the record in src/records/definitions.
+<xsl:apply-templates select="author"/>
+ */
+public class <xsl:value-of select="@name"/>SubRecord
+ extends SubRecord
+{
+ public final static short sid = <xsl:value-of select="@id"/>;
+<xsl:for-each select="//fields/field"> private <xsl:value-of select="recutil:getType(@size,@type,10)"/><xsl:text> </xsl:text><xsl:value-of select="recutil:getFieldName(position(),@name,0)"/><xsl:value-of select="recutil:initializeText(@size,@type)"/>;
+<xsl:apply-templates select="./bit|./const|./bit/const"/>
+</xsl:for-each>
+
+ public <xsl:value-of select="@name"/>SubRecord()
+ {
+<xsl:for-each select="//fields/field"><xsl:if test="@default">
+<xsl:text> </xsl:text>
+<xsl:value-of select="recutil:getFieldName(position(),@name,0)"/> = <xsl:value-of select="@default"/>;
+</xsl:if></xsl:for-each>
+ }
+
+ /**
+ * Constructs a <xsl:value-of select="@name"/> record and sets its fields appropriately.
+ *
+ * @param id id must be <xsl:value-of select="@id"/> or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ */
+
+ public <xsl:value-of select="@name"/>SubRecord(short id, short size, byte [] data)
+ {
+ super(id, size, data);
+ <xsl:for-each select="//fields/field">
+ <xsl:if test="@default">
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="recutil:getFieldName(position(),@name,0)"/> =
+ <xsl:value-of select="@default"/>;
+
+ </xsl:if>
+ </xsl:for-each>
+ }
+
+ /**
+ * Constructs a <xsl:value-of select="@name"/> record and sets its fields appropriately.
+ *
+ * @param id id must be <xsl:value-of select="@id"/> or an exception
+ * will be throw upon validation
+ * @param size size the size of the data area of the record
+ * @param data data of the record (should not contain sid/len)
+ * @param offset of the record's data
+ */
+
+ public <xsl:value-of select="@name"/>SubRecord(short id, short size, byte [] data, int offset)
+ {
+ super(id, size, data, offset);
+ <xsl:for-each select="//fields/field">
+ <xsl:if test="@default">
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="recutil:getFieldName(position(),@name,0)"/> =
+ <xsl:value-of select="@default"/>;
+
+ </xsl:if>
+ </xsl:for-each>
+ }
+
+ /**
+ * Checks the sid matches the expected side for this record
+ *
+ * @param id the expected sid.
+ */
+ protected void validateSid(short id)
+ {
+ if (id != sid)
+ {
+ throw new RecordFormatException(&quot;Not a <xsl:value-of select="@name"/> record&quot;);
+ }
+ }
+
+ protected void fillFields(byte [] data, short size, int offset)
+ {
+
+<xsl:text> int pos = 0;
+</xsl:text>
+
+ <xsl:variable name="fieldIterator" select="field:new()"/>
+<xsl:for-each select="//fields/field">
+ <xsl:text> </xsl:text><xsl:value-of select="field:fillDecoder2($fieldIterator,position(),@name,@size,@type)"/>;
+</xsl:for-each>
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+
+ buffer.append("[<xsl:value-of select="recutil:getRecordId(@name,@excel-record-id)"/>]\n");
+<xsl:apply-templates select="//field" mode="tostring"/>
+ buffer.append("[/<xsl:value-of select="recutil:getRecordId(@name,@excel-record-id)"/>]\n");
+ return buffer.toString();
+ }
+
+ public int serialize(int offset, byte[] data)
+ {
+ int pos = 0;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
+<xsl:variable name="fieldIterator" select="field:new()"/>
+<xsl:for-each select="//fields/field"><xsl:text>
+ </xsl:text><xsl:value-of select="field:serialiseEncoder($fieldIterator,position(),@name,@size,@type)"/>
+</xsl:for-each>
+
+ return getRecordSize();
+ }
+
+ /**
+ * Size of record (exluding 4 byte header)
+ */
+ public int getRecordSize()
+ {
+<xsl:variable name="fieldIterator" select="field:new()"/>
+<xsl:text> return 4 </xsl:text>
+<xsl:for-each select="//fields/field">
+ <xsl:value-of select="field:calcSize($fieldIterator,position(),@name,@size,@type)"/>
+</xsl:for-each>;
+ }
+
+ public short getSid()
+ {
+ return this.sid;
+ }
+
+ public Object clone() {
+ <xsl:value-of select="@name"/>SubRecord rec = new <xsl:value-of select="@name"/>SubRecord();
+ <xsl:for-each select="//fields/field">
+ <xsl:text>
+ </xsl:text><xsl:value-of select="recutil:clone(@name,@type,position())"/><xsl:text>;</xsl:text>
+ </xsl:for-each>
+ return rec;
+ }
+
+<xsl:apply-templates select="//field" mode="getset"/>
+<xsl:apply-templates select="//field" mode="bits"/>
+
+} // END OF CLASS
+
+
+</xsl:template>
+
+<xsl:template match = "field" mode="bits">
+<xsl:variable name="fieldNum" select="position()"/>
+<xsl:for-each select="bit">
+<xsl:if test="not (@mask)">
+ /**
+ * Sets the <xsl:value-of select="@name"/> field value.
+ * <xsl:value-of select="@description"/>
+ */
+ public void set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>(boolean value)
+ {
+ <xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/> = <xsl:value-of select="recutil:getFieldName(@name,0)"/>.set<xsl:value-of select="recutil:getType1stCap(../@size,../@type,0)"/>Boolean(<xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/>, value);
+ }
+
+ /**
+ * <xsl:value-of select="@description"/>
+ * @return the <xsl:value-of select="@name"/> field value.
+ */
+ public boolean is<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>()
+ {
+ return <xsl:value-of select="recutil:getFieldName(@name,0)"/>.isSet(<xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/>);
+ }
+</xsl:if>
+<xsl:if test="@mask">
+ /**
+ * Sets the <xsl:value-of select="@name"/> field value.
+ * <xsl:value-of select="@description"/>
+ */
+ public void set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>(short value)
+ {
+ <xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/> = <xsl:value-of select="recutil:getFieldName(@name,0)"/>.set<xsl:value-of select="recutil:getType1stCap(../@size,../@type,0)"/>Value(<xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/>, value);
+ }
+
+ /**
+ * <xsl:value-of select="@description"/>
+ * @return the <xsl:value-of select="@name"/> field value.
+ */
+ public short get<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>()
+ {
+ return <xsl:value-of select="recutil:getFieldName(@name,0)"/>.getShortValue(<xsl:value-of select="recutil:getFieldName($fieldNum,../@name,0)"/>);
+ }
+</xsl:if>
+</xsl:for-each>
+</xsl:template>
+
+<xsl:template match = "bit" ><xsl:if test="not (@mask)"> private BitField <xsl:value-of select="recutil:getFieldName(@name,42)"/> = new BitField(<xsl:value-of select="recutil:getMask(@number)"/>);
+</xsl:if><xsl:if test="@mask"> private BitField <xsl:value-of select="recutil:getFieldName(@name,42)"/> = new BitField(<xsl:value-of select="@mask"/>);
+</xsl:if>
+</xsl:template>
+<xsl:template match = "const"> public final static <xsl:value-of select="recutil:getType(../@size,../@type,10)"/><xsl:text> </xsl:text><xsl:value-of select="recutil:getConstName(../@name,@name,30)"/> = <xsl:value-of select="@value"/>;
+</xsl:template>
+
+<xsl:template match = "const" mode="listconsts">
+<xsl:text>
+ * </xsl:text>
+<xsl:value-of select="recutil:getConstName(../@name,@name,0)"/></xsl:template>
+<xsl:template match="field" mode="getset">
+ /**
+ * Get the <xsl:value-of select="@name"/> field for the <xsl:value-of select="../../@name"/> record.<xsl:if test="./const">
+ *
+ * @return One of <xsl:apply-templates select="./const" mode="listconsts"/></xsl:if>
+ */
+ public <xsl:value-of select="recutil:getType(@size,@type,0)"/> get<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>()
+ {
+ return <xsl:value-of select="recutil:getFieldName(position(),@name,0)"/>;
+ }
+
+ /**
+ * Set the <xsl:value-of select="@name"/> field for the <xsl:value-of select="../../@name"/> record.<xsl:if test="./const">
+ *
+ * @param <xsl:value-of select="recutil:getFieldName(position(),@name,0)"/>
+ * One of <xsl:apply-templates select="./const" mode="listconsts"/></xsl:if>
+ */
+ public void set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>(<xsl:value-of select="recutil:getType(@size,@type,0)"/><xsl:text> </xsl:text><xsl:value-of select="recutil:getFieldName(position(),@name,0)"/>)
+ {
+ this.<xsl:value-of select="recutil:getFieldName(position(),@name,0)"/> = <xsl:value-of select="recutil:getFieldName(position(),@name,0)"/>;
+ }
+</xsl:template>
+
+<xsl:template match="field" mode="tostring">
+ <xsl:value-of select="recutil:getToString(@name,@type,@size)"/>
+ <xsl:text>
+ buffer.append(System.getProperty("line.separator")); </xsl:text>
+ <xsl:apply-templates select="bit" mode="bittostring"/>
+ <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+ <xsl:template match="bit" mode="bittostring">
+ <xsl:if test="not (@mask)">
+ <xsl:text>&#10; buffer.append(" .</xsl:text>
+ <xsl:value-of select="recutil:getFieldName(@name,20)"/>
+ <xsl:text> = ").append(is</xsl:text>
+ <xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>
+ <xsl:text>()).append('\n'); </xsl:text>
+ </xsl:if>
+ <xsl:if test="@mask">
+ <xsl:text>&#10; buffer.append(" .</xsl:text>
+ <xsl:value-of select="recutil:getFieldName(@name,20)"/>
+ <xsl:text> = ").append(get</xsl:text>
+ <xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>
+ <xsl:text>()).append('\n'); </xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+<xsl:template match="author">
+ * @author <xsl:value-of select="."/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/records/definitions/subrecord_test.xsl b/src/records/definitions/subrecord_test.xsl
new file mode 100644
index 0000000000..a98b77dac1
--- /dev/null
+++ b/src/records/definitions/subrecord_test.xsl
@@ -0,0 +1,155 @@
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:recutil="org.apache.poi.generator.RecordUtil"
+ xmlns:field="org.apache.poi.generator.FieldIterator"
+ xmlns:java="java" >
+
+<xsl:template match="record">
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * &quot;This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/).&quot;
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names &quot;Apache&quot; and &quot;Apache Software Foundation&quot; and
+ * &quot;Apache POI&quot; must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called &quot;Apache&quot;,
+ * &quot;Apache POI&quot;, nor may &quot;Apache&quot; appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * &lt;http://www.apache.org/&gt;.
+ */
+
+<xsl:if test="@package">
+package <xsl:value-of select="@package"/>;
+</xsl:if>
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the serialization and deserialization of the <xsl:value-of select="@name"/>SubRecord
+ * class works correctly. Test data taken directly from a real
+ * Excel file.
+ *
+<xsl:apply-templates select="author"/>
+ */
+public class Test<xsl:value-of select="@name"/>SubRecord
+ extends TestCase
+{
+ byte[] data = new byte[] {
+ <xsl:value-of select="recutil:getByteArrayString(testdata)"/>
+ };
+
+ public Test<xsl:value-of select="@name"/>SubRecord(String name)
+ {
+ super(name);
+ }
+
+ public void testLoad()
+ throws Exception
+ {
+ <xsl:value-of select="@name"/>SubRecord record = new <xsl:value-of select="@name"/>SubRecord((short)<xsl:value-of select="@id"/>, (short)data.length, data);
+
+<xsl:for-each select="//fields/field">
+ <xsl:choose><xsl:when test="@type='string' or @type='hbstring'">
+ assertEquals( "<xsl:value-of select="./testresult/@value"/>", record.get<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>());
+</xsl:when><xsl:when test="@size='1'">
+ assertEquals( (byte)<xsl:value-of select="./testresult/@value"/>, record.get<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>());
+</xsl:when><xsl:when test="@size='2'">
+ assertEquals( (short)<xsl:value-of select="./testresult/@value"/>, record.get<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>());
+</xsl:when><xsl:when test="@size='4'">
+ assertEquals( (int)<xsl:value-of select="./testresult/@value"/>, record.get<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>());
+</xsl:when><xsl:otherwise>
+ assertEquals( (double)<xsl:value-of select="./testresult/@value"/>, record.get<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>());
+</xsl:otherwise></xsl:choose>
+<xsl:apply-templates select="./bit" mode="get"/>
+</xsl:for-each>
+
+ assertEquals( <xsl:value-of select="./testsize"/>, record.getRecordSize() );
+
+ record.validateSid((short)<xsl:value-of select="@id"/>);
+ }
+
+ public void testStore()
+ {
+ <xsl:value-of select="@name"/>SubRecord record = new <xsl:value-of select="@name"/>SubRecord();
+
+
+<xsl:for-each select="//fields/field">
+ <xsl:choose><xsl:when test="@type='string' or @type='hbstring'">
+ record.set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>( "<xsl:value-of select="./testresult/@value"/>" );
+</xsl:when><xsl:when test="@size='1'">
+ record.set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>( (byte)<xsl:value-of select="./testresult/@value"/> );
+</xsl:when><xsl:when test="@size='2'">
+ record.set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>( (short)<xsl:value-of select="./testresult/@value"/> );
+</xsl:when><xsl:when test="@size='4'">
+ record.set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>( (int)<xsl:value-of select="./testresult/@value"/> );
+</xsl:when><xsl:otherwise>
+ record.set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>( (double)<xsl:value-of select="./testresult/@value"/> );
+</xsl:otherwise></xsl:choose>
+
+<xsl:apply-templates select="./bit" mode="set"/>
+</xsl:for-each>
+
+ byte [] recordBytes = record.serialize();
+ assertEquals(recordBytes.length - 4, data.length);
+ for (int i = 0; i &lt; data.length; i++)
+ assertEquals("At offset " + i, data[i], recordBytes[i+4]);
+ }
+}
+</xsl:template>
+
+<xsl:template match="author">
+ * @author <xsl:value-of select="."/>
+</xsl:template>
+
+<xsl:template match="bit" mode="get">
+<xsl:text> </xsl:text>assertEquals( <xsl:value-of select="./testresult/@value"/>, record.is<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>() );<xsl:text>
+</xsl:text>
+</xsl:template>
+
+<xsl:template match="bit" mode="set">
+<xsl:text> </xsl:text>record.set<xsl:value-of select="recutil:getFieldName1stCap(@name,0)"/>( <xsl:value-of select="./testresult/@value"/> );<xsl:text>
+</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/records/definitions/textobject_record.xml b/src/records/definitions/textobject_record.xml
new file mode 100644
index 0000000000..6d4bc9c666
--- /dev/null
+++ b/src/records/definitions/textobject_record.xml
@@ -0,0 +1,41 @@
+<record id="0x1B6" name="TextObjectBase" excel-record-id="TXO"
+ package="org.apache.poi.hssf.record">
+ <suffix>Record</suffix>
+ <extends>Record</extends>
+ <description>The TXO record is used to define the properties of a text box. It is followed
+ by two continue records unless there is no actual text. The first continue record contains
+ the text data and the next continue record contains the formatting runs.</description>
+ <author>Glen Stampoultzis (glens at apache.org)</author>
+ <fields>
+ <field type="int" size="2" name="options" description="option flags">
+ <bit number="0" name="reserved1" description="reserved field"/>
+ <bit mask="0x000E" name="Horizontal text alignment">
+ <const name="left aligned" value="1"/>
+ <const name="centered" value="2"/>
+ <const name="right aligned" value="3"/>
+ <const name="justified" value="4"/>
+ </bit>
+ <bit mask="0x0070" name="Vertical text alignment">
+ <const name="top" value="1"/>
+ <const name="center" value="2"/>
+ <const name="bottom" value="3"/>
+ <const name="justify" value="4"/>
+ </bit>
+ <bit mask="0x0180" name="reserved2"/>
+ <bit number="9" name="text locked" description="Text has been locked"/>
+ <bit mask="0xFC00" name="reserved3"/>
+ </field>
+ <field type="int" size="2" name="text orientation" description="specifies whether the text is rotated">
+ <const name="none" value="0"/>
+ <const name="top_to_bottom" value="1"/>
+ <const name="rot_right" value="2"/>
+ <const name="rot_left" value="3"/>
+ </field>
+ <field type="int" size="2" name="reserved4" description="reserved field"/>
+ <field type="int" size="2" name="reserved5" description="reserved field"/>
+ <field type="int" size="2" name="reserved6" description="reserved field"/>
+ <field type="int" size="2" name="text length" description="the left of the text string"/>
+ <field type="int" size="2" name="formatting run length" description="the length of the formatting runs."/>
+ <field type="int" size="4" name="reserved7" description="reserved field"/>
+ </fields>
+</record>
diff --git a/src/resources/fontmetrics/font_metrics.properties b/src/resources/fontmetrics/font_metrics.properties
new file mode 100644
index 0000000000..24fb9918ea
--- /dev/null
+++ b/src/resources/fontmetrics/font_metrics.properties
@@ -0,0 +1,912 @@
+#Font Metrics
+#Sun Sep 07 21:54:47 EST 2003
+font.Lucida\ Sans\ Demibold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Medium\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Jokerman.height=16
+font.Harrington.height=13
+font.Curlz\ MT.height=14
+font.Garamond\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Wide\ Latin.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Colonna\ MT.widths=5, 6, 5, 6, 6, 4, 7, 6, 4, 4, 6, 4, 9, 6, 6, 6, 6, 5, 5, 5, 6, 6, 8, 6, 6, 5, 8, 6, 7, 8, 6, 6, 7, 8, 4, 4, 7, 6, 9, 9, 7, 7, 7, 8, 5, 7, 8, 7, 9, 7, 8, 6, 6, 4, 5, 5, 6, 5, 6, 5, 5, 6,
+font.Gill\ Sans\ MT\ Ext\ Condensed\ Bold.height=12
+font.Beesknees\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Freestyle\ Script.height=13
+font.Palace\ Script\ MT.widths=4, 3, 3, 4, 3, 3, 4, 4, 3, 3, 4, 3, 6, 4, 3, 4, 3, 3, 3, 3, 4, 4, 5, 4, 4, 3, 8, 8, 6, 8, 5, 7, 6, 8, 6, 6, 8, 7, 8, 7, 5, 7, 6, 8, 6, 7, 5, 5, 6, 7, 5, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+font.Lucida\ Sans\ Demibold\ Roman.height=14
+font.Franklin\ Gothic\ Book\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Serif.widths=7, 7, 6, 7, 6, 4, 7, 7, 5, 5, 8, 5, 11, 7, 6, 6, 7, 6, 6, 5, 7, 6, 9, 6, 6, 6, 8, 9, 8, 9, 8, 8, 8, 10, 5, 5, 9, 7, 11, 9, 9, 8, 9, 8, 7, 7, 9, 7, 10, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bitstream\ Vera\ Sans\ Mono.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Matisse\ ITC.height=14
+font.Arial\ Special\ G2\ Bold\ Italic.height=14
+font.Lucida\ Fax\ Demibold.height=14
+font.Arial\ Special\ G1\ Bold\ Italic.height=14
+font.Tempus\ Sans\ ITC.height=14
+font.Impact.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Algerian.widths=9, 7, 7, 7, 7, 7, 8, 7, 4, 6, 8, 6, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 8, 7, 7, 9, 7, 7, 7, 7, 7, 8, 7, 4, 6, 8, 6, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Gill\ Sans\ MT\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Narrow\ Special\ G1\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.OCR\ A\ Extended.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Cooper\ Black.widths=7, 7, 6, 8, 6, 5, 7, 8, 6, 6, 8, 6, 11, 8, 7, 8, 7, 6, 6, 6, 8, 7, 11, 7, 7, 6, 9, 8, 8, 9, 8, 8, 9, 9, 4, 7, 9, 7, 10, 9, 9, 8, 9, 9, 7, 8, 9, 9, 11, 9, 9, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Century\ Gothic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bauhaus\ 93.widths=7, 7, 7, 7, 7, 4, 7, 6, 4, 5, 6, 4, 9, 6, 7, 7, 7, 4, 5, 4, 6, 5, 9, 6, 6, 6, 7, 7, 9, 8, 6, 6, 7, 7, 4, 4, 7, 4, 9, 7, 9, 7, 9, 7, 5, 6, 7, 6, 9, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bernard\ MT\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Old\ English\ Text\ MT.widths=5, 5, 4, 5, 4, 3, 5, 5, 3, 3, 6, 3, 7, 5, 5, 5, 5, 4, 5, 3, 5, 5, 7, 6, 5, 4, 9, 10, 10, 10, 10, 10, 9, 10, 8, 8, 10, 9, 12, 11, 10, 10, 10, 9, 10, 9, 11, 9, 10, 8, 9, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Century\ Schoolbook.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Fax\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Rounded\ MT\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Informal\ Roman.widths=5, 5, 4, 6, 5, 4, 5, 5, 3, 3, 5, 3, 7, 5, 4, 5, 5, 5, 4, 4, 5, 5, 7, 5, 5, 6, 7, 7, 6, 7, 6, 6, 7, 7, 4, 6, 6, 6, 8, 7, 6, 6, 6, 6, 5, 7, 7, 7, 9, 7, 7, 7, 5, 4, 6, 6, 6, 7, 6, 6, 6, 6,
+font.Lucida\ Sans\ Demibold.height=14
+font.Ravie.height=14
+font.Perpetua.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.MT\ Extra.height=13
+font.Bitstream\ Vera\ Sans\ Mono\ Bold\ Oblique.height=14
+font.Book\ Antiqua\ Bold\ Italic.height=14
+font.Eras\ Medium\ ITC.height=12
+font.Rockwell\ Bold.height=14
+font.Snap\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.dialog.italic.widths=7, 7, 6, 7, 7, 4, 7, 7, 4, 3, 6, 4, 8, 7, 7, 7, 7, 4, 6, 4, 7, 6, 9, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 3, 6, 8, 7, 9, 8, 9, 8, 9, 8, 8, 7, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Century\ Gothic\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Demi\ Italic.height=14
+font.Franklin\ Gothic\ Book.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Special\ G1\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tw\ Cen\ MT\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Desdemona.height=12
+font.Franklin\ Gothic\ Demi\ Cond.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Wingdings\ 3.height=13
+font.High\ Tower\ Text.height=13
+font.Edwardian\ Script\ ITC.widths=4, 4, 3, 4, 3, 2, 4, 4, 3, 3, 4, 3, 6, 5, 4, 4, 4, 4, 3, 3, 4, 4, 5, 4, 4, 4, 10, 10, 9, 9, 9, 8, 8, 10, 7, 7, 10, 9, 11, 10, 9, 9, 8, 10, 8, 7, 10, 8, 10, 11, 10, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Gigi.widths=6, 5, 5, 5, 6, 5, 6, 5, 5, 4, 6, 6, 9, 6, 4, 6, 5, 5, 4, 5, 6, 5, 8, 6, 6, 4, 10, 8, 7, 10, 5, 9, 7, 9, 6, 6, 9, 8, 11, 9, 6, 8, 8, 7, 6, 7, 8, 9, 13, 9, 9, 7, 5, 5, 6, 5, 6, 5, 5, 5, 5, 6,
+font.Engravers\ MT.height=13
+font.Californian\ FB\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Calligraphy\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Special\ G1\ Italic.height=14
+font.Book\ Antiqua\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Heavy.widths=7, 7, 6, 7, 7, 5, 7, 7, 4, 4, 7, 4, 10, 7, 7, 7, 7, 5, 6, 5, 7, 6, 8, 7, 6, 6, 7, 7, 7, 8, 7, 6, 8, 8, 4, 5, 8, 6, 9, 8, 8, 7, 8, 8, 7, 6, 8, 7, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Sans\ Typewriter\ Regular.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Ransom\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.High\ Tower\ Text\ Italic.height=14
+font.dialog.bold.height=14
+font.Times\ New\ Roman\ Special\ G2\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Trebuchet\ MS\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Special\ G1\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ Ultra\ Bold\ Condensed.widths=5, 5, 4, 5, 5, 4, 5, 5, 3, 3, 5, 3, 8, 5, 5, 5, 5, 5, 4, 4, 5, 5, 7, 6, 5, 4, 7, 6, 5, 6, 5, 5, 6, 7, 3, 4, 6, 5, 7, 6, 6, 6, 6, 6, 5, 5, 6, 6, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Monotype\ Sorts\ 2.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Rockwell\ Condensed.height=13
+font.Bell\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G2\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Special\ G1\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Gill\ Sans\ Ultra\ Bold.height=13
+font.American\ Uncial.height=13
+font.Kino\ MT.widths=5, 5, 5, 5, 5, 4, 5, 5, 3, 3, 5, 3, 7, 5, 5, 5, 5, 4, 4, 4, 5, 5, 7, 4, 5, 4, 6, 5, 6, 6, 5, 5, 6, 6, 4, 5, 5, 5, 8, 6, 7, 5, 7, 5, 5, 5, 6, 6, 7, 5, 6, 4, 7, 4, 5, 5, 5, 5, 5, 4, 5, 5,
+font.Wingdings.height=12
+font.Mistral.widths=5, 4, 4, 5, 3, 3, 4, 5, 4, 4, 5, 4, 7, 6, 5, 4, 4, 4, 4, 4, 5, 4, 6, 5, 5, 4, 5, 6, 6, 6, 5, 5, 6, 6, 5, 4, 6, 4, 7, 6, 6, 5, 6, 6, 4, 4, 6, 5, 7, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 4, 5, 5,
+font.Bitstream\ Vera\ Sans\ Bold\ Oblique.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Harrington.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Console.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookshelf\ Symbol\ 1.widths=5, 5, 5, 7, 7, 9, 7, 7, 3, 3, 4, 3, 10, 5, 7, 9, 9, 4, 6, 4, 9, 6, 6, 4, 4, 5, 8, 9, 6, 7, 9, 9, 7, 8, 4, 9, 5, 3, 10, 7, 9, 9, 9, 4, 6, 4, 9, 9, 9, 9, 9, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+font.Lucida\ Fax\ Demibold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Eras\ Demi\ ITC.height=12
+font.Calisto\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Blackadder\ ITC.height=13
+font.Arial\ Narrow\ Special\ G2\ Italic.height=14
+font.Century\ Gothic.widths=7, 7, 6, 7, 6, 3, 7, 6, 2, 3, 6, 2, 10, 6, 6, 7, 7, 3, 4, 3, 6, 6, 8, 6, 6, 5, 7, 6, 8, 7, 5, 5, 8, 7, 3, 5, 6, 4, 9, 7, 8, 6, 8, 6, 5, 4, 6, 7, 9, 7, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Ransom\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Monotype\ Sorts.widths=9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+font.Vivaldi\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Typewriter\ Bold\ Oblique.height=14
+font.Lucida\ Handwriting\ Italic.height=14
+font.Castellar.widths=9, 8, 9, 10, 7, 7, 9, 10, 5, 6, 9, 7, 12, 10, 10, 7, 10, 9, 6, 8, 10, 9, 12, 10, 9, 8, 9, 8, 9, 10, 7, 7, 9, 10, 5, 6, 9, 7, 12, 10, 10, 7, 10, 9, 6, 8, 10, 9, 12, 10, 9, 8, 8, 5, 6, 6, 7, 6, 7, 6, 7, 7,
+font.Forte.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tw\ Cen\ MT\ Medium.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Narrow\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Abadi\ MT\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Regular.height=14
+font.Bradley\ Hand\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Eurostile.height=10
+font.Perpetua\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Rockwell\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookshelf\ Symbol\ 2.height=13
+font.Calisto\ MT\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.serif.plain.height=14
+font.Lucida\ Bright\ Regular.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bell\ MT\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Tw\ Cen\ MT\ Condensed.widths=4, 4, 3, 4, 4, 3, 4, 4, 2, 2, 4, 2, 7, 4, 4, 4, 4, 3, 3, 3, 4, 4, 6, 4, 4, 3, 5, 5, 4, 5, 4, 4, 5, 5, 3, 4, 5, 4, 6, 5, 5, 4, 5, 5, 4, 4, 5, 5, 7, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+font.dialog.bolditalic.height=14
+font.Palatino\ Linotype\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.dialoginput.italic.widths=7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Georgia\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Rockwell\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Demi.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Goudy\ Old\ Style\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Perpetua\ Titling\ MT\ Light.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Parchment.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Symbol.height=14
+font.Abadi\ MT\ Condensed.widths=5, 5, 5, 5, 5, 3, 5, 5, 3, 3, 5, 3, 7, 5, 5, 5, 5, 4, 5, 3, 5, 5, 7, 5, 5, 5, 5, 6, 5, 6, 6, 5, 6, 6, 3, 4, 5, 5, 7, 6, 6, 6, 6, 5, 5, 5, 6, 5, 7, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Palatino\ Linotype\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Special\ G1.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Verdana\ Ref.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Century\ Gothic\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Vladimir\ Script.widths=5, 5, 4, 5, 4, 4, 5, 5, 4, 3, 5, 4, 6, 5, 5, 5, 5, 4, 4, 4, 5, 5, 6, 5, 5, 4, 9, 9, 7, 8, 7, 7, 8, 9, 7, 8, 7, 7, 10, 8, 7, 7, 7, 8, 7, 7, 8, 8, 10, 7, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Franklin\ Gothic\ Demi\ Cond.widths=5, 5, 4, 5, 5, 3, 5, 5, 2, 2, 5, 2, 7, 5, 5, 5, 5, 3, 4, 3, 5, 4, 6, 4, 4, 4, 5, 6, 5, 6, 5, 4, 6, 6, 3, 3, 5, 4, 8, 6, 6, 5, 6, 5, 5, 4, 6, 5, 7, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.OCR\ A\ Extended.height=11
+font.Arial\ Narrow\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Runic\ MT\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Wide\ Latin.widths=11, 11, 10, 11, 10, 8, 10, 12, 7, 7, 13, 7, 18, 12, 11, 11, 11, 10, 10, 7, 12, 12, 16, 13, 11, 10, 15, 15, 14, 16, 15, 14, 16, 17, 10, 11, 16, 14, 18, 15, 14, 15, 14, 16, 13, 13, 16, 14, 19, 15, 14, 13, 13, 10, 13, 12, 13, 12, 13, 11, 13, 13,
+font.Arial\ Special\ G2\ Bold.height=14
+font.Lucida\ Console.height=11
+font.Lucida\ Sans\ Typewriter\ Regular.height=14
+font.Bookman\ Old\ Style\ Bold.height=14
+font.Copperplate\ Gothic\ Light.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tw\ Cen\ MT\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Trebuchet\ MS\ Bold.height=14
+font.Garamond\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Serif\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Viner\ Hand\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Book.widths=6, 6, 6, 6, 6, 4, 6, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 6, 4, 6, 6, 8, 5, 5, 5, 6, 7, 7, 7, 6, 6, 7, 7, 4, 5, 7, 6, 9, 8, 7, 7, 7, 7, 7, 6, 7, 6, 9, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bookman\ Old\ Style\ Bold\ Italic.height=14
+font.Ransom\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Pacmania.height=12
+font.Arial\ Narrow\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Fax\ Demibold\ Italic.height=14
+font.monospaced.italic.widths=7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Book\ Antiqua\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Sans\ Demibold\ Roman.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial.height=13
+font.Lucida\ Bright\ Demibold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Special\ G2.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Mistral.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Verdana\ Ref.height=14
+font.Papyrus.height=17
+font.Lucida\ Sans\ Unicode.widths=7, 7, 6, 7, 6, 5, 7, 7, 4, 4, 7, 4, 9, 7, 7, 7, 7, 6, 6, 5, 7, 7, 9, 7, 7, 7, 8, 7, 8, 8, 6, 6, 8, 8, 4, 4, 8, 6, 10, 8, 9, 7, 9, 7, 6, 7, 8, 8, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Sans\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Bold\ Italic.height=14
+font.Century\ Schoolbook\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Special\ G2\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Special\ G1.widths=8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.Arial\ Narrow\ Special\ G1.widths=10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+font.Marlett.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Century\ Gothic\ Italic.height=14
+font.Century\ Gothic\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Georgia\ Bold.height=14
+font.Lucida\ Sans\ Typewriter\ Bold.height=14
+font.MS\ Outlook.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Fax\ Demibold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Rockwell\ Bold\ Italic.height=14
+font.Parchment.widths=3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 3, 2, 4, 3, 3, 3, 3, 3, 3, 2, 3, 3, 4, 3, 3, 2, 10, 9, 8, 8, 8, 7, 11, 9, 7, 7, 9, 9, 10, 10, 10, 9, 9, 8, 8, 8, 9, 8, 10, 8, 8, 9, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+font.Palatino\ Linotype\ Bold\ Italic.height=14
+font.Verdana\ Bold\ Italic.height=14
+font.Juice\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Typewriter\ Oblique.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G2\ Bold.height=14
+font.Ransom.height=16
+font.Rockwell\ Condensed\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Californian\ FB\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Verdana\ Italic.height=14
+font.French\ Script\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Garamond\ Bold.height=14
+font.Lucida\ Calligraphy\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Narrow\ Special\ G2\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Eras\ Light\ ITC.height=12
+font.Franklin\ Gothic\ Book\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Broadway.widths=8, 8, 7, 8, 7, 5, 7, 9, 5, 5, 8, 5, 13, 9, 7, 8, 8, 6, 6, 5, 9, 7, 10, 8, 7, 8, 9, 8, 8, 9, 8, 8, 9, 9, 5, 7, 8, 8, 10, 8, 9, 9, 9, 9, 7, 8, 9, 9, 11, 9, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Californian\ FB\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Courier\ New\ Bold\ Italic.height=14
+font.Marlett.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Trebuchet\ MS.widths=5, 6, 5, 6, 6, 4, 6, 6, 3, 4, 6, 3, 9, 6, 6, 6, 6, 5, 4, 4, 6, 6, 9, 6, 5, 5, 7, 6, 6, 6, 6, 6, 7, 7, 3, 5, 6, 6, 8, 7, 7, 6, 7, 6, 5, 6, 7, 7, 10, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Tw\ Cen\ MT\ Medium\ Italic.height=14
+font.Microsoft\ Sans\ Serif.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Brush\ Script\ MT\ Italic.height=14
+font.Baskerville\ Old\ Face.widths=5, 6, 5, 6, 6, 4, 5, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 5, 4, 6, 5, 7, 5, 5, 5, 8, 8, 8, 9, 7, 6, 9, 9, 5, 4, 8, 7, 10, 9, 9, 7, 9, 7, 6, 8, 9, 8, 12, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Edda.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans\ Bold.height=14
+font.Rage\ Italic.widths=5, 4, 4, 6, 4, 3, 5, 5, 4, 3, 5, 4, 7, 6, 5, 6, 6, 5, 4, 4, 6, 5, 6, 5, 6, 4, 7, 8, 6, 8, 7, 6, 6, 8, 5, 6, 8, 7, 10, 8, 7, 7, 6, 8, 7, 6, 8, 7, 9, 7, 8, 7, 6, 4, 6, 6, 6, 5, 6, 5, 6, 6,
+font.Franklin\ Gothic\ Medium\ Italic.height=14
+font.Century\ Schoolbook\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Braggadocio.widths=9, 9, 7, 9, 8, 6, 9, 9, 5, 5, 10, 5, 13, 9, 9, 9, 9, 8, 7, 6, 9, 8, 11, 8, 10, 9, 10, 12, 10, 11, 10, 10, 10, 12, 7, 8, 13, 10, 13, 10, 11, 11, 11, 12, 9, 11, 10, 11, 13, 10, 11, 12, 11, 7, 10, 11, 12, 11, 10, 11, 11, 11,
+font.Wingdings\ 2.height=12
+font.Britannic\ Bold.height=11
+font.Bitstream\ Vera\ Sans\ Mono\ Bold\ Oblique.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookshelf\ Symbol\ 5.widths=9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+font.Playbill.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Narrow\ Bold\ Italic.height=14
+font.Georgia\ Ref.widths=7, 7, 6, 7, 6, 5, 7, 8, 5, 4, 7, 5, 11, 8, 6, 7, 6, 6, 6, 5, 8, 7, 9, 7, 7, 6, 9, 8, 7, 8, 8, 7, 8, 9, 5, 6, 8, 7, 10, 10, 8, 8, 8, 9, 7, 7, 9, 9, 11, 9, 9, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Georgia.widths=7, 7, 5, 7, 6, 5, 6, 8, 5, 4, 7, 5, 11, 8, 6, 7, 6, 5, 5, 5, 8, 7, 8, 7, 7, 5, 8, 8, 7, 8, 7, 7, 8, 9, 5, 6, 8, 7, 10, 8, 8, 7, 8, 8, 6, 7, 8, 8, 11, 8, 8, 7, 7, 6, 6, 6, 6, 6, 7, 6, 7, 7,
+font.Arial\ Black\ Italic.height=14
+font.Franklin\ Gothic\ Medium\ Cond.widths=6, 6, 5, 6, 5, 4, 5, 6, 3, 3, 5, 3, 8, 6, 6, 6, 6, 4, 5, 4, 6, 5, 7, 5, 5, 4, 6, 6, 6, 7, 6, 5, 7, 6, 3, 4, 6, 5, 8, 7, 7, 6, 7, 6, 6, 5, 6, 6, 8, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Bauhaus\ 93.height=15
+font.Old\ English\ Text\ MT.height=11
+font.Verdana\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Augsburger\ Initials.widths=8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.monospaced.bold.height=14
+font.Bell\ MT.widths=5, 6, 5, 6, 5, 4, 6, 6, 4, 4, 6, 4, 8, 6, 6, 6, 6, 5, 5, 4, 6, 6, 8, 6, 6, 5, 8, 7, 7, 9, 8, 7, 8, 9, 5, 5, 8, 7, 10, 9, 8, 7, 8, 8, 6, 8, 9, 8, 11, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Rockwell\ Italic.height=14
+font.Lucida\ Sans\ Unicode.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Castellar.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Stencil.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Chiller.widths=4, 4, 4, 4, 4, 3, 4, 4, 3, 3, 4, 3, 6, 4, 4, 4, 4, 3, 4, 4, 4, 4, 5, 4, 5, 5, 6, 6, 6, 5, 5, 4, 5, 5, 4, 5, 6, 5, 7, 6, 6, 5, 6, 5, 4, 5, 5, 5, 7, 6, 5, 6, 6, 4, 5, 5, 5, 5, 5, 5, 6, 5,
+font.Gill\ Sans\ MT\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.dialog.italic.height=14
+font.Perpetua\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.ProFont.widths=8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.dialog.bolditalic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Trebuchet\ MS\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Bold.height=14
+font.Californian\ FB\ Bold.height=14
+font.Rockwell\ Condensed\ Bold.height=14
+font.Georgia\ Bold\ Italic.height=14
+font.Trebuchet\ MS\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Special\ G1\ Bold.height=14
+font.Harlow\ Solid\ Italic\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Poor\ Richard.widths=6, 6, 5, 6, 5, 4, 6, 6, 4, 3, 6, 4, 8, 6, 5, 6, 6, 4, 4, 4, 6, 6, 7, 5, 6, 4, 8, 7, 8, 8, 6, 6, 8, 8, 4, 5, 7, 6, 9, 8, 9, 6, 9, 7, 6, 6, 8, 8, 11, 7, 8, 6, 6, 4, 6, 6, 6, 6, 6, 6, 6, 6,
+font.serif.bolditalic.widths=5, 5, 4, 5, 4, 3, 5, 6, 3, 3, 5, 3, 8, 6, 5, 5, 5, 4, 4, 3, 6, 4, 7, 5, 4, 4, 7, 7, 7, 7, 7, 7, 7, 8, 4, 5, 7, 6, 9, 7, 7, 6, 7, 7, 6, 6, 7, 7, 9, 7, 7, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Gill\ Sans\ Ultra\ Bold\ Condensed.height=13
+font.Verdana.widths=7, 7, 6, 7, 7, 5, 7, 7, 3, 4, 7, 3, 11, 7, 7, 7, 7, 5, 6, 5, 7, 7, 9, 7, 7, 6, 9, 8, 7, 9, 7, 7, 8, 8, 6, 6, 8, 6, 9, 8, 9, 8, 9, 8, 7, 6, 8, 8, 12, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Gill\ Sans\ Ultra\ Bold\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G2\ Bold\ Italic.height=14
+font.Courier\ New\ Italic.height=14
+font.Goudy\ Old\ Style.widths=5, 6, 5, 6, 5, 4, 5, 6, 4, 4, 6, 4, 8, 7, 6, 6, 6, 4, 4, 4, 6, 5, 7, 5, 5, 4, 8, 7, 8, 8, 7, 6, 9, 9, 4, 4, 8, 6, 10, 8, 9, 6, 9, 8, 6, 7, 9, 8, 11, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.dialog.plain.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Bold.height=14
+font.Bitstream\ Vera\ Sans\ Mono\ Oblique.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Trebuchet\ MS.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Modern\ No.\ 20.widths=6, 6, 5, 6, 5, 5, 5, 7, 4, 4, 6, 4, 9, 6, 5, 6, 6, 5, 4, 4, 6, 5, 8, 6, 6, 5, 8, 8, 6, 8, 8, 7, 8, 9, 5, 6, 8, 8, 10, 8, 7, 7, 7, 8, 6, 7, 8, 8, 11, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Kino\ MT.height=12
+font.Viner\ Hand\ ITC.widths=7, 6, 5, 6, 5, 5, 6, 7, 5, 5, 8, 5, 10, 8, 5, 6, 7, 5, 4, 6, 8, 7, 9, 6, 7, 6, 9, 9, 8, 9, 8, 7, 7, 9, 5, 6, 10, 8, 10, 9, 6, 8, 9, 8, 8, 7, 8, 8, 12, 7, 8, 8, 5, 4, 8, 6, 7, 5, 6, 6, 6, 6,
+font.Forte.widths=7, 6, 5, 7, 5, 5, 7, 6, 4, 4, 6, 6, 9, 6, 6, 7, 6, 4, 5, 4, 6, 5, 8, 6, 6, 5, 8, 8, 7, 8, 7, 6, 7, 9, 7, 6, 8, 5, 12, 9, 7, 7, 8, 7, 6, 6, 7, 6, 9, 8, 8, 7, 6, 5, 5, 6, 6, 6, 6, 6, 6, 6,
+font.dialoginput.bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Impact.height=14
+font.Showcard\ Gothic.widths=7, 8, 7, 10, 7, 6, 8, 9, 5, 6, 8, 6, 10, 9, 9, 8, 9, 8, 7, 7, 9, 7, 11, 8, 7, 7, 7, 8, 7, 10, 7, 6, 8, 9, 5, 6, 8, 6, 10, 9, 9, 8, 9, 8, 7, 7, 9, 7, 11, 8, 7, 7, 7, 5, 6, 6, 7, 6, 6, 6, 7, 7,
+font.Felix\ Titling.widths=8, 7, 9, 9, 6, 6, 9, 8, 4, 4, 9, 6, 10, 9, 9, 6, 9, 9, 6, 7, 9, 8, 11, 8, 7, 8, 8, 7, 9, 9, 6, 6, 9, 8, 4, 4, 9, 6, 10, 9, 9, 6, 9, 9, 6, 7, 9, 8, 11, 8, 7, 8, 7, 4, 6, 7, 6, 7, 7, 6, 7, 7,
+font.Arial\ Black.height=15
+font.Comic\ Sans\ MS\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Stencil.height=11
+font.Gill\ Sans\ MT\ Condensed.height=13
+font.Tw\ Cen\ MT\ Medium\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.French\ Script\ MT.widths=4, 4, 4, 4, 4, 3, 5, 5, 3, 3, 5, 3, 6, 4, 4, 4, 4, 3, 4, 3, 4, 4, 5, 4, 4, 4, 6, 8, 6, 8, 6, 7, 6, 9, 6, 6, 9, 7, 11, 10, 7, 6, 7, 8, 7, 6, 8, 8, 9, 7, 8, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Symbol.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Book\ Antiqua\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G1.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Felix\ Titling.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Perpetua\ Bold\ Italic.height=14
+font.Arial\ Special\ G1\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tw\ Cen\ MT\ Condensed\ Extra\ Bold.height=12
+font.Century\ Schoolbook\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Calisto\ MT.widths=4, 6, 4, 5, 4, 3, 5, 6, 3, 3, 5, 3, 9, 6, 6, 6, 5, 4, 4, 3, 6, 6, 8, 5, 5, 5, 8, 7, 8, 8, 6, 6, 9, 8, 3, 5, 8, 6, 10, 8, 9, 5, 9, 7, 6, 5, 8, 7, 11, 7, 8, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.dialoginput.bolditalic.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Pristina.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G1\ Bold.height=14
+font.serif.bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Kristen\ ITC.height=15
+font.Lucida\ Bright\ Demibold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Special\ G2.widths=8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.Palatino\ Linotype.height=14
+font.dialoginput.plain.height=14
+font.Times\ New\ Roman\ Special\ G2.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Kunstler\ Script.height=12
+font.Book\ Antiqua.widths=5, 6, 4, 6, 5, 4, 6, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 4, 3, 6, 6, 8, 5, 6, 5, 8, 7, 7, 8, 6, 6, 8, 8, 4, 4, 8, 6, 10, 8, 8, 6, 8, 7, 6, 7, 8, 8, 10, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Times\ New\ Roman.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tw\ Cen\ MT\ Medium.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Mercurius\ Script\ MT\ Bold.height=12
+font.Tw\ Cen\ MT\ Condensed.height=12
+font.Monotype\ Sorts\ 2.widths=11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+font.sansserif.bold.height=14
+font.Elephant\ Italic.height=14
+font.dialoginput.italic.height=14
+font.Tahoma\ Bold.height=14
+font.Rockwell\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Narrow.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ MT\ Italic.height=14
+font.serif.bold.widths=5, 6, 4, 6, 4, 4, 5, 6, 3, 3, 5, 3, 8, 6, 5, 6, 6, 4, 4, 3, 6, 5, 7, 6, 5, 3, 7, 7, 7, 7, 7, 6, 8, 8, 4, 5, 8, 7, 9, 7, 8, 6, 9, 7, 6, 7, 7, 7, 10, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Baskerville\ Old\ Face.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Abadi\ MT\ Condensed.height=12
+font.Copperplate\ Gothic\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Snap\ ITC.widths=7, 7, 7, 7, 7, 7, 8, 7, 6, 5, 8, 5, 9, 7, 7, 7, 7, 7, 7, 7, 8, 8, 11, 9, 8, 7, 10, 9, 9, 9, 9, 9, 10, 10, 6, 9, 9, 8, 12, 10, 9, 9, 9, 9, 9, 8, 9, 9, 12, 10, 10, 9, 9, 6, 9, 9, 10, 9, 9, 9, 10, 9,
+font.Rockwell\ Extra\ Bold.widths=8, 8, 7, 8, 7, 5, 8, 8, 4, 4, 7, 4, 12, 8, 8, 8, 8, 5, 6, 5, 8, 7, 10, 7, 7, 7, 8, 9, 8, 9, 8, 7, 8, 9, 5, 4, 9, 7, 11, 9, 9, 8, 9, 9, 6, 8, 7, 8, 11, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bradley\ Hand\ ITC.height=13
+font.Imprint\ MT\ Shadow.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Special\ G2.height=16
+font.Arial\ Narrow\ Special\ G2.height=16
+font.Bitstream\ Vera\ Sans\ Oblique.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Elephant.widths=7, 7, 6, 7, 6, 5, 6, 7, 4, 4, 7, 4, 10, 8, 7, 7, 7, 6, 6, 5, 8, 6, 9, 6, 6, 6, 8, 10, 9, 10, 10, 9, 10, 11, 6, 8, 10, 9, 12, 10, 10, 9, 10, 10, 8, 9, 10, 8, 13, 9, 8, 9, 9, 6, 8, 8, 8, 8, 8, 7, 8, 8,
+font.Eurostile\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Gigi.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Imprint\ MT\ Shadow.height=13
+font.Monotype\ Corsiva.widths=5, 5, 4, 5, 4, 4, 6, 5, 3, 3, 5, 3, 7, 6, 5, 5, 5, 4, 4, 4, 6, 5, 8, 5, 5, 5, 7, 7, 6, 8, 7, 7, 7, 8, 5, 5, 8, 7, 9, 8, 7, 7, 7, 7, 6, 6, 8, 7, 10, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Matisse\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookman\ Old\ Style.widths=6, 6, 6, 6, 6, 5, 6, 7, 4, 4, 7, 4, 10, 7, 7, 7, 6, 5, 5, 5, 7, 6, 10, 6, 6, 6, 7, 8, 7, 8, 7, 7, 8, 8, 4, 7, 8, 7, 11, 8, 8, 7, 8, 8, 7, 8, 8, 8, 11, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Perpetua.height=13
+font.Franklin\ Gothic\ Book.height=13
+font.Gloucester\ MT\ Extra\ Condensed.widths=4, 5, 4, 5, 4, 3, 4, 5, 3, 3, 5, 3, 6, 5, 5, 5, 5, 4, 4, 3, 5, 4, 6, 5, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 3, 4, 5, 4, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Ransom\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans\ Mono.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Garamond\ Italic.height=14
+font.Colonna\ MT.height=12
+font.Gill\ Sans\ MT\ Ext\ Condensed\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Calisto\ MT\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Edda.widths=9, 10, 9, 10, 8, 9, 9, 10, 5, 5, 9, 8, 13, 9, 8, 10, 9, 9, 8, 8, 9, 9, 14, 9, 10, 9, 9, 9, 9, 10, 9, 9, 10, 10, 5, 6, 10, 8, 11, 10, 10, 9, 10, 9, 9, 9, 10, 9, 12, 9, 8, 9, 9, 5, 8, 8, 8, 8, 8, 7, 8, 8,
+font.Palace\ Script\ MT.height=10
+font.Bitstream\ Vera\ Serif.height=13
+font.Onyx.widths=5, 5, 5, 5, 5, 3, 5, 5, 3, 3, 5, 3, 7, 5, 5, 5, 5, 5, 4, 4, 5, 5, 7, 5, 5, 4, 5, 5, 5, 5, 4, 4, 5, 5, 3, 4, 5, 4, 7, 6, 5, 5, 5, 6, 5, 5, 5, 5, 7, 5, 5, 5, 5, 3, 4, 4, 5, 5, 5, 4, 5, 5,
+font.Playbill.widths=4, 4, 3, 4, 3, 3, 3, 4, 2, 2, 4, 2, 5, 4, 3, 4, 4, 3, 3, 3, 4, 4, 5, 4, 4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 6, 5, 4, 4, 4, 5, 4, 4, 4, 4, 5, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Bookman\ Old\ Style\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Vladimir\ Script.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tempus\ Sans\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Algerian.height=14
+font.Arial\ Narrow\ Special\ G1\ Italic.height=14
+font.Cooper\ Black.height=13
+font.Chiller.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Fax\ Italic.height=14
+font.Bitstream\ Vera\ Sans\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Informal\ Roman.height=13
+font.Kunstler\ Script.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.monospaced.plain.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Script\ MT\ Bold.widths=5, 4, 4, 5, 4, 4, 5, 5, 2, 4, 5, 3, 7, 5, 4, 6, 5, 4, 4, 3, 5, 4, 7, 6, 5, 5, 7, 8, 6, 8, 5, 6, 6, 9, 6, 5, 8, 6, 10, 9, 6, 7, 6, 8, 6, 7, 8, 7, 10, 7, 8, 6, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Juice\ ITC.widths=5, 5, 4, 5, 4, 4, 4, 5, 3, 3, 4, 3, 6, 5, 4, 5, 5, 4, 4, 4, 5, 4, 6, 4, 5, 4, 5, 4, 4, 4, 4, 4, 4, 5, 3, 4, 4, 4, 5, 5, 5, 4, 5, 4, 4, 4, 5, 5, 5, 5, 4, 4, 5, 3, 4, 4, 4, 4, 5, 4, 4, 5,
+font.Calisto\ MT\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Georgia\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tahoma.widths=6, 6, 5, 6, 6, 4, 6, 6, 3, 4, 7, 3, 9, 6, 6, 6, 6, 5, 5, 4, 6, 6, 9, 6, 6, 5, 8, 7, 7, 8, 6, 6, 7, 8, 5, 5, 7, 6, 9, 7, 8, 7, 8, 7, 6, 7, 7, 7, 10, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Special\ G2\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bookshelf\ Symbol\ 4.widths=9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+font.Tw\ Cen\ MT\ Bold\ Italic.height=14
+font.High\ Tower\ Text\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Demibold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Enviro.widths=5, 5, 6, 5, 6, 4, 6, 5, 3, 3, 5, 4, 9, 5, 5, 5, 6, 4, 5, 4, 6, 5, 7, 4, 5, 6, 7, 8, 7, 7, 8, 6, 7, 7, 3, 7, 8, 8, 8, 7, 8, 8, 8, 8, 9, 7, 7, 7, 9, 6, 8, 7, 7, 3, 8, 8, 8, 8, 8, 8, 8, 8,
+font.Edwardian\ Script\ ITC.height=13
+font.Gigi.height=15
+font.Abadi\ MT\ Condensed\ Light.widths=5, 5, 5, 5, 5, 3, 5, 5, 3, 3, 5, 3, 7, 5, 5, 5, 5, 4, 5, 3, 5, 5, 6, 5, 5, 5, 5, 6, 5, 6, 5, 5, 5, 6, 3, 4, 5, 5, 7, 6, 6, 6, 6, 6, 5, 5, 6, 5, 7, 6, 6, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Goudy\ Old\ Style\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Heavy.height=13
+font.Stop.widths=7, 6, 6, 7, 6, 6, 7, 6, 4, 5, 6, 5, 9, 7, 8, 6, 8, 6, 6, 6, 7, 7, 9, 7, 7, 6, 7, 6, 6, 7, 6, 6, 7, 6, 4, 5, 6, 5, 9, 7, 8, 6, 8, 6, 6, 6, 7, 7, 9, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Andy\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Haettenschweiler.widths=5, 5, 5, 5, 5, 4, 5, 5, 3, 3, 5, 3, 7, 5, 5, 5, 5, 5, 5, 4, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 4, 4, 5, 5, 3, 5, 5, 4, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 6, 5, 5, 5, 3, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Maiandra\ GD.widths=5, 6, 5, 6, 5, 4, 5, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 4, 4, 6, 6, 9, 5, 6, 5, 8, 7, 7, 8, 6, 6, 8, 9, 4, 5, 7, 5, 10, 8, 9, 6, 9, 7, 5, 7, 8, 7, 11, 7, 7, 6, 6, 4, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Franklin\ Gothic\ Heavy\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.sansserif.bolditalic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.dialoginput.italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Ransom\ Bold.height=14
+font.Britannic\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Edwardian\ Script\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Trebuchet\ MS\ Italic.height=14
+font.Californian\ FB.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Baskerville\ Old\ Face.height=11
+font.Times\ New\ Roman\ Special\ G1\ Bold\ Italic.height=14
+font.Californian\ FB\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Book\ Antiqua\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bernard\ MT\ Condensed.widths=6, 6, 5, 6, 5, 4, 6, 6, 4, 4, 6, 4, 8, 6, 6, 6, 6, 5, 4, 4, 6, 5, 7, 5, 5, 5, 6, 6, 5, 6, 5, 5, 6, 7, 4, 4, 6, 5, 7, 6, 6, 6, 6, 6, 5, 6, 5, 6, 8, 6, 6, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Lucida\ Bright\ Demibold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ MT\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans\ Mono\ Oblique.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Freestyle\ Script.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Century\ Schoolbook.widths=7, 7, 5, 7, 6, 4, 6, 7, 4, 4, 7, 4, 10, 7, 6, 7, 7, 5, 6, 5, 7, 6, 9, 6, 6, 6, 8, 8, 8, 9, 8, 8, 9, 9, 5, 7, 9, 8, 10, 9, 9, 8, 9, 8, 7, 8, 9, 8, 11, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Typewriter\ Oblique.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Mistral.height=11
+font.Bitstream\ Vera\ Sans\ Bold\ Oblique.height=14
+font.Arial\ Special\ G2\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Georgia\ Ref.height=13
+font.Bitstream\ Vera\ Sans\ Mono\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Ransom.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookshelf\ Symbol\ 1.height=13
+font.Century\ Gothic\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Century\ Gothic.height=14
+font.Wingdings\ 2.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ MT.widths=5, 6, 5, 6, 6, 3, 5, 6, 3, 3, 6, 3, 10, 6, 6, 6, 6, 4, 4, 4, 6, 5, 8, 6, 5, 5, 8, 7, 8, 8, 6, 6, 8, 8, 3, 3, 7, 6, 9, 8, 9, 7, 9, 7, 6, 7, 8, 7, 12, 8, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Ransom\ Bold\ Italic.height=14
+font.Times\ New\ Roman\ Special\ G1\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Monotype\ Sorts.height=10
+font.Book\ Antiqua.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ MT\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Maiandra\ GD\ Demi\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Franklin\ Gothic\ Heavy\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Castellar.height=13
+font.sansserif.plain.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.OpenSymbol.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tw\ Cen\ MT\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G1\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Gradl.widths=4, 4, 3, 4, 3, 2, 4, 4, 2, 2, 4, 2, 5, 4, 4, 4, 4, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 6, 4, 5, 4, 4, 4, 4, 4, 5, 4, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+font.Arial\ Narrow\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Parade.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.ProFont.height=14
+font.Perpetua\ Bold.height=14
+font.Wingdings\ 3.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Calisto\ MT\ Italic.height=14
+font.Lucida\ Bright\ Regular.height=14
+font.Bell\ MT\ Italic.height=14
+font.Kristen\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Typewriter\ Bold\ Oblique.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Demi\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Rage\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Georgia\ Italic.height=14
+font.MS\ Outlook.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Goudy\ Old\ Style\ Italic.height=14
+font.Lucida\ Sans\ Typewriter\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Perpetua\ Titling\ MT\ Light.height=14
+font.Footlight\ MT\ Light.widths=6, 6, 5, 6, 5, 4, 6, 7, 4, 4, 6, 4, 9, 7, 6, 6, 6, 5, 5, 4, 7, 6, 9, 6, 6, 5, 7, 6, 7, 8, 6, 5, 8, 8, 4, 4, 7, 6, 10, 8, 8, 6, 8, 6, 5, 6, 8, 7, 11, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Augsburger\ Initials.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Courier\ New\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.monospaced.bolditalic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookman\ Old\ Style\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Century\ Gothic\ Bold.height=14
+font.Vladimir\ Script.height=13
+font.Franklin\ Gothic\ Demi\ Cond.height=13
+font.Century\ Schoolbook\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Narrow\ Bold.height=14
+font.Times\ New\ Roman\ Special\ G1.widths=8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.OpenSymbol.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Wide\ Latin.height=11
+font.Showcard\ Gothic.height=14
+font.Californian\ FB.widths=4, 6, 5, 5, 5, 4, 6, 5, 3, 3, 6, 3, 7, 5, 5, 6, 5, 5, 4, 4, 5, 5, 8, 5, 5, 5, 7, 6, 7, 7, 7, 6, 7, 8, 4, 4, 7, 6, 10, 8, 7, 6, 7, 7, 5, 6, 8, 7, 11, 7, 6, 7, 5, 4, 5, 5, 6, 5, 5, 5, 5, 5,
+font.Tw\ Cen\ MT\ Bold.height=14
+font.Bitstream\ Vera\ Sans.widths=7, 7, 6, 7, 7, 4, 7, 7, 3, 3, 7, 3, 11, 7, 7, 7, 7, 5, 6, 5, 7, 7, 9, 7, 7, 5, 9, 7, 7, 8, 6, 6, 8, 8, 3, 3, 7, 6, 9, 8, 8, 7, 8, 8, 7, 7, 8, 9, 11, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Palatino\ Linotype\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.sansserif.italic.widths=7, 7, 6, 7, 7, 4, 7, 7, 4, 3, 6, 4, 8, 7, 7, 7, 7, 4, 6, 4, 7, 6, 9, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 3, 6, 8, 7, 9, 8, 9, 8, 9, 8, 8, 7, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Footlight\ MT\ Light.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Verdana.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Narrow\ Italic.height=14
+font.monospaced.italic.height=14
+font.Book\ Antiqua\ Italic.height=14
+font.Arial\ Narrow.widths=5, 5, 5, 5, 5, 3, 5, 5, 2, 2, 5, 2, 7, 5, 5, 5, 5, 4, 5, 3, 5, 5, 6, 5, 5, 4, 6, 6, 6, 6, 5, 5, 6, 6, 2, 5, 6, 5, 7, 6, 6, 5, 6, 6, 5, 5, 6, 5, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Lucida\ Bright\ Demibold\ Italic.height=14
+font.Times\ New\ Roman.widths=5, 6, 4, 6, 4, 4, 5, 6, 3, 3, 5, 3, 8, 6, 5, 6, 6, 4, 4, 3, 6, 5, 7, 6, 5, 3, 7, 7, 7, 7, 7, 6, 8, 8, 4, 5, 8, 7, 9, 7, 8, 6, 9, 7, 6, 7, 7, 7, 10, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Arial.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Handwriting\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Sans\ Unicode.height=16
+font.Ravie.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.serif.italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Andy\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Century\ Schoolbook\ Bold.height=14
+font.Times\ New\ Roman\ Special\ G2\ Italic.height=14
+font.Arial\ Special\ G1.height=16
+font.Arial\ Narrow\ Special\ G1.height=16
+font.Bookman\ Old\ Style\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Special\ G2.height=16
+font.Rockwell\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Century\ Gothic\ Bold\ Italic.height=14
+font.sansserif.plain.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Tw\ Cen\ MT\ Medium.height=14
+font.Parchment.height=12
+font.Bitstream\ Vera\ Serif\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Informal\ Roman.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Fax\ Regular.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Ransom\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Narrow\ Special\ G1\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Showcard\ Gothic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.OCRB.widths=7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Jokerman.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Colonna\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Eras\ Light\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Fax\ Regular.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Copperplate\ Gothic\ Bold.widths=7, 7, 7, 7, 6, 6, 7, 7, 4, 5, 7, 6, 9, 7, 7, 7, 7, 7, 6, 4, 7, 7, 9, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 9, 4, 6, 8, 7, 9, 9, 9, 8, 9, 8, 7, 6, 8, 7, 13, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Pepita\ MT.widths=5, 5, 4, 6, 4, 4, 6, 5, 3, 4, 5, 4, 7, 5, 4, 6, 5, 4, 4, 4, 5, 5, 8, 6, 5, 6, 10, 9, 8, 11, 6, 9, 10, 9, 5, 5, 11, 8, 12, 9, 9, 9, 12, 9, 9, 9, 10, 9, 12, 9, 9, 11, 5, 4, 6, 5, 7, 6, 5, 6, 6, 7,
+font.Eras\ Bold\ ITC.widths=6, 7, 5, 7, 6, 4, 7, 7, 3, 3, 6, 3, 9, 7, 6, 7, 7, 4, 5, 4, 7, 6, 9, 6, 6, 5, 8, 7, 7, 8, 7, 6, 8, 8, 3, 5, 8, 6, 9, 8, 9, 6, 9, 7, 6, 6, 8, 8, 11, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Franklin\ Gothic\ Demi.widths=5, 5, 5, 5, 5, 3, 6, 5, 3, 3, 5, 3, 8, 5, 5, 5, 5, 3, 5, 4, 5, 5, 7, 5, 4, 4, 6, 7, 6, 6, 6, 6, 6, 6, 3, 4, 6, 5, 9, 6, 6, 6, 6, 7, 6, 5, 7, 6, 9, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Rockwell\ Extra\ Bold.height=13
+font.monospaced.plain.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Centaur.widths=5, 6, 5, 6, 5, 4, 5, 6, 3, 3, 6, 3, 8, 6, 6, 6, 6, 4, 4, 4, 6, 5, 7, 5, 5, 5, 7, 6, 7, 8, 7, 6, 8, 9, 5, 5, 7, 7, 10, 9, 8, 6, 8, 8, 6, 8, 8, 8, 11, 8, 8, 7, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Tw\ Cen\ MT\ Condensed\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Harlow\ Solid\ Italic\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Calligraphy\ Italic.height=14
+font.Palatino\ Linotype\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Courier\ New\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.dialoginput.bold.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Broadway.height=13
+font.Franklin\ Gothic\ Book\ Italic.height=14
+font.Marlett.height=10
+font.Trebuchet\ MS.height=13
+font.Arial\ Narrow\ Special\ G2\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Webdings.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Monotype\ Corsiva.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.High\ Tower\ Text.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gloucester\ MT\ Extra\ Condensed.height=13
+font.Rage\ Italic.height=14
+font.Abadi\ MT\ Condensed\ Extra\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookshelf\ Symbol\ 3.widths=7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Braggadocio.height=10
+font.Microsoft\ Sans\ Serif.widths=7, 7, 6, 7, 7, 4, 7, 7, 3, 3, 6, 3, 9, 7, 7, 7, 7, 4, 6, 4, 7, 7, 9, 6, 6, 6, 8, 8, 8, 9, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 6, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Curlz\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Goudy\ Stout.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.sansserif.bolditalic.widths=6, 6, 6, 6, 6, 3, 6, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 6, 3, 6, 6, 8, 6, 6, 5, 7, 7, 7, 7, 7, 6, 8, 7, 3, 6, 7, 6, 9, 7, 8, 7, 8, 7, 7, 6, 7, 7, 9, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Bookshelf\ Symbol\ 5.height=13
+font.Edda.height=16
+font.Vivaldi\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bookshelf\ Symbol\ 1.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Georgia.height=13
+font.Bell\ MT\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Franklin\ Gothic\ Medium\ Cond.height=13
+font.Bookman\ Old\ Style.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Augsburger\ Initials.height=12
+font.Beesknees\ ITC.widths=7, 7, 6, 7, 6, 6, 7, 7, 4, 4, 7, 5, 8, 7, 8, 6, 8, 7, 5, 5, 7, 7, 9, 7, 7, 7, 9, 8, 7, 8, 7, 7, 8, 8, 5, 5, 8, 6, 10, 8, 9, 7, 9, 8, 6, 6, 8, 9, 10, 9, 8, 8, 9, 5, 7, 6, 7, 6, 8, 6, 7, 8,
+font.Perpetua\ Titling\ MT\ Light.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bell\ MT.height=12
+font.Arial\ Narrow\ Special\ G1\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G2\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Juice\ ITC.height=13
+font.Trebuchet\ MS\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Rounded\ MT\ Bold.widths=6, 6, 6, 6, 6, 3, 6, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 5, 4, 6, 5, 8, 5, 5, 5, 7, 7, 7, 7, 7, 6, 8, 8, 3, 6, 7, 6, 8, 8, 8, 7, 8, 7, 7, 6, 8, 7, 9, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Arial\ Special\ G2\ Italic.height=14
+font.Chiller.height=13
+font.Gill\ Sans\ MT\ Bold\ Italic.height=14
+font.Bookshelf\ Symbol\ 2.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Abadi\ MT\ Condensed\ Light.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Demibold\ Italic.height=14
+font.Ransom\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Goudy\ Stout.widths=14, 13, 11, 12, 13, 13, 13, 16, 11, 12, 16, 13, 18, 13, 13, 12, 11, 15, 11, 14, 14, 13, 18, 14, 14, 14, 14, 13, 11, 12, 13, 13, 13, 16, 11, 12, 16, 13, 18, 13, 13, 12, 11, 15, 11, 14, 14, 13, 18, 14, 14, 14, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+font.Trebuchet\ MS\ Bold\ Italic.height=14
+font.Elephant\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Narrow\ Special\ G2\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Pacmania.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Broadway.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Pristina.widths=5, 5, 4, 5, 4, 3, 5, 5, 3, 2, 5, 2, 7, 5, 4, 5, 5, 4, 4, 3, 5, 5, 6, 5, 5, 4, 7, 8, 7, 8, 8, 7, 7, 9, 6, 6, 9, 6, 11, 8, 8, 7, 8, 8, 6, 7, 9, 8, 11, 8, 8, 6, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Courier\ New.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Abadi\ MT\ Condensed\ Extra\ Bold.widths=4, 5, 4, 5, 5, 3, 5, 5, 3, 3, 4, 3, 7, 5, 5, 5, 5, 3, 4, 3, 5, 4, 6, 4, 4, 4, 5, 5, 4, 5, 5, 4, 5, 5, 3, 3, 5, 4, 6, 5, 6, 5, 6, 5, 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Copperplate\ Gothic\ Light.widths=7, 7, 8, 8, 7, 7, 8, 8, 4, 6, 7, 7, 8, 8, 9, 7, 9, 7, 7, 6, 8, 7, 10, 7, 6, 6, 8, 9, 9, 9, 8, 8, 9, 9, 4, 7, 8, 8, 10, 9, 10, 8, 10, 9, 8, 8, 9, 8, 12, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.dialog.plain.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Poor\ Richard.height=13
+font.Franklin\ Gothic\ Medium.widths=6, 6, 6, 6, 6, 4, 6, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 6, 4, 6, 5, 8, 5, 5, 5, 7, 7, 7, 8, 6, 6, 7, 7, 4, 5, 7, 6, 9, 8, 7, 7, 7, 7, 7, 6, 7, 6, 10, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.serif.bolditalic.height=14
+font.Bookshelf\ Symbol\ 3.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Comic\ Sans\ MS\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Bright\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Runic\ MT\ Condensed.widths=5, 5, 5, 5, 5, 3, 5, 5, 3, 3, 4, 3, 7, 5, 5, 5, 5, 5, 5, 3, 5, 5, 7, 5, 5, 4, 5, 6, 6, 6, 6, 4, 6, 6, 3, 5, 5, 6, 7, 6, 6, 6, 6, 5, 5, 5, 6, 5, 7, 5, 5, 5, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5,
+font.Perpetua\ Titling\ MT\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ Ultra\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Verdana.height=14
+font.Goudy\ Old\ Style.height=13
+font.monospaced.bolditalic.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Goudy\ Old\ Style.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans\ Mono\ Oblique.height=14
+font.Modern\ No.\ 20.height=12
+font.Matura\ MT\ Script\ Capitals.widths=6, 6, 5, 6, 5, 5, 6, 6, 4, 4, 6, 4, 8, 6, 6, 6, 6, 5, 6, 4, 6, 6, 9, 6, 6, 6, 14, 12, 10, 14, 9, 13, 9, 9, 9, 13, 13, 13, 16, 15, 13, 10, 12, 11, 10, 10, 12, 13, 15, 15, 12, 14, 8, 5, 6, 5, 7, 6, 6, 6, 6, 6,
+font.Viner\ Hand\ ITC.height=17
+font.Forte.height=15
+font.Placard\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Medium.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Perpetua\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Placard\ Condensed.widths=5, 5, 5, 5, 5, 3, 5, 5, 3, 3, 5, 3, 6, 5, 5, 5, 5, 3, 4, 3, 5, 4, 6, 5, 4, 3, 5, 5, 5, 5, 4, 4, 5, 5, 3, 3, 5, 4, 6, 5, 5, 5, 5, 5, 5, 4, 5, 5, 6, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Jokerman.widths=7, 7, 6, 7, 7, 4, 7, 8, 4, 4, 7, 4, 9, 7, 7, 7, 7, 6, 5, 5, 7, 6, 8, 7, 7, 7, 9, 8, 8, 8, 7, 6, 9, 8, 4, 7, 8, 8, 10, 10, 9, 8, 9, 8, 7, 8, 9, 8, 12, 9, 8, 8, 8, 5, 7, 8, 8, 7, 7, 7, 7, 8,
+font.Franklin\ Gothic\ Heavy.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Black.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Harrington.widths=6, 6, 6, 7, 6, 4, 6, 6, 4, 4, 6, 4, 8, 6, 7, 7, 6, 5, 6, 4, 7, 6, 8, 5, 7, 6, 8, 8, 7, 8, 8, 7, 8, 8, 4, 4, 7, 7, 9, 8, 8, 8, 8, 8, 7, 6, 8, 7, 10, 7, 7, 7, 7, 4, 6, 6, 7, 6, 6, 5, 6, 6,
+font.Felix\ Titling.height=13
+font.Comic\ Sans\ MS\ Bold.height=14
+font.Papyrus.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Curlz\ MT.widths=5, 5, 5, 5, 5, 4, 5, 6, 3, 3, 5, 3, 8, 6, 6, 6, 6, 5, 4, 4, 6, 5, 8, 5, 5, 5, 7, 7, 6, 8, 6, 6, 7, 8, 4, 5, 7, 6, 9, 9, 8, 7, 8, 7, 6, 6, 8, 7, 10, 7, 8, 6, 6, 4, 5, 5, 6, 6, 6, 5, 5, 6,
+font.Century\ Schoolbook\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ MT\ Ext\ Condensed\ Bold.widths=3, 3, 2, 3, 3, 2, 3, 3, 1, 2, 3, 2, 4, 3, 3, 3, 3, 2, 2, 2, 3, 3, 4, 3, 3, 2, 3, 3, 3, 3, 2, 2, 3, 3, 2, 2, 3, 2, 4, 3, 3, 3, 3, 3, 2, 3, 3, 3, 5, 3, 3, 2, 3, 2, 2, 2, 3, 2, 3, 2, 3, 3,
+font.French\ Script\ MT.height=13
+font.Tw\ Cen\ MT\ Condensed\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Freestyle\ Script.widths=3, 3, 4, 4, 4, 5, 6, 6, 2, 3, 5, 4, 4, 4, 4, 3, 5, 3, 4, 4, 5, 4, 6, 3, 4, 3, 5, 4, 5, 6, 5, 5, 5, 6, 4, 5, 6, 4, 6, 6, 5, 5, 5, 6, 5, 6, 6, 6, 7, 5, 5, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 4,
+font.Lucida\ Sans\ Demibold\ Roman.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bookshelf\ Symbol\ 4.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Goudy\ Old\ Style\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Braggadocio.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Regular.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gloucester\ MT\ Extra\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Special\ G2\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Fax\ Demibold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Special\ G1\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Perpetua\ Titling\ MT\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Century\ Schoolbook\ Bold\ Italic.height=14
+font.Calisto\ MT.height=13
+font.Lucida\ Sans\ Demibold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.dialoginput.bolditalic.height=14
+font.Poor\ Richard.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Ravie.widths=9, 9, 8, 9, 8, 8, 9, 9, 6, 8, 9, 5, 13, 9, 8, 9, 9, 9, 8, 9, 9, 9, 12, 9, 10, 9, 10, 9, 8, 9, 10, 9, 9, 10, 6, 9, 11, 9, 14, 9, 9, 9, 9, 12, 9, 10, 10, 11, 14, 10, 10, 10, 8, 6, 9, 9, 11, 9, 9, 8, 8, 9,
+font.Arial\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.MT\ Extra.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Bitstream\ Vera\ Sans\ Mono\ Bold\ Oblique.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Eras\ Medium\ ITC.widths=6, 7, 6, 7, 6, 4, 7, 7, 3, 3, 6, 3, 10, 7, 7, 7, 7, 5, 5, 4, 7, 6, 9, 6, 6, 5, 8, 7, 7, 8, 7, 6, 8, 8, 3, 5, 7, 6, 10, 9, 9, 6, 9, 7, 6, 6, 8, 8, 11, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Bright\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Bright\ Demibold.height=14
+font.Pepita\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Wingdings.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookshelf\ Symbol\ 5.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.dialoginput.bolditalic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Book\ Antiqua.height=13
+font.Franklin\ Gothic\ Demi\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Comic\ Sans\ MS.widths=6, 6, 5, 6, 6, 5, 5, 6, 3, 4, 5, 3, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 6, 6, 5, 7, 6, 6, 7, 6, 6, 7, 8, 5, 7, 6, 6, 9, 8, 8, 5, 9, 6, 7, 7, 7, 7, 10, 7, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Desdemona.widths=6, 6, 6, 6, 6, 6, 7, 7, 3, 5, 6, 6, 8, 7, 8, 6, 7, 6, 6, 6, 7, 6, 7, 6, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 3, 5, 6, 6, 8, 7, 8, 6, 7, 6, 6, 6, 7, 6, 7, 6, 5, 6, 5, 3, 5, 6, 5, 5, 5, 5, 5, 6,
+font.sansserif.italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Wingdings\ 3.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.High\ Tower\ Text.widths=5, 7, 5, 6, 5, 5, 6, 7, 4, 4, 6, 4, 10, 7, 6, 7, 6, 5, 6, 4, 7, 6, 9, 6, 6, 6, 9, 7, 9, 9, 7, 6, 9, 8, 4, 5, 8, 7, 10, 9, 10, 7, 10, 7, 7, 8, 9, 9, 11, 8, 8, 9, 6, 4, 5, 5, 6, 5, 6, 6, 6, 6,
+font.Monotype\ Sorts\ 2.height=13
+font.Bookman\ Old\ Style\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Engravers\ MT.widths=10, 10, 10, 11, 9, 9, 11, 11, 6, 8, 11, 9, 12, 11, 11, 9, 11, 11, 9, 10, 11, 10, 13, 11, 11, 9, 10, 10, 10, 11, 9, 9, 11, 11, 6, 8, 11, 9, 12, 11, 11, 9, 11, 11, 9, 10, 11, 10, 13, 11, 11, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Special\ G1\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Maiandra\ GD.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Special\ G1\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Eurostile.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.serif.bold.height=14
+font.High\ Tower\ Text\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.dialog.bold.widths=6, 6, 6, 6, 6, 3, 6, 6, 3, 4, 6, 3, 9, 6, 6, 6, 6, 4, 6, 3, 6, 7, 9, 6, 5, 5, 7, 7, 7, 7, 7, 6, 8, 7, 3, 6, 7, 6, 9, 7, 8, 7, 8, 7, 7, 5, 7, 7, 9, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Book\ Antiqua\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Snap\ ITC.height=14
+font.Trebuchet\ MS\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Rockwell\ Condensed.widths=5, 5, 4, 5, 4, 3, 5, 5, 3, 3, 5, 3, 8, 5, 5, 5, 5, 3, 4, 3, 5, 4, 6, 4, 4, 4, 6, 5, 5, 6, 5, 5, 5, 6, 4, 3, 6, 5, 7, 6, 6, 5, 5, 5, 4, 5, 5, 6, 7, 6, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+font.Cooper\ Black.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G2\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ Ultra\ Bold.widths=7, 8, 6, 8, 7, 6, 8, 8, 4, 5, 8, 4, 12, 8, 7, 8, 8, 7, 6, 6, 8, 7, 10, 8, 7, 7, 10, 9, 8, 9, 7, 7, 9, 10, 4, 6, 9, 7, 11, 9, 9, 8, 9, 9, 7, 7, 9, 8, 12, 10, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+font.Bitstream\ Vera\ Serif.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans\ Oblique.height=14
+font.Elephant.height=13
+font.Eurostile\ Bold.height=14
+font.American\ Uncial.widths=8, 7, 7, 9, 8, 8, 8, 8, 4, 6, 7, 5, 13, 8, 8, 8, 8, 8, 6, 7, 8, 8, 12, 9, 8, 8, 8, 7, 7, 9, 8, 8, 8, 8, 4, 6, 7, 5, 13, 8, 8, 8, 8, 8, 6, 7, 8, 8, 12, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.Arial\ Narrow\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Centaur.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Monotype\ Corsiva.height=12
+font.Bookman\ Old\ Style.height=13
+font.Wingdings.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Lucida\ Sans\ Demibold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ MT\ Condensed.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans\ Mono.height=13
+font.Times\ New\ Roman.height=13
+font.Arial\ Narrow\ Special\ G2\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Matura\ MT\ Script\ Capitals.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Calisto\ MT\ Bold.height=14
+font.Bitstream\ Vera\ Sans\ Mono\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.serif.italic.widths=6, 6, 5, 6, 5, 4, 6, 6, 4, 4, 5, 4, 8, 6, 6, 6, 6, 5, 5, 4, 6, 5, 8, 5, 5, 5, 7, 7, 8, 8, 7, 7, 8, 8, 4, 5, 8, 7, 9, 8, 8, 7, 8, 7, 6, 7, 8, 7, 10, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Bauhaus\ 93.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.dialog.bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Onyx.height=11
+font.Playbill.height=11
+font.Tahoma\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Handwriting\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Tahoma.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.monospaced.italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Calisto\ MT\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Georgia\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Verdana\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Georgia\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Kino\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.monospaced.plain.height=14
+font.Eurostile.widths=6, 6, 6, 6, 6, 4, 6, 6, 3, 3, 5, 3, 9, 6, 6, 6, 6, 5, 6, 4, 6, 5, 8, 5, 5, 5, 7, 7, 7, 8, 6, 6, 7, 8, 3, 6, 7, 6, 10, 8, 7, 7, 8, 7, 7, 6, 8, 7, 11, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Sans\ Regular.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Script\ MT\ Bold.height=13
+font.Tahoma.height=13
+font.Bookshelf\ Symbol\ 4.height=13
+font.dialog.bolditalic.widths=6, 6, 6, 6, 6, 3, 6, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 6, 3, 6, 6, 8, 6, 6, 5, 7, 7, 7, 7, 7, 6, 8, 7, 3, 6, 7, 6, 9, 7, 8, 7, 8, 7, 7, 6, 7, 7, 9, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Rockwell\ Extra\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Maiandra\ GD\ Demi\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Enviro.height=13
+font.Abadi\ MT\ Condensed\ Light.height=12
+font.Centaur.height=12
+font.Tw\ Cen\ MT\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Garamond.widths=5, 6, 5, 5, 5, 3, 5, 6, 3, 3, 5, 3, 8, 6, 5, 6, 5, 3, 4, 3, 6, 5, 7, 5, 5, 5, 7, 7, 7, 8, 7, 6, 7, 8, 4, 4, 7, 6, 9, 7, 8, 6, 8, 7, 5, 6, 7, 6, 9, 6, 7, 7, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Bell\ MT\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Maiandra\ GD\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Stop.height=9
+font.Andy\ Bold.height=14
+font.Palatino\ Linotype.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Symbol.widths=7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Courier\ New\ Bold.height=14
+font.Haettenschweiler.height=12
+font.Maiandra\ GD.height=13
+font.Franklin\ Gothic\ Heavy\ Italic.height=14
+font.dialoginput.bold.height=14
+font.OCR\ A\ Extended.widths=7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Verdana\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.MT\ Extra.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Italic.height=14
+font.Script\ MT\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Georgia\ Ref.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Sans\ Typewriter\ Regular.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bookman\ Old\ Style\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.sansserif.bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Trebuchet\ MS\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Californian\ FB\ Italic.height=14
+font.Bernard\ MT\ Condensed.height=13
+font.Arial\ Special\ G1\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Comic\ Sans\ MS.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Parade.widths=6, 7, 6, 7, 6, 4, 6, 7, 4, 4, 6, 4, 9, 7, 6, 6, 7, 5, 5, 4, 7, 7, 9, 7, 8, 6, 8, 7, 7, 8, 7, 7, 8, 8, 4, 7, 7, 6, 10, 8, 8, 7, 7, 7, 7, 7, 8, 9, 11, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bookman\ Old\ Style\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Jester\ Regular.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Century\ Schoolbook.height=13
+font.Lucida\ Sans\ Typewriter\ Oblique.height=14
+font.Eurostile\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans\ Mono\ Bold.height=14
+font.Garamond\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Medium\ Cond.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.dialoginput.plain.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Verdana\ Ref.widths=8, 8, 6, 8, 8, 5, 8, 8, 4, 4, 8, 4, 12, 8, 8, 8, 8, 5, 7, 5, 8, 7, 8, 8, 7, 7, 8, 8, 9, 9, 7, 7, 9, 9, 6, 6, 8, 7, 10, 8, 10, 8, 10, 9, 8, 8, 9, 8, 12, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.Papyrus.widths=6, 8, 6, 7, 7, 5, 6, 7, 4, 4, 6, 4, 10, 7, 7, 6, 7, 5, 5, 5, 7, 5, 6, 5, 6, 5, 11, 10, 11, 11, 11, 8, 11, 11, 4, 8, 10, 8, 11, 10, 13, 8, 12, 9, 11, 11, 11, 9, 12, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.OCRB.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Matisse\ ITC.widths=5, 5, 6, 5, 5, 5, 5, 5, 3, 5, 7, 5, 12, 8, 5, 5, 5, 5, 5, 5, 6, 9, 10, 5, 5, 5, 5, 5, 4, 5, 4, 5, 5, 5, 3, 5, 6, 5, 9, 7, 5, 6, 6, 6, 4, 6, 5, 5, 7, 7, 8, 5, 6, 4, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Arial\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Special\ G2\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tempus\ Sans\ ITC.widths=5, 6, 5, 6, 6, 4, 6, 6, 4, 4, 6, 4, 10, 7, 7, 6, 6, 4, 4, 5, 7, 6, 8, 6, 6, 5, 9, 6, 7, 8, 6, 6, 8, 9, 4, 5, 8, 6, 10, 9, 9, 7, 9, 7, 5, 6, 8, 8, 11, 7, 7, 7, 8, 5, 7, 6, 7, 6, 6, 6, 6, 6,
+font.Gill\ Sans\ MT.height=13
+font.Arial\ Narrow\ Special\ G1\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gill\ Sans\ MT\ Bold.height=14
+font.Century\ Gothic\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Onyx.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Modern\ No.\ 20.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Maiandra\ GD\ Demi\ Bold.height=14
+font.Garamond.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tw\ Cen\ MT\ Condensed\ Extra\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Georgia\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Sans\ Italic.height=14
+font.Bitstream\ Vera\ Sans\ Bold\ Oblique.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.serif.plain.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Rockwell\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Special\ G1\ Italic.height=14
+font.Gradl.height=11
+font.Book\ Antiqua\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Calisto\ MT\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Rockwell\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Palatino\ Linotype\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Goudy\ Stout.height=15
+font.Algerian.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Eras\ Bold\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Stop.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Engravers\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Courier\ New\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Ransom.widths=6, 7, 5, 7, 5, 4, 3, 5, 5, 3, 6, 3, 6, 6, 7, 6, 5, 4, 4, 3, 6, 4, 8, 16, 4, 9, 6, 6, 8, 8, 4, 11, 7, 8, 5, 6, 8, 7, 6, 9, 6, 3, 9, 5, 7, 8, 5, 5, 11, 8, 5, 9, 8, 4, 4, 5, 4, 4, 3, 8, 10, 7,
+font.Gill\ Sans\ MT\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Pristina.height=13
+font.Old\ English\ Text\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Courier\ New.height=12
+font.Arial\ Narrow\ Special\ G1.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Verdana\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Eras\ Medium\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Garamond\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.MS\ Outlook.height=12
+font.serif.bolditalic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Gradl.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Eras\ Light\ ITC.widths=6, 7, 6, 7, 6, 4, 7, 7, 3, 3, 5, 3, 9, 7, 7, 7, 7, 4, 5, 4, 7, 6, 9, 5, 5, 5, 7, 7, 7, 8, 6, 6, 8, 8, 3, 5, 6, 6, 9, 8, 9, 6, 9, 6, 5, 6, 8, 7, 11, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Palace\ Script\ MT.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Footlight\ MT\ Light.height=10
+font.Courier\ New\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Tw\ Cen\ MT\ Medium\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Brush\ Script\ MT\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Century\ Schoolbook\ Italic.height=14
+font.Times\ New\ Roman\ Special\ G1.height=16
+font.OpenSymbol.height=14
+font.Bitstream\ Vera\ Sans\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Placard\ Condensed.height=14
+font.Californian\ FB.height=13
+font.Bell\ MT\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Century\ Gothic\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Wingdings\ 2.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Britannic\ Bold.widths=5, 5, 5, 5, 5, 3, 5, 6, 3, 3, 5, 3, 8, 6, 5, 5, 5, 4, 5, 3, 6, 4, 7, 5, 5, 4, 5, 6, 6, 7, 5, 5, 6, 7, 3, 4, 6, 5, 7, 6, 6, 6, 6, 6, 5, 5, 6, 5, 8, 6, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.dialog.italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bitstream\ Vera\ Sans.height=13
+font.Arial\ Narrow\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Palatino\ Linotype\ Bold.height=14
+font.Goudy\ Old\ Style\ Bold.height=14
+font.sansserif.italic.height=14
+font.Arial\ Black\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Perpetua\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Enviro.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Perpetua\ Titling\ MT\ Bold.height=14
+font.Eras\ Demi\ ITC.widths=6, 6, 5, 6, 6, 3, 6, 6, 3, 3, 5, 3, 9, 6, 6, 6, 6, 4, 4, 4, 6, 5, 9, 6, 5, 5, 8, 7, 6, 8, 6, 6, 8, 8, 3, 5, 7, 5, 9, 8, 8, 6, 8, 6, 5, 6, 8, 7, 11, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Arial\ Narrow.height=13
+font.Blackadder\ ITC.widths=5, 5, 4, 5, 4, 4, 5, 5, 3, 4, 5, 3, 6, 5, 4, 5, 5, 4, 4, 4, 6, 5, 6, 5, 5, 5, 9, 9, 9, 9, 9, 7, 9, 10, 6, 7, 11, 8, 13, 9, 9, 8, 11, 10, 8, 6, 11, 10, 12, 11, 9, 10, 5, 4, 4, 4, 5, 5, 5, 5, 5, 5,
+font.Times\ New\ Roman\ Bold\ Italic.height=14
+font.monospaced.bold.widths=6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.monospaced.bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Rockwell\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Lucida\ Sans\ Typewriter\ Bold\ Oblique.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Haettenschweiler.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.American\ Uncial.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookman\ Old\ Style\ Italic.height=14
+font.Calisto\ MT\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.sansserif.plain.height=14
+font.Comic\ Sans\ MS.height=14
+font.Bitstream\ Vera\ Serif\ Bold.height=14
+font.Arial\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Tw\ Cen\ MT\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Rockwell\ Condensed\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Georgia\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bookshelf\ Symbol\ 2.widths=5, 5, 5, 7, 7, 9, 7, 7, 3, 3, 4, 3, 10, 5, 9, 9, 9, 9, 6, 4, 7, 9, 6, 4, 4, 5, 7, 9, 6, 7, 7, 9, 7, 8, 9, 9, 5, 7, 10, 7, 9, 9, 9, 4, 6, 8, 8, 9, 9, 9, 9, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+font.serif.plain.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Narrow\ Special\ G1\ Bold.height=14
+font.Arial\ Special\ G1\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.OCRB.height=15
+font.Lucida\ Fax\ Regular.height=14
+font.Copperplate\ Gothic\ Bold.height=12
+font.Maiandra\ GD\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Special\ G2\ Bold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Brush\ Script\ MT\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Book\ Antiqua\ Bold.height=14
+font.Pepita\ MT.height=12
+font.Eras\ Bold\ ITC.height=13
+font.Franklin\ Gothic\ Demi.height=13
+font.Courier\ New\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Perpetua\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Tw\ Cen\ MT\ Condensed\ Bold.height=14
+font.Harlow\ Solid\ Italic\ Italic.height=14
+font.Century\ Schoolbook\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Courier\ New\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Times\ New\ Roman\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Palatino\ Linotype\ Italic.height=14
+font.Monotype\ Sorts.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Fax\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Franklin\ Gothic\ Medium\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Impact.widths=6, 6, 6, 6, 6, 4, 6, 6, 4, 4, 6, 4, 8, 6, 6, 6, 6, 5, 6, 4, 6, 6, 8, 4, 5, 5, 6, 7, 7, 7, 5, 5, 7, 7, 4, 4, 6, 5, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 6, 6, 5, 6, 5, 6, 6, 6, 6, 6, 5, 6, 6,
+font.Arial\ Special\ G2\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Courier\ New.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Arial\ Narrow\ Special\ G2\ Bold.height=14
+font.Times\ New\ Roman\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Lucida\ Bright\ Demibold\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Webdings.height=10
+font.Arial\ Black.widths=8, 8, 8, 8, 8, 5, 8, 8, 4, 4, 8, 4, 11, 8, 8, 8, 8, 5, 7, 5, 8, 7, 10, 8, 7, 7, 9, 9, 9, 9, 8, 8, 9, 9, 5, 8, 9, 8, 10, 9, 9, 8, 9, 9, 8, 8, 9, 9, 11, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.Lucida\ Console.widths=7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Elephant.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Stencil.widths=7, 8, 7, 7, 7, 7, 7, 9, 5, 6, 8, 6, 9, 8, 7, 7, 7, 8, 7, 7, 8, 7, 9, 7, 7, 6, 7, 8, 7, 7, 7, 7, 7, 9, 5, 6, 8, 6, 9, 8, 7, 7, 7, 8, 7, 7, 8, 7, 9, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Gill\ Sans\ MT\ Condensed.widths=4, 5, 4, 5, 4, 3, 4, 5, 3, 3, 4, 3, 6, 5, 4, 5, 5, 4, 4, 3, 5, 4, 5, 4, 4, 4, 5, 5, 4, 5, 5, 4, 5, 5, 3, 3, 5, 4, 6, 5, 5, 5, 5, 5, 4, 4, 5, 4, 7, 5, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Georgia.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Eras\ Demi\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bookshelf\ Symbol\ 3.height=14
+font.Microsoft\ Sans\ Serif.height=13
+font.Arial\ Italic.height=14
+font.Mercurius\ Script\ MT\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.sansserif.bolditalic.height=14
+font.serif.italic.height=14
+font.Vivaldi\ Italic.height=14
+font.Pacmania.widths=9, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8,
+font.Lucida\ Fax\ Demibold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.ProFont.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Perpetua\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Tw\ Cen\ MT\ Condensed\ Extra\ Bold.widths=5, 5, 3, 5, 4, 3, 5, 5, 3, 3, 5, 3, 7, 5, 5, 5, 5, 3, 4, 3, 5, 4, 6, 5, 4, 4, 5, 5, 4, 5, 4, 4, 6, 6, 3, 4, 5, 4, 7, 6, 6, 5, 6, 5, 4, 4, 6, 6, 8, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Arial.widths=6, 6, 6, 6, 6, 3, 6, 6, 3, 4, 6, 3, 9, 6, 6, 6, 6, 4, 6, 3, 6, 7, 9, 6, 5, 5, 7, 7, 7, 7, 7, 6, 8, 7, 3, 6, 7, 6, 9, 7, 8, 7, 8, 7, 7, 5, 7, 7, 9, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Bell\ MT\ Bold.height=14
+font.Calisto\ MT\ Bold\ Italic.height=14
+font.Times\ New\ Roman\ Special\ G1\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Beesknees\ ITC.height=13
+font.Verdana\ Bold.height=14
+font.Kristen\ ITC.widths=7, 7, 7, 7, 7, 6, 6, 7, 4, 4, 7, 4, 9, 7, 7, 7, 7, 5, 6, 6, 7, 6, 9, 7, 5, 6, 10, 8, 9, 10, 8, 7, 9, 10, 5, 7, 9, 8, 12, 10, 10, 8, 10, 8, 7, 8, 10, 8, 11, 8, 8, 10, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Palatino\ Linotype.widths=5, 6, 5, 6, 5, 4, 6, 6, 3, 3, 6, 3, 9, 6, 6, 6, 6, 4, 4, 3, 6, 5, 8, 5, 5, 5, 8, 7, 7, 8, 6, 6, 8, 8, 4, 4, 8, 6, 10, 8, 8, 7, 8, 7, 6, 8, 8, 8, 10, 7, 6, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.dialoginput.plain.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Kunstler\ Script.widths=4, 4, 3, 4, 3, 3, 4, 5, 3, 3, 4, 3, 7, 5, 3, 5, 4, 3, 3, 3, 4, 4, 6, 4, 5, 4, 9, 10, 9, 10, 8, 8, 11, 9, 7, 6, 9, 9, 9, 9, 8, 8, 8, 10, 7, 8, 9, 8, 10, 8, 8, 8, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4,
+font.Lucida\ Sans\ Typewriter\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Arial\ Rounded\ MT\ Bold.height=13
+font.Arial\ Black\ Italic.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Mercurius\ Script\ MT\ Bold.widths=6, 6, 4, 6, 4, 4, 6, 5, 4, 4, 5, 4, 8, 5, 5, 6, 6, 4, 4, 4, 6, 5, 8, 6, 6, 5, 8, 8, 7, 8, 7, 7, 7, 9, 5, 7, 8, 7, 11, 9, 7, 8, 7, 8, 6, 8, 9, 7, 11, 8, 8, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Webdings.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Jester\ Regular.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.sansserif.bold.widths=6, 6, 6, 6, 6, 3, 6, 6, 3, 4, 6, 3, 9, 6, 6, 6, 6, 4, 6, 3, 6, 7, 9, 6, 5, 5, 7, 7, 7, 7, 7, 6, 8, 7, 3, 6, 7, 6, 9, 7, 8, 7, 8, 7, 7, 5, 7, 7, 9, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+font.Desdemona.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Ransom\ Italic.height=14
+font.Elephant\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Verdana\ Bold\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Tahoma\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Palatino\ Linotype\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Times\ New\ Roman\ Special\ G2\ Bold.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Garamond.height=12
+font.Gill\ Sans\ MT\ Italic.widths=7, 7, 7, 7, 7, 4, 7, 7, 4, 4, 5, 4, 10, 7, 7, 7, 7, 4, 5, 4, 7, 6, 8, 6, 6, 6, 8, 8, 8, 8, 8, 7, 9, 8, 4, 6, 8, 7, 10, 8, 9, 8, 9, 8, 8, 8, 8, 8, 10, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+font.Bitstream\ Vera\ Sans\ Oblique.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Maiandra\ GD\ Italic.height=14
+font.Abadi\ MT\ Condensed\ Extra\ Bold.height=12
+font.Copperplate\ Gothic\ Light.height=12
+font.Lucida\ Bright\ Regular.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Bradley\ Hand\ ITC.widths=6, 6, 5, 6, 5, 5, 7, 6, 4, 4, 7, 4, 10, 7, 5, 6, 7, 5, 5, 4, 7, 6, 8, 6, 7, 6, 8, 8, 7, 7, 7, 6, 8, 8, 4, 4, 8, 7, 10, 9, 8, 7, 8, 8, 8, 7, 8, 7, 11, 7, 8, 10, 6, 6, 7, 7, 7, 7, 7, 7, 6, 6,
+font.dialog.plain.height=14
+font.Arial\ Special\ G2.widths=8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+font.Franklin\ Gothic\ Medium.height=13
+font.Arial\ Narrow\ Special\ G2.widths=10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+font.Lucida\ Bright\ Italic.height=14
+font.Runic\ MT\ Condensed.height=12
+font.Imprint\ MT\ Shadow.widths=5, 7, 5, 7, 5, 4, 6, 7, 4, 4, 6, 4, 10, 7, 6, 7, 7, 5, 5, 4, 7, 6, 9, 6, 6, 5, 8, 8, 8, 10, 8, 8, 9, 10, 5, 5, 9, 8, 10, 9, 9, 8, 9, 9, 7, 8, 9, 8, 11, 9, 8, 8, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6,
+font.monospaced.bolditalic.height=14
+font.Verdana\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Perpetua.widths=5, 6, 5, 6, 5, 3, 5, 6, 3, 3, 6, 3, 8, 6, 6, 6, 6, 4, 4, 3, 6, 5, 8, 6, 5, 5, 7, 6, 6, 7, 5, 5, 7, 8, 4, 4, 7, 5, 9, 7, 7, 5, 7, 7, 5, 6, 7, 6, 9, 7, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+font.Matura\ MT\ Script\ Capitals.height=14
+font.Parade.height=12
+font.Jester\ Regular.height=14
+font.Blackadder\ ITC.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Goudy\ Old\ Style\ Bold.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Perpetua\ Italic.height=14
+font.Arial\ Narrow\ Special\ G2.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+
+font.Something.height=13
+font.Something.characters=a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+font.Something.widths=6, 6, 6, 6, 6, 3, 6, 6, 3, 4, 6, 3, 9, 6, 6, 6, 6, 4, 6, 3, 6, 7, 9, 6, 5, 5, 7, 7, 7, 7, 7, 6, 8, 7, 3, 6, 7, 6, 9, 7, 8, 7, 8, 7, 7, 5, 7, 7, 9, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
diff --git a/src/testcases/org/apache/poi/hssf/model/SheetTest.java b/src/testcases/org/apache/poi/hssf/model/SheetTest.java
index 3f604e9cf5..1f4bfaf78b 100644
--- a/src/testcases/org/apache/poi/hssf/model/SheetTest.java
+++ b/src/testcases/org/apache/poi/hssf/model/SheetTest.java
@@ -3,19 +3,11 @@ package org.apache.poi.hssf.model;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
+import java.util.Iterator;
import junit.framework.TestCase;
-import org.apache.poi.hssf.record.BOFRecord;
-import org.apache.poi.hssf.record.BlankRecord;
-import org.apache.poi.hssf.record.ColumnInfoRecord;
-import org.apache.poi.hssf.record.EOFRecord;
-import org.apache.poi.hssf.record.FormulaRecord;
-import org.apache.poi.hssf.record.LabelSSTRecord;
-import org.apache.poi.hssf.record.MergeCellsRecord;
-import org.apache.poi.hssf.record.RowRecord;
-import org.apache.poi.hssf.record.SharedFormulaRecord;
-import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
import org.apache.poi.hssf.record.formula.ExpPtg;
@@ -257,8 +249,126 @@ public class SheetTest extends TestCase
}
+ /**
+ * Make sure page break functionality works (in memory)
+ *
+ */
+ public void testRowPageBreaks(){
+ short colFrom = 0;
+ short colTo = 255;
-}
+ Sheet sheet = Sheet.createSheet();
+ sheet.setRowBreak(0, colFrom, colTo);
+
+ assertTrue("no row break at 0", sheet.isRowBroken(0));
+ assertEquals("1 row break available", 1, sheet.getNumRowBreaks());
+
+ sheet.setRowBreak(0, colFrom, colTo);
+ sheet.setRowBreak(0, colFrom, colTo);
+
+ assertTrue("no row break at 0", sheet.isRowBroken(0));
+ assertEquals("1 row break available", 1, sheet.getNumRowBreaks());
+
+ sheet.setRowBreak(10, colFrom, colTo);
+ sheet.setRowBreak(11, colFrom, colTo);
+
+ assertTrue("no row break at 10", sheet.isRowBroken(10));
+ assertTrue("no row break at 11", sheet.isRowBroken(11));
+ assertEquals("3 row break available", 3, sheet.getNumRowBreaks());
+
+
+ boolean is10 = false;
+ boolean is0 = false;
+ boolean is11 = false;
+ Iterator iterator = sheet.getRowBreaks();
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ int main = (int)breakItem.main;
+ if (main != 0 && main != 10 && main != 11) fail("Invalid page break");
+ if (main == 0) is0 = true;
+ if (main == 10) is10= true;
+ if (main == 11) is11 = true;
+ }
+
+ assertTrue("one of the breaks didnt make it", is0 && is10 && is11);
+
+ sheet.removeRowBreak(11);
+ assertFalse("row should be removed", sheet.isRowBroken(11));
+
+ sheet.removeRowBreak(0);
+ assertFalse("row should be removed", sheet.isRowBroken(0));
+
+ sheet.removeRowBreak(10);
+ assertFalse("row should be removed", sheet.isRowBroken(10));
+
+ assertEquals("no more breaks", 0, sheet.getNumRowBreaks());
+
+
+ }
+
+ /**
+ * Make sure column pag breaks works properly (in-memory)
+ *
+ */
+ public void testColPageBreaks(){
+ short rowFrom = 0;
+ short rowTo = (short)65535;
+
+ Sheet sheet = Sheet.createSheet();
+ sheet.setColumnBreak((short)0, rowFrom, rowTo);
+
+ assertTrue("no col break at 0", sheet.isColumnBroken((short)0));
+ assertEquals("1 col break available", 1, sheet.getNumColumnBreaks());
+
+ sheet.setColumnBreak((short)0, rowFrom, rowTo);
+
+ assertTrue("no col break at 0", sheet.isColumnBroken((short)0));
+ assertEquals("1 col break available", 1, sheet.getNumColumnBreaks());
+
+ sheet.setColumnBreak((short)1, rowFrom, rowTo);
+ sheet.setColumnBreak((short)10, rowFrom, rowTo);
+ sheet.setColumnBreak((short)15, rowFrom, rowTo);
+
+ assertTrue("no col break at 1", sheet.isColumnBroken((short)1));
+ assertTrue("no col break at 10", sheet.isColumnBroken((short)10));
+ assertTrue("no col break at 15", sheet.isColumnBroken((short)15));
+ assertEquals("4 col break available", 4, sheet.getNumColumnBreaks());
+
+ boolean is10 = false;
+ boolean is0 = false;
+ boolean is1 = false;
+ boolean is15 = false;
+
+ Iterator iterator = sheet.getColumnBreaks();
+ while (iterator.hasNext()) {
+ PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
+ int main = (int)breakItem.main;
+ if (main != 0 && main != 1 && main != 10 && main != 15) fail("Invalid page break");
+ if (main == 0) is0 = true;
+ if (main == 1) is1 = true;
+ if (main == 10) is10= true;
+ if (main == 15) is15 = true;
+ }
+
+ assertTrue("one of the breaks didnt make it", is0 && is1 && is10 && is15);
+
+ sheet.removeColumnBreak((short)15);
+ assertFalse("column break should not be there", sheet.isColumnBroken((short)15));
+
+ sheet.removeColumnBreak((short)0);
+ assertFalse("column break should not be there", sheet.isColumnBroken((short)0));
+
+ sheet.removeColumnBreak((short)1);
+ assertFalse("column break should not be there", sheet.isColumnBroken((short)1));
+
+ sheet.removeColumnBreak((short)10);
+ assertFalse("column break should not be there", sheet.isColumnBroken((short)10));
+
+ assertEquals("no more breaks", 0, sheet.getNumColumnBreaks());
+ }
+
+
+}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java
index 6ccf29ec27..a82e0d5f37 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestCloneSheet.java
@@ -56,8 +56,6 @@
package org.apache.poi.hssf.usermodel;
import junit.framework.TestCase;
-import org.apache.poi.hssf.usermodel.HSSFSheet;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.Region;
/**
@@ -82,4 +80,23 @@ public class TestCloneSheet extends TestCase {
catch(Exception e){e.printStackTrace();fail(e.getMessage());}
}
+ /**
+ * Ensures that pagebreak cloning works properly
+ *
+ */
+ public void testPageBreakClones() {
+ HSSFWorkbook b = new HSSFWorkbook();
+ HSSFSheet s = b.createSheet("Test");
+ s.setRowBreak(3);
+ s.setColumnBreak((short)6);
+
+ HSSFSheet clone = b.cloneSheet(0);
+ assertTrue("Row 3 not broken", clone.isRowBroken(3));
+ assertTrue("Column 6 not broken", clone.isColumnBroken((short)6));
+
+ s.removeRowBreak(3);
+
+ assertTrue("Row 3 still should be broken", clone.isRowBroken(3));
+ }
+
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPalette.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPalette.java
index 788eeef0f3..61a3f1d861 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPalette.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPalette.java
@@ -178,7 +178,21 @@ public class TestHSSFPalette extends TestCase
c.compare(expectedColor, paletteColor);
}
}
-
+
+ public void testAddColor() throws Exception
+ {
+ try
+ {
+ HSSFColor hssfColor = hssfPalette.addColor((byte)10,(byte)10,(byte)10);
+ fail();
+ }
+ catch ( RuntimeException e )
+ {
+ // Failing because by default there are no colours left in the palette.
+ }
+ }
+
+
private static interface ColorComparator
{
void compare(HSSFColor expected, HSSFColor palette);
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
index 3779f3a781..647ed8f4af 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
@@ -382,6 +382,54 @@ public class TestHSSFSheet
assertEquals(sheet.isDisplayFormulas(), true);
}
+ /**
+ * Make sure the excel file loads work
+ *
+ */
+ public void testPageBreakFiles() throws Exception{
+ FileInputStream fis = null;
+ HSSFWorkbook wb = null;
+
+ String filename = System.getProperty("HSSF.testdata.path");
+
+ filename = filename + "/SimpleWithPageBreaks.xls";
+ fis = new FileInputStream(filename);
+ wb = new HSSFWorkbook(fis);
+ fis.close();
+
+ HSSFSheet sheet = wb.getSheetAt(0);
+ assertNotNull(sheet);
+
+ assertEquals("1 row page break", 1, sheet.getRowBreaks().length);
+ assertEquals("1 column page break", 1, sheet.getColumnBreaks().length);
+
+ assertTrue("No row page break", sheet.isRowBroken(22));
+ assertTrue("No column page break", sheet.isColumnBroken((short)4));
+
+ sheet.setRowBreak(10);
+ sheet.setColumnBreak((short)13);
+
+ assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
+ assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
+
+ File tempFile = File.createTempFile("display", "testPagebreaks.xls");
+ FileOutputStream stream = new FileOutputStream(tempFile);
+ wb.write(stream);
+ stream.close();
+
+ wb = new HSSFWorkbook(new FileInputStream(tempFile));
+ sheet = wb.getSheetAt(0);
+
+ assertTrue("No row page break", sheet.isRowBroken(22));
+ assertTrue("No column page break", sheet.isColumnBroken((short)4));
+
+
+ assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
+ assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
+
+
+ }
+
public static void main(java.lang.String[] args) {
junit.textui.TestRunner.run(TestHSSFSheet.class);
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
index e91003e4da..e65bac11e9 100755
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
@@ -188,5 +188,22 @@ public class TestSheetShiftRows extends TestCase {
s.createRow(3).createCell((short)0).setCellValue("TEST2");
s.shiftRows(0,4,1);
}
+
+ /**
+ * When shifting rows, the page breaks should go with it
+ *
+ */
+ public void testShiftRowBreaks()
+ {
+ HSSFWorkbook b = new HSSFWorkbook();
+ HSSFSheet s = b.createSheet();
+ HSSFRow row = s.createRow(4);
+ row.createCell((short)0).setCellValue("test");
+ s.setRowBreak(4);
+
+ s.shiftRows(4, 4, 2);
+ assertTrue("Row number 6 should have a pagebreak", s.isRowBroken(6));
+ }
+
}