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
This commit is contained in:
Glen Stampoultzis 2004-04-09 11:45:38 +00:00
parent 89101ac1da
commit d335e740b6
123 changed files with 18708 additions and 112 deletions

View File

@ -135,10 +135,27 @@ Main targets:
</echo> </echo>
</target> </target>
<target name="with.clover" if="clover.present">
<taskdef resource="clovertasks"/>
<clover-setup initString="mycoverage.db"/>
</target>
<target name="clover.html" depends="with.clover" if="clover.present">
<echo>Generating clover report</echo>
<clover-report>
<current outfile="build/tmp/site/build/site/clover_html">
<format type="html"/>
</current>
</clover-report>
</target>
<target name="init" depends="check-jars,fetch-jars"> <target name="init" depends="check-jars,fetch-jars">
<tstamp/> <tstamp/>
<available resource="clovertasks" property="clover.present"/>
<antcall target="with.clover"/>
<mkdir dir="build"/> <mkdir dir="build"/>
<mkdir dir="${main.output.dir}"/> <mkdir dir="${main.output.dir}"/>
<mkdir dir="${contrib.output.dir}"/> <mkdir dir="${contrib.output.dir}"/>
@ -253,7 +270,7 @@ Main targets:
<pathelement location="${junit.jar1.dir}"/> <pathelement location="${junit.jar1.dir}"/>
</path> </path>
<target name="test-main" depends="compile-main,-test-main-check" unless="main.test.notRequired"> <target name="test-main" depends="compile-main,-test-main-check" unless="main.test.notRequired">
<junit printsummary="yes" showoutput="true" filtertrace="no" fork="no" <junit printsummary="no" showoutput="true" fork="no"
haltonfailure="${halt.on.test.failure}" failureproperty="main.test.failed"> haltonfailure="${halt.on.test.failure}" failureproperty="main.test.failed">
<classpath refid="test.classpath"/> <classpath refid="test.classpath"/>
<sysproperty key="HSSF.testdata.path" file="${main.src.test}/org/apache/poi/hssf/data"/> <sysproperty key="HSSF.testdata.path" file="${main.src.test}/org/apache/poi/hssf/data"/>
@ -312,7 +329,7 @@ Main targets:
</target> </target>
<target name="test-scratchpad" depends="compile-scratchpad,-test-scratchpad-check" unless="scratchpad.test.notRequired"> <target name="test-scratchpad" depends="compile-scratchpad,-test-scratchpad-check" unless="scratchpad.test.notRequired">
<junit printsummary="yes" fork="no" haltonfailure="${halt.on.test.failure}" failureproperty="scratchpad.test.failed"> <junit printsummary="no" fork="no" haltonfailure="${halt.on.test.failure}" failureproperty="scratchpad.test.failed">
<classpath> <classpath>
<path refid="scratchpad.classpath"/> <path refid="scratchpad.classpath"/>
<pathelement location="${main.output.dir}"/> <pathelement location="${main.output.dir}"/>
@ -521,6 +538,7 @@ FORREST_HOME environment variable!</echo>
<group title="Utils" packages="org.apache.poi.util*"/> <group title="Utils" packages="org.apache.poi.util*"/>
</javadoc> </javadoc>
<antcall target="clover.html"/>
</target> </target>
@ -560,6 +578,9 @@ FORREST_HOME environment variable!</echo>
<group title="Record Generator" packages="org.apache.poi.record*"/> <group title="Record Generator" packages="org.apache.poi.record*"/>
<group title="Utils" packages="org.apache.poi.util*"/> <group title="Utils" packages="org.apache.poi.util*"/>
</javadoc> </javadoc>
<antcall target="clover.html"/>
</target> </target>

View File

@ -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();
}
}
}

View File

@ -40,6 +40,9 @@
<li><link href="#Splits">Create split and freeze panes.</link></li> <li><link href="#Splits">Create split and freeze panes.</link></li>
<li><link href="#Repeating">Repeating rows and columns.</link></li> <li><link href="#Repeating">Repeating rows and columns.</link></li>
<li><link href="#HeaderFooter">Headers and Footers.</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> </ul>
</section> </section>
<section><title>Features</title> <section><title>Features</title>
@ -692,6 +695,212 @@
fileOut.close(); fileOut.close();
</source> </source>
</section> </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>
</section> </section>
</body> </body>

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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";
}
}
}

View File

@ -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();
}
}

View File

@ -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);
// }
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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++;
}
}

View File

@ -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()] );
}
}

View File

@ -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 );
}
}

View File

@ -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() ) );
}
} );
}
}

View File

@ -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();
}
}

View File

@ -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 );
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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 +
"}";
}
}
}

View File

@ -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 );
}

View File

@ -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);
}

View File

@ -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 );
}
}

View File

@ -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) + ")";
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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() );
}
}

View File

@ -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() );
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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() ) );
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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() );
}
}

View File

@ -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 );
}
}

View File

@ -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>

View File

@ -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) private static void generateRecords(String defintionsDir, String recordStyleDir, String destSrcPathDir, String testSrcPathDir)
throws Exception { throws Exception {
File definitionsFile = new File(defintionsDir); File definitionsFile = new File(defintionsDir);

View File

@ -2,7 +2,7 @@
* ==================================================================== * ====================================================================
* The Apache Software License, Version 1.1 * 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. * reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -107,7 +107,7 @@ public class BiffViewer {
new POIFSFileSystem(new FileInputStream(filename)); new POIFSFileSystem(new FileInputStream(filename));
InputStream stream = InputStream stream =
fs.createDocumentInputStream("Workbook"); fs.createDocumentInputStream("Workbook");
Record[] records = createRecords(stream, dump); createRecords(stream, dump);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -128,17 +128,20 @@ public class BiffViewer {
public static Record[] createRecords(InputStream in, boolean dump) public static Record[] createRecords(InputStream in, boolean dump)
throws RecordFormatException { throws RecordFormatException {
ArrayList records = new ArrayList(); ArrayList records = new ArrayList();
Record last_record = null; //Record last_record = null;
int loc = 0; int loc = 0;
RecordDetails activeRecord = null;
try { try {
// long offset = 0; // long offset = 0;
short rectype = 0; short rectype = 0;
do { do {
rectype = LittleEndian.readShort(in); rectype = LittleEndian.readShort(in);
System.out.println("============================================"); int startloc = loc;
System.out.println("Offset 0x" + Integer.toHexString(loc) + " (" + loc + ")"); // System.out.println("============================================");
// System.out.println("Offset 0x" + Integer.toHexString(loc) + " (" + loc + ")");
loc += 2; loc += 2;
if (rectype != 0) { if (rectype != 0) {
short recsize = LittleEndian.readShort(in); short recsize = LittleEndian.readShort(in);
@ -147,36 +150,27 @@ public class BiffViewer {
byte[] data = new byte[(int) recsize]; byte[] data = new byte[(int) recsize];
in.read(data); in.read(data);
if ((rectype == WSBoolRecord.sid) && (recsize == 0)) {
System.out.println(loc);
}
loc += recsize; loc += recsize;
// offset += 4 + recsize; Record record = createRecord(rectype, recsize, data );
if (dump) { if (record.getSid() != ContinueRecord.sid)
dump(rectype, recsize, data); {
}
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);
}
if (record != null) {
if (rectype == ContinueRecord.sid) {
dumpContinueRecord(last_record, dump, data);
} else {
last_record = record;
records.add(record); records.add(record);
if (activeRecord != null)
activeRecord.dump();
activeRecord = new RecordDetails(rectype, recsize, startloc, data, record);
} }
else
{
activeRecord.getRecord().processContinueRecord(data);
}
if (dump) {
dumpRaw(rectype, recsize, data);
} }
} }
} while (rectype != 0); } while (rectype != 0);
activeRecord.dump();
} catch (IOException e) { } catch (IOException e) {
throw new RecordFormatException("Error reading bytes"); throw new RecordFormatException("Error reading bytes");
} }
@ -186,15 +180,14 @@ public class BiffViewer {
return retval; 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 { private static void dumpContinueRecord(Record last_record, boolean dump, byte[] data) throws IOException {
if (last_record == null) { if (last_record == null) {
throw new RecordFormatException( 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 { private static void dumpUnknownRecord(byte[] data) throws IOException {
// record hex dump it! // record hex dump it!
System.out.println( 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 // System.out
// .println("fixing to recordize the following"); // .println("fixing to recordize the following");
System.out.println("============================================");
System.out.print( "rectype = 0x" System.out.print( "rectype = 0x"
+ Integer.toHexString( rectype ) ); + Integer.toHexString( rectype ) );
System.out.println( ", recsize = 0x" System.out.println( ", recsize = 0x"
@ -275,13 +263,9 @@ public class BiffViewer {
* Essentially a duplicate of RecordFactory. Kept seperate as not to screw * Essentially a duplicate of RecordFactory. Kept seperate as not to screw
* up non-debug operations. * 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 ) byte[] data )
{ {
Record retval = null; Record retval = null;
@ -429,6 +413,15 @@ public class BiffViewer {
case GridsetRecord.sid: case GridsetRecord.sid:
retval = new GridsetRecord( rectype, size, data ); retval = new GridsetRecord( rectype, size, data );
break; 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: case GutsRecord.sid:
retval = new GutsRecord( rectype, size, data ); retval = new GutsRecord( rectype, size, data );
break; break;
@ -633,17 +626,22 @@ public class BiffViewer {
case SharedFormulaRecord.sid: case SharedFormulaRecord.sid:
retval = new SharedFormulaRecord( rectype, size, data); retval = new SharedFormulaRecord( rectype, size, data);
break; 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: default:
retval = new UnknownRecord( rectype, size, data ); retval = new UnknownRecord( rectype, size, data );
} }
if ( realretval == null ) return retval;
{
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;
} }
@ -674,6 +672,7 @@ public class BiffViewer {
public static void main(String[] args) { public static void main(String[] args) {
try { try {
System.setProperty("poi.deserialize.escher", "true");
BiffViewer viewer = new BiffViewer(args); BiffViewer viewer = new BiffViewer(args);
if ((args.length > 1) && args[1].equals("on")) { if ((args.length > 1) && args[1].equals("on")) {
@ -696,4 +695,50 @@ public class BiffViewer {
e.printStackTrace(); 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);
}
}
} }

View File

@ -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
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -122,6 +122,8 @@ public class Sheet implements Model
private Iterator rowRecIterator = null; private Iterator rowRecIterator = null;
protected int eofLoc = 0; protected int eofLoc = 0;
protected ProtectRecord protect = null; 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_LOWER_RIGHT = (byte)0;
public static final byte PANE_UPPER_RIGHT = (byte)1; 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) public static Sheet createSheet(List recs, int sheetnum, int offset)
{ {
log.logFormatted(log.DEBUG, log.logFormatted(POILogger.DEBUG,
"Sheet createSheet (existing file) with %", "Sheet createSheet (existing file) with %",
new Integer(recs.size())); new Integer(recs.size()));
Sheet retval = new Sheet(); Sheet retval = new Sheet();
@ -170,18 +172,18 @@ public class Sheet implements Model
if (rec.getSid() == LabelRecord.sid) if (rec.getSid() == LabelRecord.sid)
{ {
log.log(log.DEBUG, "Hit label record."); log.log(POILogger.DEBUG, "Hit label record.");
retval.containsLabels = true; retval.containsLabels = true;
} }
else if (rec.getSid() == BOFRecord.sid) else if (rec.getSid() == BOFRecord.sid)
{ {
bofEofNestingLevel++; 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) else if (rec.getSid() == EOFRecord.sid)
{ {
--bofEofNestingLevel; --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) { if (bofEofNestingLevel == 0) {
records.add(rec); records.add(rec);
retval.eofLoc = k; retval.eofLoc = k;
@ -296,6 +298,14 @@ public class Sheet implements Model
{ {
rec = null; 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) if (rec != null)
@ -312,7 +322,7 @@ public class Sheet implements Model
{ {
retval.cells = new ValueRecordsAggregate(); retval.cells = new ValueRecordsAggregate();
} }
log.log(log.DEBUG, "sheet createSheet (existing file) exited"); log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
return retval; return retval;
} }
@ -371,7 +381,7 @@ public class Sheet implements Model
public static Sheet createSheet(List records, int sheetnum) public static Sheet createSheet(List records, int sheetnum)
{ {
log.log(log.DEBUG, log.log(POILogger.DEBUG,
"Sheet createSheet (exisiting file) assumed offset 0"); "Sheet createSheet (exisiting file) assumed offset 0");
return createSheet(records, sheetnum, 0); return createSheet(records, sheetnum, 0);
} }
@ -386,7 +396,7 @@ public class Sheet implements Model
public static Sheet createSheet() 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(); Sheet retval = new Sheet();
ArrayList records = new ArrayList(30); ArrayList records = new ArrayList(30);
@ -409,6 +419,10 @@ public class Sheet implements Model
(DefaultRowHeightRecord) retval.createDefaultRowHeight(); (DefaultRowHeightRecord) retval.createDefaultRowHeight();
records.add( retval.defaultrowheight ); records.add( retval.defaultrowheight );
records.add( retval.createWSBool() ); 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(); retval.header = (HeaderRecord) retval.createHeader();
records.add( retval.header ); records.add( retval.header );
retval.footer = (FooterRecord) retval.createFooter(); retval.footer = (FooterRecord) retval.createFooter();
@ -421,7 +435,7 @@ public class Sheet implements Model
(DefaultColWidthRecord) retval.createDefaultColWidth(); (DefaultColWidthRecord) retval.createDefaultColWidth();
records.add( retval.defaultcolwidth); records.add( retval.defaultcolwidth);
retval.dims = ( DimensionsRecord ) retval.createDimensions(); retval.dims = ( DimensionsRecord ) retval.createDimensions();
retval.dimsloc = 19; retval.dimsloc = records.size()-1;
records.add(retval.dims); records.add(retval.dims);
records.add(retval.windowTwo = retval.createWindowTwo()); records.add(retval.windowTwo = retval.createWindowTwo());
retval.setLoc(records.size() - 1); retval.setLoc(records.size() - 1);
@ -432,7 +446,7 @@ public class Sheet implements Model
records.add(retval.protect); records.add(retval.protect);
records.add(retval.createEOF()); records.add(retval.createEOF());
retval.records = records; retval.records = records;
log.log(log.DEBUG, "Sheet createsheet from scratch exit"); log.log(POILogger.DEBUG, "Sheet createsheet from scratch exit");
return retval; return retval;
} }
@ -576,7 +590,7 @@ public class Sheet implements Model
public void convertLabelRecords(Workbook wb) public void convertLabelRecords(Workbook wb)
{ {
log.log(log.DEBUG, "convertLabelRecords called"); log.log(POILogger.DEBUG, "convertLabelRecords called");
if (containsLabels) if (containsLabels)
{ {
for (int k = 0; k < records.size(); k++) 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(); checkCells();
checkRows(); checkRows();
log.log(log.DEBUG, "Sheet.getNumRecords"); log.log(POILogger.DEBUG, "Sheet.getNumRecords");
log.logFormatted(log.DEBUG, "returning % + % + % - 2 = %", new int[] log.logFormatted(POILogger.DEBUG, "returning % + % + % - 2 = %", new int[]
{ {
records.size(), cells.getPhysicalNumberOfCells(), records.size(), cells.getPhysicalNumberOfCells(),
rows.getPhysicalNumberOfRows(), rows.getPhysicalNumberOfRows(),
@ -638,8 +652,8 @@ public class Sheet implements Model
public void setDimensions(int firstrow, short firstcol, int lastrow, public void setDimensions(int firstrow, short firstcol, int lastrow,
short lastcol) short lastcol)
{ {
log.log(log.DEBUG, "Sheet.setDimensions"); log.log(POILogger.DEBUG, "Sheet.setDimensions");
log.log(log.DEBUG, log.log(POILogger.DEBUG,
(new StringBuffer("firstrow")).append(firstrow) (new StringBuffer("firstrow")).append(firstrow)
.append("firstcol").append(firstcol).append("lastrow") .append("firstcol").append(firstcol).append("lastrow")
.append(lastrow).append("lastcol").append(lastcol) .append(lastrow).append("lastcol").append(lastcol)
@ -2560,4 +2574,192 @@ public class Sheet implements Model
{ {
return protect; 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();
}
} }

View File

@ -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;
}
}

View File

@ -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() );
}
}

View File

@ -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;
}
}

View File

@ -60,6 +60,7 @@ import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.hssf.util.SheetReferences; import org.apache.poi.hssf.util.SheetReferences;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
import org.apache.poi.ddf.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
@ -133,6 +134,7 @@ public class Workbook implements Model {
protected int numfonts = 0; // hold the number of font records protected int numfonts = 0; // hold the number of font records
private short maxformatid = -1; // holds the max format id private short maxformatid = -1; // holds the max format id
private boolean uses1904datewindowing = false; // whether 1904 date windowing is being used private boolean uses1904datewindowing = false; // whether 1904 date windowing is being used
private DrawingManager drawingManager;
private static POILogger log = POILogFactory.getLogger(Workbook.class); private static POILogger log = POILogFactory.getLogger(Workbook.class);
@ -2090,6 +2092,56 @@ public class Workbook implements Model {
return palette; 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;
}
} }

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -112,7 +112,10 @@ public class RecordFactory
FormulaRecord.class, BoolErrRecord.class, ExternSheetRecord.class, FormulaRecord.class, BoolErrRecord.class, ExternSheetRecord.class,
NameRecord.class, LeftMarginRecord.class, RightMarginRecord.class, NameRecord.class, LeftMarginRecord.class, RightMarginRecord.class,
TopMarginRecord.class, BottomMarginRecord.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 { } else {
records = new Class[] records = new Class[]
@ -143,7 +146,10 @@ public class RecordFactory
BoolErrRecord.class, ExternSheetRecord.class, NameRecord.class, BoolErrRecord.class, ExternSheetRecord.class, NameRecord.class,
LeftMarginRecord.class, RightMarginRecord.class, LeftMarginRecord.class, RightMarginRecord.class,
TopMarginRecord.class, BottomMarginRecord.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
}; };
} }

View File

@ -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;
}
}

View File

@ -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]);
}
}

View File

@ -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());
}
}

View File

@ -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]);
}
}

View File

@ -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 ) );
}
}

View File

@ -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]);
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -95,6 +95,13 @@ public class UnknownRecord
this.thedata = data; 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 * spit the record out AS IS. no interperatation or identification
*/ */

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -386,4 +386,12 @@ public class HSSFFont
{ {
return font.getUnderline(); return font.getUnderline();
} }
public String toString()
{
return "org.apache.poi.hssf.usermodel.HSSFFont{" +
font +
"}";
}
} }

View File

@ -111,6 +111,59 @@ public class HSSFPalette
return null; 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 * Sets the color at the given offset
* *

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}

Some files were not shown because too many files have changed in this diff Show More