Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-658312 via svnmerge from

https://svn.apache.org/repos/asf/poi/trunk

........
  r657702 | josh | 2008-05-19 02:06:54 +0100 (Mon, 19 May 2008) | 1 line
  
  Bug 44306 - fixed reading/writing of AttrPtg and rendering of CHOOSE formulas
........
  r657875 | yegor | 2008-05-19 18:29:55 +0100 (Mon, 19 May 2008) | 1 line
  
  updated release date and started a new section for 3.1-final
........
  r658033 | josh | 2008-05-20 00:07:27 +0100 (Tue, 20 May 2008) | 1 line
  
  Minor patch to improve FormulaParser error messages like those from bug 45041
........
  r658285 | nick | 2008-05-20 16:42:16 +0100 (Tue, 20 May 2008) | 1 line
  
  Fix bug 44898 - Correctly handle short last blocks in POIFS
........
  r658287 | nick | 2008-05-20 16:46:54 +0100 (Tue, 20 May 2008) | 1 line
  
  Fix #44824 - Avoid an infinite loop when reading some HWPF pictures
........
  r658302 | nick | 2008-05-20 17:01:53 +0100 (Tue, 20 May 2008) | 1 line
  
  Patch from bug #44937 from Squeeself- Partial support for extracting Escher images from HWPF files
........
  r658308 | nick | 2008-05-20 17:30:19 +0100 (Tue, 20 May 2008) | 1 line
  
  Support for specifying a policy to HSSF on missing / blank cells when fetching
........


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@658333 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-05-20 16:49:41 +00:00
parent 937d2700ed
commit 81550a63a9
25 changed files with 915 additions and 114 deletions

View File

@ -43,7 +43,14 @@
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action> <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release> </release>
<release version="3.1-beta2" date="2008-05-??"> <release version="3.1-final" date="2008-06-??">
<action dev="POI-DEVELOPERS" type="add">Support for specifying a policy to HSSF on missing / blank cells when fetching</action>
<action dev="POI-DEVELOPERS" type="add">44937 - Partial support for extracting Escher images from HWPF files</action>
<action dev="POI-DEVELOPERS" type="fix">44824 - Avoid an infinite loop when reading some HWPF pictures</action>
<action dev="POI-DEVELOPERS" type="fix">44898 - Correctly handle short last blocks in POIFS</action>
</release>
<release version="3.1-beta2" date="2008-05-26">
<action dev="POI-DEVELOPERS" type="fix">44306 - fixed reading/writing of AttrPtg(type=choose) and method toFormulaString() for CHOOSE formulas</action>
<action dev="POI-DEVELOPERS" type="fix">24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists</action> <action dev="POI-DEVELOPERS" type="fix">24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists</action>
<action dev="POI-DEVELOPERS" type="fix">40414 - fixed selected/active sheet after removing sheet from workbook</action> <action dev="POI-DEVELOPERS" type="fix">40414 - fixed selected/active sheet after removing sheet from workbook</action>
<action dev="POI-DEVELOPERS" type="fix">44523 - fixed workbook sheet selection and focus</action> <action dev="POI-DEVELOPERS" type="fix">44523 - fixed workbook sheet selection and focus</action>

View File

@ -40,7 +40,14 @@
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action> <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release> </release>
<release version="3.1-beta2" date="2008-05-??"> <release version="3.1-final" date="2008-06-??">
<action dev="POI-DEVELOPERS" type="add">Support for specifying a policy to HSSF on missing / blank cells when fetching</action>
<action dev="POI-DEVELOPERS" type="add">44937 - Partial support for extracting Escher images from HWPF files</action>
<action dev="POI-DEVELOPERS" type="fix">44824 - Avoid an infinite loop when reading some HWPF pictures</action>
<action dev="POI-DEVELOPERS" type="fix">44898 - Correctly handle short last blocks in POIFS</action>
</release>
<release version="3.1-beta2" date="2008-05-26">
<action dev="POI-DEVELOPERS" type="fix">44306 - fixed reading/writing of AttrPtg(type=choose) and method toFormulaString() for CHOOSE formulas</action>
<action dev="POI-DEVELOPERS" type="fix">24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists</action> <action dev="POI-DEVELOPERS" type="fix">24207 - added HSSFName.isDeleted() to check if the name points to cell that no longer exists</action>
<action dev="POI-DEVELOPERS" type="fix">40414 - fixed selected/active sheet after removing sheet from workbook</action> <action dev="POI-DEVELOPERS" type="fix">40414 - fixed selected/active sheet after removing sheet from workbook</action>
<action dev="POI-DEVELOPERS" type="fix">44523 - fixed workbook sheet selection and focus</action> <action dev="POI-DEVELOPERS" type="fix">44523 - fixed workbook sheet selection and focus</action>

View File

@ -65,20 +65,27 @@ public class EscherClientAnchorRecord
int size = 0; int size = 0;
// Always find 4 two byte entries. Sometimes find 9 // Always find 4 two byte entries. Sometimes find 9
field_1_flag = LittleEndian.getShort( data, pos + size ); size += 2; if (bytesRemaining == 4) // Word format only 4 bytes
field_2_col1 = LittleEndian.getShort( data, pos + size ); size += 2; {
field_3_dx1 = LittleEndian.getShort( data, pos + size ); size += 2; // Not sure exactly what the format is quite yet, likely a reference to a PLC
field_4_row1 = LittleEndian.getShort( data, pos + size ); size += 2; }
if(bytesRemaining >= 18) { else
field_5_dy1 = LittleEndian.getShort( data, pos + size ); size += 2; {
field_6_col2 = LittleEndian.getShort( data, pos + size ); size += 2; field_1_flag = LittleEndian.getShort( data, pos + size ); size += 2;
field_7_dx2 = LittleEndian.getShort( data, pos + size ); size += 2; field_2_col1 = LittleEndian.getShort( data, pos + size ); size += 2;
field_8_row2 = LittleEndian.getShort( data, pos + size ); size += 2; field_3_dx1 = LittleEndian.getShort( data, pos + size ); size += 2;
field_9_dy2 = LittleEndian.getShort( data, pos + size ); size += 2; field_4_row1 = LittleEndian.getShort( data, pos + size ); size += 2;
shortRecord = false; if(bytesRemaining >= 18) {
} else { field_5_dy1 = LittleEndian.getShort( data, pos + size ); size += 2;
shortRecord = true; 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;
shortRecord = false;
} else {
shortRecord = true;
}
}
bytesRemaining -= size; bytesRemaining -= size;
remainingData = new byte[bytesRemaining]; remainingData = new byte[bytesRemaining];
System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining ); System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining );

View File

@ -134,7 +134,10 @@ public final class FormulaParser {
/** Report What Was Expected */ /** Report What Was Expected */
private RuntimeException expected(String s) { private RuntimeException expected(String s) {
return new FormulaParseException(s + " Expected"); String msg = "Parse error near char " + (pointer-1) + "'" + look + "'"
+ " in specified formula '" + formulaString + "'. Expected "
+ s;
return new FormulaParseException(msg);
} }
@ -1000,7 +1003,7 @@ end;
if (ptg instanceof AttrPtg) { if (ptg instanceof AttrPtg) {
AttrPtg attrPtg = ((AttrPtg) ptg); AttrPtg attrPtg = ((AttrPtg) ptg);
if (attrPtg.isOptimizedIf()) { if (attrPtg.isOptimizedIf() || attrPtg.isOptimizedChoose() || attrPtg.isGoto()) {
continue; continue;
} }
if (attrPtg.isSpace()) { if (attrPtg.isSpace()) {
@ -1014,6 +1017,9 @@ end;
// similar to tAttrSpace - RPN is violated // similar to tAttrSpace - RPN is violated
continue; continue;
} }
if (!attrPtg.isSum()) {
throw new RuntimeException("Unexpected tAttr: " + attrPtg.toString());
}
} }
final OperationPtg o = (OperationPtg) ptg; final OperationPtg o = (OperationPtg) ptg;

View File

@ -39,6 +39,11 @@ public final class AttrPtg extends OperationPtg {
private byte field_1_options; private byte field_1_options;
private short field_2_data; private short field_2_data;
/** only used for tAttrChoose: table of offsets to starts of args */
private final int[] _jumpTable;
/** only used for tAttrChoose: offset to the tFuncVar for CHOOSE() */
private final int _chooseFuncOffset;
// flags 'volatile' and 'space', can be combined. // flags 'volatile' and 'space', can be combined.
// OOO spec says other combinations are theoretically possible but not likely to occur. // OOO spec says other combinations are theoretically possible but not likely to occur.
private static final BitField semiVolatile = BitFieldFactory.getInstance(0x01); private static final BitField semiVolatile = BitFieldFactory.getInstance(0x01);
@ -63,7 +68,7 @@ public final class AttrPtg extends OperationPtg {
/** 03H = Carriage returns before opening parenthesis (only allowed before tParen token) */ /** 03H = Carriage returns before opening parenthesis (only allowed before tParen token) */
public static final int CR_BEFORE_OPEN_PAREN = 0x03; public static final int CR_BEFORE_OPEN_PAREN = 0x03;
/** 04H = Spaces before closing parenthesis (only allowed before tParen, tFunc, and tFuncVar tokens) */ /** 04H = Spaces before closing parenthesis (only allowed before tParen, tFunc, and tFuncVar tokens) */
public static final int SPACE_BEFORE_CLOSE_PAERN = 0x04; public static final int SPACE_BEFORE_CLOSE_PAREN = 0x04;
/** 05H = Carriage returns before closing parenthesis (only allowed before tParen, tFunc, and tFuncVar tokens) */ /** 05H = Carriage returns before closing parenthesis (only allowed before tParen, tFunc, and tFuncVar tokens) */
public static final int CR_BEFORE_CLOSE_PAREN = 0x05; public static final int CR_BEFORE_CLOSE_PAREN = 0x05;
/** 06H = Spaces following the equality sign (only in macro sheets) */ /** 06H = Spaces following the equality sign (only in macro sheets) */
@ -71,16 +76,33 @@ public final class AttrPtg extends OperationPtg {
} }
public AttrPtg() { public AttrPtg() {
_jumpTable = null;
_chooseFuncOffset = -1;
} }
public AttrPtg(RecordInputStream in) public AttrPtg(RecordInputStream in)
{ {
field_1_options = in.readByte(); field_1_options = in.readByte();
field_2_data = in.readShort(); field_2_data = in.readShort();
if (isOptimizedChoose()) {
int nCases = field_2_data;
int[] jumpTable = new int[nCases];
for (int i = 0; i < jumpTable.length; i++) {
jumpTable[i] = in.readUShort();
}
_jumpTable = jumpTable;
_chooseFuncOffset = in.readUShort();
} else {
_jumpTable = null;
_chooseFuncOffset = -1;
}
} }
private AttrPtg(int options, int data) { private AttrPtg(int options, int data, int[] jt, int chooseFuncOffset) {
field_1_options = (byte) options; field_1_options = (byte) options;
field_2_data = (short) data; field_2_data = (short) data;
_jumpTable = jt;
_chooseFuncOffset = chooseFuncOffset;
} }
/** /**
@ -89,7 +111,7 @@ public final class AttrPtg extends OperationPtg {
*/ */
public static AttrPtg createSpace(int type, int count) { public static AttrPtg createSpace(int type, int count) {
int data = type & 0x00FF | (count << 8) & 0x00FFFF; int data = type & 0x00FF | (count << 8) & 0x00FFFF;
return new AttrPtg(space.set(0), data); return new AttrPtg(space.set(0), data, null, -1);
} }
public void setOptions(byte options) public void setOptions(byte options)
@ -136,14 +158,14 @@ public final class AttrPtg extends OperationPtg {
field_1_options=optiIf.setByteBoolean(field_1_options,bif); field_1_options=optiIf.setByteBoolean(field_1_options,bif);
} }
/** /**
* Flags this ptg as a goto/jump * Flags this ptg as a goto/jump
* @param isGoto * @param isGoto
*/ */
public void setGoto(boolean isGoto) { public void setGoto(boolean isGoto) {
field_1_options=optGoto.setByteBoolean(field_1_options, isGoto); field_1_options=optGoto.setByteBoolean(field_1_options, isGoto);
} }
// lets hope no one uses this anymore // lets hope no one uses this anymore
public boolean isBaxcel() public boolean isBaxcel()
{ {
@ -181,7 +203,7 @@ public final class AttrPtg extends OperationPtg {
if(isOptimizedIf()) { if(isOptimizedIf()) {
sb.append("if dist=").append(getData()); sb.append("if dist=").append(getData());
} else if(isOptimizedChoose()) { } else if(isOptimizedChoose()) {
sb.append("choose dist=").append(getData()); sb.append("choose nCases=").append(getData());
} else if(isGoto()) { } else if(isGoto()) {
sb.append("skip dist=").append(getData()); sb.append("skip dist=").append(getData());
} else if(isSum()) { } else if(isSum()) {
@ -195,13 +217,28 @@ public final class AttrPtg extends OperationPtg {
public void writeBytes(byte [] array, int offset) public void writeBytes(byte [] array, int offset)
{ {
array[offset]=sid; LittleEndian.putByte(array, offset+0, sid);
array[offset+1]=field_1_options; LittleEndian.putByte(array, offset+1, field_1_options);
LittleEndian.putShort(array,offset+2,field_2_data); LittleEndian.putShort(array,offset+2, field_2_data);
int[] jt = _jumpTable;
if (jt != null) {
int joff = offset+4;
LittleEndian.putUShort(array, joff, _chooseFuncOffset);
joff+=2;
for (int i = 0; i < jt.length; i++) {
LittleEndian.putUShort(array, joff, jt[i]);
joff+=2;
}
LittleEndian.putUShort(array, joff, _chooseFuncOffset);
}
} }
public int getSize() public int getSize()
{ {
if (_jumpTable != null) {
return SIZE + (_jumpTable.length + 1) * LittleEndian.SHORT_SIZE;
}
return SIZE; return SIZE;
} }
@ -255,12 +292,17 @@ public final class AttrPtg extends OperationPtg {
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;} public byte getDefaultOperandClass() {
return Ptg.CLASS_VALUE;
}
public Object clone() { public Object clone() {
AttrPtg ptg = new AttrPtg(); int[] jt;
ptg.field_1_options = field_1_options; if (_jumpTable == null) {
ptg.field_2_data = field_2_data; jt = null;
return ptg; } else {
jt = (int[]) _jumpTable.clone();
}
return new AttrPtg(field_1_options, field_2_data, jt, _chooseFuncOffset);
} }
} }

View File

@ -319,6 +319,36 @@ public final class HSSFRow implements Comparable, Row {
if(cellnum<0||cellnum>=cells.length) return null; if(cellnum<0||cellnum>=cells.length) return null;
return cells[cellnum]; return cells[cellnum];
} }
/**
* Get the hssfcell representing a given column (logical cell)
* 0-based. If you ask for a cell that is not defined, then
* your supplied policy says what to do
*
* @param cellnum 0 based column number
* @param policy Policy on blank / missing cells
* @return representing that column or null if undefined + policy allows.
*/
public HSSFCell getCell(int cellnum, MissingCellPolicy policy) {
HSSFCell cell = getCell(cellnum);
if(policy == RETURN_NULL_AND_BLANK) {
return cell;
}
if(policy == RETURN_BLANK_AS_NULL) {
if(cell == null) return cell;
if(cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
return null;
}
return cell;
}
if(policy == CREATE_NULL_AS_BLANK) {
if(cell == null) {
return createCell((short)cellnum, HSSFCell.CELL_TYPE_BLANK);
}
return cell;
}
throw new IllegalArgumentException("Illegal policy " + policy + " (" + policy.id + ")");
}
/** /**
* get the number of the first cell contained in this row. * get the number of the first cell contained in this row.
@ -485,6 +515,7 @@ public final class HSSFRow implements Comparable, Row {
return cellnum; return cellnum;
} }
/** /**
* @return cell iterator of the physically defined cells. * @return cell iterator of the physically defined cells.
* Note that the 4th element might well not be cell 4, as the iterator * Note that the 4th element might well not be cell 4, as the iterator
@ -503,6 +534,9 @@ public final class HSSFRow implements Comparable, Row {
return cellIterator(); return cellIterator();
} }
/**
* An iterator over the (physical) cells in the row.
*/
private class CellIterator implements Iterator private class CellIterator implements Iterator
{ {
int thisId=-1; int thisId=-1;

View File

@ -37,6 +37,7 @@ public class RawDataBlock
{ {
private byte[] _data; private byte[] _data;
private boolean _eof; private boolean _eof;
private boolean _hasData;
private static POILogger log = POILogFactory.getLogger(RawDataBlock.class); private static POILogger log = POILogFactory.getLogger(RawDataBlock.class);
/** /**
@ -66,6 +67,7 @@ public class RawDataBlock
throws IOException { throws IOException {
_data = new byte[ blockSize ]; _data = new byte[ blockSize ];
int count = IOUtils.readFully(stream, _data); int count = IOUtils.readFully(stream, _data);
_hasData = (count > 0);
if (count == -1) { if (count == -1) {
_eof = true; _eof = true;
@ -94,16 +96,21 @@ public class RawDataBlock
/** /**
* When we read the data, did we hit end of file? * When we read the data, did we hit end of file?
* *
* @return true if no data was read because we were at the end of * @return true if the EoF was hit during this block, or
* the file, else false * false if not. If you have a dodgy short last block, then
* * it's possible to both have data, and also hit EoF...
* @exception IOException
*/ */
public boolean eof() public boolean eof() {
throws IOException
{
return _eof; return _eof;
} }
/**
* Did we actually find any data to read? It's possible,
* in the event of a short last block, to both have hit
* the EoF, but also to have data
*/
public boolean hasData() {
return _hasData;
}
/* ********** START implementation of ListManagedBlock ********** */ /* ********** START implementation of ListManagedBlock ********** */
@ -117,7 +124,7 @@ public class RawDataBlock
public byte [] getData() public byte [] getData()
throws IOException throws IOException
{ {
if (eof()) if (! hasData())
{ {
throw new IOException("Cannot return empty data"); throw new IOException("Cannot return empty data");
} }

View File

@ -51,12 +51,16 @@ public class RawDataBlockList
while (true) while (true)
{ {
RawDataBlock block = new RawDataBlock(stream, bigBlockSize); RawDataBlock block = new RawDataBlock(stream, bigBlockSize);
// If there was data, add the block to the list
if(block.hasData()) {
blocks.add(block);
}
if (block.eof()) // If the stream is now at the End Of File, we're done
{ if (block.eof()) {
break; break;
} }
blocks.add(block);
} }
setBlocks(( RawDataBlock [] ) blocks.toArray(new RawDataBlock[ 0 ])); setBlocks(( RawDataBlock [] ) blocks.toArray(new RawDataBlock[ 0 ]));
} }

View File

@ -33,4 +33,22 @@ public interface Row {
HSSFCell getCell(int cellnum); HSSFCell getCell(int cellnum);
Iterator cellIterator(); Iterator cellIterator();
/**
* Used to specify the different possible policies
* if for the case of null and blank cells
*/
public static class MissingCellPolicy {
private static int NEXT_ID = 1;
public final int id;
private MissingCellPolicy() {
this.id = NEXT_ID++;
}
}
/** Missing cells are returned as null, Blank cells are returned as normal */
public static final MissingCellPolicy RETURN_NULL_AND_BLANK = new MissingCellPolicy();
/** Missing cells are returned as null, as are blank cells */
public static final MissingCellPolicy RETURN_BLANK_AS_NULL = new MissingCellPolicy();
/** A new, blank cell is created for missing cells. Blank cells are returned as normal */
public static final MissingCellPolicy CREATE_NULL_AS_BLANK = new MissingCellPolicy();
} }

View File

@ -88,9 +88,20 @@ public interface Row extends Iterable<Cell> {
* ask for a cell that is not defined....you get a null. * ask for a cell that is not defined....you get a null.
* *
* @param cellnum 0 based column number * @param cellnum 0 based column number
* @return HSSFCell representing that column or null if undefined. * @return Cell representing that column or null if undefined.
*/ */
Cell getCell(int cellnum); Cell getCell(int cellnum);
/**
* Get the hssfcell representing a given column (logical cell)
* 0-based. If you ask for a cell that is not defined, then
* your supplied policy says what to do
*
* @param cellnum 0 based column number
* @param policy Policy on blank / missing cells
* @return representing that column or null if undefined + policy allows.
*/
public Cell getCell(int cellnum, MissingCellPolicy policy);
/** /**
* get the number of the first cell contained in this row. * get the number of the first cell contained in this row.
@ -170,4 +181,22 @@ public interface Row extends Iterable<Cell> {
boolean equals(Object obj); boolean equals(Object obj);
/**
* Used to specify the different possible policies
* if for the case of null and blank cells
*/
public static class MissingCellPolicy {
private static int NEXT_ID = 1;
public final int id;
private MissingCellPolicy() {
this.id = NEXT_ID++;
}
}
/** Missing cells are returned as null, Blank cells are returned as normal */
public static final MissingCellPolicy RETURN_NULL_AND_BLANK = new MissingCellPolicy();
/** Missing cells are returned as null, as are blank cells */
public static final MissingCellPolicy RETURN_BLANK_AS_NULL = new MissingCellPolicy();
/** A new, blank cell is created for missing cells. Blank cells are returned as normal */
public static final MissingCellPolicy CREATE_NULL_AS_BLANK = new MissingCellPolicy();
} }

View File

@ -21,6 +21,7 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
@ -138,6 +139,27 @@ public class XSSFRow implements Row {
} }
return null; return null;
} }
public Cell getCell(int cellnum, MissingCellPolicy policy) {
Cell cell = getCell(cellnum);
if(policy == RETURN_NULL_AND_BLANK) {
return cell;
}
if(policy == RETURN_BLANK_AS_NULL) {
if(cell == null) return cell;
if(cell.getCellType() == HSSFCell.CELL_TYPE_BLANK) {
return null;
}
return cell;
}
if(policy == CREATE_NULL_AS_BLANK) {
if(cell == null) {
return createCell((short)cellnum, HSSFCell.CELL_TYPE_BLANK);
}
return cell;
}
throw new IllegalArgumentException("Illegal policy " + policy + " (" + policy.id + ")");
}
public short getFirstCellNum() { public short getFirstCellNum() {
for (Iterator<Cell> it = cellIterator() ; it.hasNext() ; ) { for (Iterator<Cell> it = cellIterator() ; it.hasNext() ; ) {

View File

@ -22,6 +22,7 @@ import java.util.Iterator;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.TestXSSFCell.DummySharedStringSource; import org.apache.poi.xssf.usermodel.TestXSSFCell.DummySharedStringSource;
/** /**
@ -166,6 +167,64 @@ public final class TestXSSFRow extends TestCase {
row.setZeroHeight(true); row.setZeroHeight(true);
assertTrue(row.getZeroHeight()); assertTrue(row.getZeroHeight());
} }
/**
* Tests for the missing/blank cell policy stuff
*/
public void testGetCellPolicy() throws Exception {
XSSFRow row = new XSSFRow(createParentObjects());
// 0 -> string
// 1 -> num
// 2 missing
// 3 missing
// 4 -> blank
// 5 -> num
row.createCell((short)0).setCellValue(new XSSFRichTextString("test"));
row.createCell((short)1).setCellValue(3.2);
row.createCell((short)4, Cell.CELL_TYPE_BLANK);
row.createCell((short)5).setCellValue(4);
// First up, no policy
assertEquals(Cell.CELL_TYPE_STRING, row.getCell(0).getCellType());
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(1).getCellType());
assertEquals(null, row.getCell(2));
assertEquals(null, row.getCell(3));
assertEquals(Cell.CELL_TYPE_BLANK, row.getCell(4).getCellType());
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(5).getCellType());
// RETURN_NULL_AND_BLANK - same as default
assertEquals(Cell.CELL_TYPE_STRING, row.getCell(0, Row.RETURN_NULL_AND_BLANK).getCellType());
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(1, Row.RETURN_NULL_AND_BLANK).getCellType());
assertEquals(null, row.getCell(2, Row.RETURN_NULL_AND_BLANK));
assertEquals(null, row.getCell(3, Row.RETURN_NULL_AND_BLANK));
assertEquals(Cell.CELL_TYPE_BLANK, row.getCell(4, Row.RETURN_NULL_AND_BLANK).getCellType());
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(5, Row.RETURN_NULL_AND_BLANK).getCellType());
// RETURN_BLANK_AS_NULL - nearly the same
assertEquals(Cell.CELL_TYPE_STRING, row.getCell(0, XSSFRow.RETURN_BLANK_AS_NULL).getCellType());
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(1, XSSFRow.RETURN_BLANK_AS_NULL).getCellType());
assertEquals(null, row.getCell(2, XSSFRow.RETURN_BLANK_AS_NULL));
assertEquals(null, row.getCell(3, XSSFRow.RETURN_BLANK_AS_NULL));
assertEquals(null, row.getCell(4, XSSFRow.RETURN_BLANK_AS_NULL));
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(5, XSSFRow.RETURN_BLANK_AS_NULL).getCellType());
// CREATE_NULL_AS_BLANK - creates as needed
assertEquals(Cell.CELL_TYPE_STRING, row.getCell(0, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(1, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(Cell.CELL_TYPE_BLANK, row.getCell(2, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(Cell.CELL_TYPE_BLANK, row.getCell(3, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(Cell.CELL_TYPE_BLANK, row.getCell(4, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(Cell.CELL_TYPE_NUMERIC, row.getCell(5, XSSFRow.CREATE_NULL_AS_BLANK).getCellType());
// Check created ones get the right column
assertEquals((short)0, row.getCell(0, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)1, row.getCell(1, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)2, row.getCell(2, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)3, row.getCell(3, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)4, row.getCell(4, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)5, row.getCell(5, XSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
}
/** /**
* Method that returns a row with some sample cells * Method that returns a row with some sample cells

View File

@ -53,10 +53,10 @@ public class HWPFDocument extends POIDocument
protected FileInformationBlock _fib; protected FileInformationBlock _fib;
/** main document stream buffer*/ /** main document stream buffer*/
private byte[] _mainStream; protected byte[] _mainStream;
/** table stream buffer*/ /** table stream buffer*/
private byte[] _tableStream; protected byte[] _tableStream;
/** data stream buffer*/ /** data stream buffer*/
protected byte[] _dataStream; protected byte[] _dataStream;
@ -93,6 +93,12 @@ public class HWPFDocument extends POIDocument
/** Holds pictures table */ /** Holds pictures table */
protected PicturesTable _pictures; protected PicturesTable _pictures;
/** Holds FSBA (shape) information */
protected FSPATable _fspa;
/** Escher Drawing Group information */
protected EscherRecordHolder _dgg;
protected HWPFDocument() protected HWPFDocument()
{ {
@ -204,9 +210,6 @@ public class HWPFDocument extends POIDocument
{ {
_dataStream = new byte[0]; _dataStream = new byte[0];
} }
// read in the pictures stream
_pictures = new PicturesTable(this, _dataStream);
// get the start of text in the main stream // get the start of text in the main stream
int fcMin = _fib.getFcMin(); int fcMin = _fib.getFcMin();
@ -226,6 +229,20 @@ public class HWPFDocument extends POIDocument
_cbt.adjustForDelete(0, 0, cpMin); _cbt.adjustForDelete(0, 0, cpMin);
_pbt.adjustForDelete(0, 0, cpMin); _pbt.adjustForDelete(0, 0, cpMin);
} }
// Read FSPA and Escher information
_fspa = new FSPATable(_tableStream, _fib.getFcPlcspaMom(), _fib.getLcbPlcspaMom(), getTextTable().getTextPieces());
if (_fib.getFcDggInfo() != 0)
{
_dgg = new EscherRecordHolder(_tableStream, _fib.getFcDggInfo(), _fib.getLcbDggInfo());
} else
{
_dgg = new EscherRecordHolder();
}
// read in the pictures stream
_pictures = new PicturesTable(this, _dataStream, _mainStream, _fspa, _dgg);
_st = new SectionTable(_mainStream, _tableStream, _fib.getFcPlcfsed(), _fib.getLcbPlcfsed(), fcMin, getTextTable().getTextPieces()); _st = new SectionTable(_mainStream, _tableStream, _fib.getFcPlcfsed(), _fib.getLcbPlcfsed(), fcMin, getTextTable().getTextPieces());
_ss = new StyleSheet(_tableStream, _fib.getFcStshf()); _ss = new StyleSheet(_tableStream, _fib.getFcStshf());

View File

@ -0,0 +1,116 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.apache.poi.hwpf.model;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.ddf.DefaultEscherRecordFactory;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.ddf.EscherRecordFactory;
/**
* Based on AbstractEscherRecordHolder fomr HSSF.
*
* @author Squeeself
*/
public class EscherRecordHolder
{
protected ArrayList escherRecords = new ArrayList();
public EscherRecordHolder()
{
}
public EscherRecordHolder(byte[] data, int offset, int size)
{
fillEscherRecords(data, offset, size);
}
private void fillEscherRecords(byte[] data, int offset, int size)
{
EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
int pos = offset;
while ( pos < offset + size)
{
EscherRecord r = recordFactory.createRecord(data, pos);
escherRecords.add(r);
int bytesRead = r.fillFields(data, pos, recordFactory);
pos += bytesRead + 1; // There is an empty byte between each top-level record in a Word doc
}
}
public List getEscherRecords()
{
return escherRecords;
}
public String toString()
{
StringBuffer buffer = new StringBuffer();
final String nl = System.getProperty("line.separator");
if (escherRecords.size() == 0)
buffer.append("No Escher Records Decoded" + nl);
for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); )
{
EscherRecord r = (EscherRecord) iterator.next();
buffer.append(r.toString());
}
return buffer.toString();
}
/**
* If we have a EscherContainerRecord as one of our
* children (and most top level escher holders do),
* then return that.
*/
public EscherContainerRecord getEscherContainer() {
for(Iterator it = escherRecords.iterator(); it.hasNext();) {
Object er = it.next();
if(er instanceof EscherContainerRecord) {
return (EscherContainerRecord)er;
}
}
return null;
}
/**
* Descends into all our children, returning the
* first EscherRecord with the given id, or null
* if none found
*/
public EscherRecord findFirstWithId(short id) {
return findFirstWithId(id, getEscherRecords());
}
private EscherRecord findFirstWithId(short id, List records) {
// Check at our level
for(Iterator it = records.iterator(); it.hasNext();) {
EscherRecord r = (EscherRecord)it.next();
if(r.getRecordId() == id) {
return r;
}
}
// Then check our children in turn
for(Iterator it = records.iterator(); it.hasNext();) {
EscherRecord r = (EscherRecord)it.next();
if(r.isContainerRecord()) {
EscherRecord found =
findFirstWithId(id, r.getChildRecords());
if(found != null) {
return found;
}
}
}
// Not found in this lot
return null;
}
}

View File

@ -0,0 +1,182 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hwpf.model;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
/**
* File Shape Address structure
*
* @author Squeeself
*/
public class FSPA
{
public static final int FSPA_SIZE = 26;
private int spid; // Shape identifier. Used to get data position
private int xaLeft; // Enclosing rectangle
private int yaTop; // Enclosing rectangle
private int xaRight; // Enclosing rectangle
private int yaBottom; // Enclosing rectangle
private short options;
private static BitField fHdr = BitFieldFactory.getInstance(0x0001); // 1 in undo when in header
private static BitField bx = BitFieldFactory.getInstance(0x0006); // x pos relative to anchor CP: 0 - page margin, 1 - top of page, 2 - text, 3 - reserved
private static BitField by = BitFieldFactory.getInstance(0x0018); // y pos relative to anchor CP: ditto
private static BitField wr = BitFieldFactory.getInstance(0x01E0); // Text wrapping mode: 0 - like 2 w/o absolute, 1 - no text next to shape, 2 - wrap around absolute object, 3 - wrap as if no object, 4 - wrap tightly around object, 5 - wrap tightly, allow holes, 6-15 - reserved
private static BitField wrk = BitFieldFactory.getInstance(0x1E00); // Text wrapping mode type (for modes 2&4): 0 - wrap both sides, 1 - wrap only left, 2 - wrap only right, 3 - wrap largest side
private static BitField fRcaSimple = BitFieldFactory.getInstance(0x2000); // Overwrites bx if set, forcing rectangle to be page relative
private static BitField fBelowText = BitFieldFactory.getInstance(0x4000); // if true, shape is below text, otherwise above
private static BitField fAnchorLock = BitFieldFactory.getInstance(0x8000); // if true, anchor is locked
private int cTxbx; // Count of textboxes in shape (undo doc only)
public FSPA()
{
}
public FSPA(byte[] bytes, int offset)
{
spid = LittleEndian.getInt(bytes, offset);
offset += LittleEndian.INT_SIZE;
xaLeft = LittleEndian.getInt(bytes, offset);
offset += LittleEndian.INT_SIZE;
yaTop = LittleEndian.getInt(bytes, offset);
offset += LittleEndian.INT_SIZE;
xaRight = LittleEndian.getInt(bytes, offset);
offset += LittleEndian.INT_SIZE;
yaBottom = LittleEndian.getInt(bytes, offset);
offset += LittleEndian.INT_SIZE;
options = LittleEndian.getShort(bytes, offset);
offset += LittleEndian.SHORT_SIZE;
cTxbx = LittleEndian.getInt(bytes, offset);
}
public int getSpid()
{
return spid;
}
public int getXaLeft()
{
return xaLeft;
}
public int getYaTop()
{
return yaTop;
}
public int getXaRight()
{
return xaRight;
}
public int getYaBottom()
{
return yaBottom;
}
public boolean isFHdr()
{
return fHdr.isSet(options);
}
public short getBx()
{
return bx.getShortValue(options);
}
public short getBy()
{
return by.getShortValue(options);
}
public short getWr()
{
return wr.getShortValue(options);
}
public short getWrk()
{
return wrk.getShortValue(options);
}
public boolean isFRcaSimple()
{
return fRcaSimple.isSet(options);
}
public boolean isFBelowText()
{
return fBelowText.isSet(options);
}
public boolean isFAnchorLock()
{
return fAnchorLock.isSet(options);
}
public int getCTxbx()
{
return cTxbx;
}
public byte[] toByteArray()
{
int offset = 0;
byte[] buf = new byte[FSPA_SIZE];
LittleEndian.putInt(buf, offset, spid);
offset += LittleEndian.INT_SIZE;
LittleEndian.putInt(buf, offset, xaLeft);
offset += LittleEndian.INT_SIZE;
LittleEndian.putInt(buf, offset, yaTop);
offset += LittleEndian.INT_SIZE;
LittleEndian.putInt(buf, offset, xaRight);
offset += LittleEndian.INT_SIZE;
LittleEndian.putInt(buf, offset, yaBottom);
offset += LittleEndian.INT_SIZE;
LittleEndian.putShort(buf, offset, options);
offset += LittleEndian.SHORT_SIZE;
LittleEndian.putInt(buf, offset, cTxbx);
offset += LittleEndian.INT_SIZE;
return buf;
}
public String toString()
{
StringBuffer buf = new StringBuffer();
buf.append("spid: ").append(spid);
buf.append(", xaLeft: ").append(xaLeft);
buf.append(", yaTop: ").append(yaTop);
buf.append(", xaRight: ").append(xaRight);
buf.append(", yaBottom: ").append(yaBottom);
buf.append(", options: ").append(options);
buf.append(" (fHdr: ").append(isFHdr());
buf.append(", bx: ").append(getBx());
buf.append(", by: ").append(getBy());
buf.append(", wr: ").append(getWr());
buf.append(", wrk: ").append(getWrk());
buf.append(", fRcaSimple: ").append(isFRcaSimple());
buf.append(", fBelowText: ").append(isFBelowText());
buf.append(", fAnchorLock: ").append(isFAnchorLock());
buf.append("), cTxbx: ").append(cTxbx);
return buf.toString();
}
}

View File

@ -0,0 +1,82 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hwpf.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* This class holds all the FSPA (File Shape Address) structures.
*
* @author Squeeself
*/
public class FSPATable
{
protected ArrayList shapes = new ArrayList();
protected HashMap cps = new HashMap();
protected List _text;
public FSPATable(byte[] tableStream, int fcPlcspa, int lcbPlcspa, List tpt)
{
_text = tpt;
// Will be 0 if no drawing objects in document
if (fcPlcspa == 0)
return;
PlexOfCps plex = new PlexOfCps(tableStream, fcPlcspa, lcbPlcspa, FSPA.FSPA_SIZE);
for (int i=0; i < plex.length(); i++)
{
GenericPropertyNode property = plex.getProperty(i);
FSPA fspa = new FSPA(property.getBytes(), 0);
shapes.add(fspa);
cps.put(Integer.valueOf(property.getStart()), Integer.valueOf(i));
}
}
public FSPA getFspaFromCp(int cp)
{
Integer idx = (Integer)cps.get(Integer.valueOf(cp));
if (idx == null)
return null;
return (FSPA)shapes.get(idx.intValue());
}
public List getShapes()
{
return shapes;
}
public String toString()
{
StringBuffer buf = new StringBuffer();
buf.append("[FPSA PLC size=").append(shapes.size()).append("]\n");
for (Iterator it = cps.keySet().iterator(); it.hasNext(); )
{
Integer i = (Integer) it.next();
FSPA fspa = (FSPA) shapes.get(((Integer)cps.get(i)).intValue());
buf.append(" [FC: ").append(i.toString()).append("] ");
buf.append(fspa.toString());
buf.append("\n");
}
buf.append("[/FSPA PLC]");
return buf.toString();
}
}

View File

@ -308,6 +308,26 @@ public class FileInformationBlock extends FIBAbstractType
{ {
return _fieldHandler.getFieldSize(FIBFieldHandler.PLCFFLDMOM); return _fieldHandler.getFieldSize(FIBFieldHandler.PLCFFLDMOM);
} }
public int getFcPlcspaMom()
{
return _fieldHandler.getFieldOffset(FIBFieldHandler.PLCSPAMOM);
}
public int getLcbPlcspaMom()
{
return _fieldHandler.getFieldSize(FIBFieldHandler.PLCSPAMOM);
}
public int getFcDggInfo()
{
return _fieldHandler.getFieldOffset(FIBFieldHandler.DGGINFO);
}
public int getLcbDggInfo()
{
return _fieldHandler.getFieldSize(FIBFieldHandler.DGGINFO);
}
public void writeTo (byte[] mainStream, HWPFOutputStream tableStream) public void writeTo (byte[] mainStream, HWPFOutputStream tableStream)
throws IOException throws IOException

View File

@ -26,7 +26,12 @@ import org.apache.poi.hwpf.usermodel.Range;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import org.apache.poi.ddf.DefaultEscherRecordFactory;
import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherBlipRecord;
import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.ddf.EscherRecordFactory;
/** /**
* Holds information about all pictures embedded in Word Document either via "Insert -> Picture -> From File" or via * Holds information about all pictures embedded in Word Document either via "Insert -> Picture -> From File" or via
@ -57,6 +62,9 @@ public class PicturesTable
private HWPFDocument _document; private HWPFDocument _document;
private byte[] _dataStream; private byte[] _dataStream;
private byte[] _mainStream;
private FSPATable _fspa;
private EscherRecordHolder _dgg;
/** @link dependency /** @link dependency
* @stereotype instantiate*/ * @stereotype instantiate*/
@ -67,10 +75,13 @@ public class PicturesTable
* @param document * @param document
* @param _dataStream * @param _dataStream
*/ */
public PicturesTable(HWPFDocument _document, byte[] _dataStream) public PicturesTable(HWPFDocument _document, byte[] _dataStream, byte[] _mainStream, FSPATable fspa, EscherRecordHolder dgg)
{ {
this._document = _document; this._document = _document;
this._dataStream = _dataStream; this._dataStream = _dataStream;
this._mainStream = _mainStream;
this._fspa = fspa;
this._dgg = dgg;
} }
/** /**
@ -83,6 +94,13 @@ public class PicturesTable
} }
return false; return false;
} }
public boolean hasEscherPicture(CharacterRun run) {
if (run.isSpecialCharacter() && !run.isObj() && !run.isOle2() && !run.isData() && run.text().startsWith("\u0008")) {
return true;
}
return false;
}
/** /**
* determines whether specified CharacterRun contains reference to a picture * determines whether specified CharacterRun contains reference to a picture
@ -122,6 +140,46 @@ public class PicturesTable
} }
return null; return null;
} }
/**
* Performs a recursive search for pictures in the given list of escher records.
*
* @param escherRecords the escher records.
* @param pictures the list to populate with the pictures.
*/
private void searchForPictures(List escherRecords, List pictures)
{
Iterator recordIter = escherRecords.iterator();
while (recordIter.hasNext())
{
Object obj = recordIter.next();
if (obj instanceof EscherRecord)
{
EscherRecord escherRecord = (EscherRecord) obj;
if (escherRecord instanceof EscherBSERecord)
{
EscherBSERecord bse = (EscherBSERecord) escherRecord;
EscherBlipRecord blip = bse.getBlipRecord();
if (blip != null)
{
pictures.add(new Picture(blip.getPicturedata()));
}
else if (bse.getOffset() > 0)
{
// Blip stored in delay stream, which in a word doc, is the main stream
EscherRecordFactory recordFactory = new DefaultEscherRecordFactory();
blip = (EscherBlipRecord) recordFactory.createRecord(_mainStream, bse.getOffset());
blip.fillFields(_mainStream, bse.getOffset(), recordFactory);
pictures.add(new Picture(blip.getPicturedata()));
}
}
// Recursive call.
searchForPictures(escherRecord.getChildRecords(), pictures);
}
}
}
/** /**
* Not all documents have all the images concatenated in the data stream * Not all documents have all the images concatenated in the data stream
@ -136,12 +194,13 @@ public class PicturesTable
for (int i = 0; i < range.numCharacterRuns(); i++) { for (int i = 0; i < range.numCharacterRuns(); i++) {
CharacterRun run = range.getCharacterRun(i); CharacterRun run = range.getCharacterRun(i);
String text = run.text(); String text = run.text();
int j = text.charAt(0);
Picture picture = extractPicture(run, false); Picture picture = extractPicture(run, false);
if (picture != null) { if (picture != null) {
pictures.add(picture); pictures.add(picture);
} }
} }
searchForPictures(_dgg.getEscherRecords(), pictures);
return pictures; return pictures;
} }

View File

@ -98,6 +98,15 @@ public class Picture
fillImageContent(); fillImageContent();
} }
} }
public Picture(byte[] _dataStream)
{
this._dataStream = _dataStream;
this.dataBlockStartOfsset = 0;
this.dataBlockSize = _dataStream.length;
this.pictureBytesStartOffset = 0;
this.size = _dataStream.length;
}
private void fillWidthHeight() private void fillWidthHeight()
{ {
@ -363,6 +372,7 @@ public class Picture
do { do {
firstByte = _dataStream[pointer]; firstByte = _dataStream[pointer];
secondByte = _dataStream[pointer+1]; secondByte = _dataStream[pointer+1];
pointer += 2;
} while (!(firstByte==(byte)0xFF) && pointer<endOfPicture-1); } while (!(firstByte==(byte)0xFF) && pointer<endOfPicture-1);
if (firstByte==((byte)0xFF) && pointer<endOfPicture-1) { if (firstByte==((byte)0xFF) && pointer<endOfPicture-1) {

View File

@ -35,10 +35,12 @@ public class TestHWPFPictures extends TestCase {
private String docAFile; private String docAFile;
private String docBFile; private String docBFile;
private String docCFile; private String docCFile;
private String docDFile;
private String imgAFile; private String imgAFile;
private String imgBFile; private String imgBFile;
private String imgCFile; private String imgCFile;
private String imgDFile;
protected void setUp() throws Exception { protected void setUp() throws Exception {
String dirname = System.getProperty("HWPF.testdata.path"); String dirname = System.getProperty("HWPF.testdata.path");
@ -46,10 +48,12 @@ public class TestHWPFPictures extends TestCase {
docAFile = dirname + "/testPictures.doc"; docAFile = dirname + "/testPictures.doc";
docBFile = dirname + "/two_images.doc"; docBFile = dirname + "/two_images.doc";
docCFile = dirname + "/vector_image.doc"; docCFile = dirname + "/vector_image.doc";
docDFile = dirname + "/GaiaTest.doc";
imgAFile = dirname + "/simple_image.jpg"; imgAFile = dirname + "/simple_image.jpg";
imgBFile = dirname + "/simple_image.png"; imgBFile = dirname + "/simple_image.png";
imgCFile = dirname + "/vector_image.emf"; imgCFile = dirname + "/vector_image.emf";
imgDFile = dirname + "/GaiaTestImg.png";
} }
/** /**
@ -126,7 +130,26 @@ public class TestHWPFPictures extends TestCase {
assertEquals(picBytes.length, pic.getContent().length); assertEquals(picBytes.length, pic.getContent().length);
assertBytesSame(picBytes, pic.getContent()); assertBytesSame(picBytes, pic.getContent());
} }
/**
* Pending the missing files being uploaded to
* bug #44937
*/
public void BROKENtestEscherDrawing() throws Exception
{
HWPFDocument docD = new HWPFDocument(new FileInputStream(docDFile));
List allPictures = docD.getPicturesTable().getAllPictures();
assertEquals(1, allPictures.size());
Picture pic = (Picture) allPictures.get(0);
assertNotNull(pic);
byte[] picD = readFile(imgDFile);
assertEquals(picD.length, pic.getContent().length);
assertBytesSame(picD, pic.getContent());
}
private void assertBytesSame(byte[] a, byte[] b) { private void assertBytesSame(byte[] a, byte[] b) {
assertEquals(a.length, b.length); assertEquals(a.length, b.length);

View File

@ -38,6 +38,8 @@ public final class TestExternalNameRecord extends TestCase {
// data taken from bugzilla 44774 att 21790 // data taken from bugzilla 44774 att 21790
private static final byte[] dataPlainName = { private static final byte[] dataPlainName = {
0, 0, 0, 0, 0, 0, 9, 0, 82, 97, 116, 101, 95, 68, 97, 116, 101, 9, 0, 58, 0, 0, 0, 0, 4, 0, 8, 0 0, 0, 0, 0, 0, 0, 9, 0, 82, 97, 116, 101, 95, 68, 97, 116, 101, 9, 0, 58, 0, 0, 0, 0, 4, 0, 8, 0
// TODO - the last 2 bytes of formula data (8,0) seem weird. They encode to ConcatPtg, UnknownPtg
// UnknownPtg is otherwise not created by any other test cases
}; };
private static ExternalNameRecord createSimpleENR(byte[] data) { private static ExternalNameRecord createSimpleENR(byte[] data) {

View File

@ -19,17 +19,15 @@ package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.List;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.ConcatPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.RangePtg;
import org.apache.poi.hssf.record.formula.ReferencePtg;
import org.apache.poi.hssf.record.formula.UnknownPtg;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.ReferencePtg;
/** /**
* Tests the serialization and deserialization of the FormulaRecord * Tests the serialization and deserialization of the FormulaRecord
* class works correctly. * class works correctly.
@ -57,7 +55,7 @@ public final class TestFormulaRecord extends TestCase {
*/ */
public void testCheckNanPreserve() { public void testCheckNanPreserve() {
byte[] formulaByte = new byte[29]; byte[] formulaByte = new byte[29];
for (int i = 0; i < formulaByte.length; i++) formulaByte[i] = (byte)0;
formulaByte[4] = (byte)0x0F; formulaByte[4] = (byte)0x0F;
formulaByte[6] = (byte)0x02; formulaByte[6] = (byte)0x02;
formulaByte[8] = (byte)0x07; formulaByte[8] = (byte)0x07;
@ -91,8 +89,6 @@ public final class TestFormulaRecord extends TestCase {
public void testExpFormula() { public void testExpFormula() {
byte[] formulaByte = new byte[27]; byte[] formulaByte = new byte[27];
for (int i = 0; i < formulaByte.length; i++) formulaByte[i] = (byte)0;
formulaByte[4] =(byte)0x0F; formulaByte[4] =(byte)0x0F;
formulaByte[14]=(byte)0x08; formulaByte[14]=(byte)0x08;
formulaByte[18]=(byte)0xE0; formulaByte[18]=(byte)0xE0;
@ -109,15 +105,14 @@ public final class TestFormulaRecord extends TestCase {
public void testWithConcat() throws Exception { public void testWithConcat() throws Exception {
// =CHOOSE(2,A2,A3,A4) // =CHOOSE(2,A2,A3,A4)
byte[] data = new byte[] { byte[] data = {
6, 0, 68, 0, 6, 0, 68, 0,
1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57, 1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57,
64, 0, 0, 12, 0, 12, -4, 46, 0, 64, 0, 0, 12, 0, 12, -4, 46, 0,
30, 2, 0, // Int - 2 30, 2, 0, // Int - 2
25, 4, 3, 0, // Attr 25, 4, 3, 0, // Attr
8, 0, // Concat 8, 0, 17, 0, 26, 0, // jumpTable
17, 0, // Range 35, 0, // chooseOffset
26, 0, 35, 0, // Bit like an attr
36, 1, 0, 0, -64, // Ref - A2 36, 1, 0, 0, -64, // Ref - A2
25, 8, 21, 0, // Attr 25, 8, 21, 0, // Attr
36, 2, 0, 0, -64, // Ref - A3 36, 2, 0, 0, -64, // Ref - A3
@ -126,30 +121,24 @@ public final class TestFormulaRecord extends TestCase {
25, 8, 3, 0, // Attr 25, 8, 3, 0, // Attr
66, 4, 100, 0 // CHOOSE 66, 4, 100, 0 // CHOOSE
}; };
RecordInputStream inp = new RecordInputStream( RecordInputStream inp = new RecordInputStream( new ByteArrayInputStream(data));
new ByteArrayInputStream(data)
);
inp.nextRecord(); inp.nextRecord();
FormulaRecord fr = new FormulaRecord(inp); FormulaRecord fr = new FormulaRecord(inp);
assertEquals(14, fr.getNumberOfExpressionTokens()); List ptgs = fr.getParsedExpression();
assertEquals(IntPtg.class, fr.getParsedExpression().get(0).getClass()); assertEquals(9, ptgs.size());
assertEquals(AttrPtg.class, fr.getParsedExpression().get(1).getClass()); assertEquals(IntPtg.class, ptgs.get(0).getClass());
assertEquals(ConcatPtg.class, fr.getParsedExpression().get(2).getClass()); assertEquals(AttrPtg.class, ptgs.get(1).getClass());
assertEquals(UnknownPtg.class, fr.getParsedExpression().get(3).getClass()); assertEquals(ReferencePtg.class, ptgs.get(2).getClass());
assertEquals(RangePtg.class, fr.getParsedExpression().get(4).getClass()); assertEquals(AttrPtg.class, ptgs.get(3).getClass());
assertEquals(UnknownPtg.class, fr.getParsedExpression().get(5).getClass()); assertEquals(ReferencePtg.class, ptgs.get(4).getClass());
assertEquals(AttrPtg.class, fr.getParsedExpression().get(6).getClass()); assertEquals(AttrPtg.class, ptgs.get(5).getClass());
assertEquals(ReferencePtg.class, fr.getParsedExpression().get(7).getClass()); assertEquals(ReferencePtg.class, ptgs.get(6).getClass());
assertEquals(AttrPtg.class, fr.getParsedExpression().get(8).getClass()); assertEquals(AttrPtg.class, ptgs.get(7).getClass());
assertEquals(ReferencePtg.class, fr.getParsedExpression().get(9).getClass()); assertEquals(FuncVarPtg.class, ptgs.get(8).getClass());
assertEquals(AttrPtg.class, fr.getParsedExpression().get(10).getClass());
assertEquals(ReferencePtg.class, fr.getParsedExpression().get(11).getClass());
assertEquals(AttrPtg.class, fr.getParsedExpression().get(12).getClass());
assertEquals(FuncVarPtg.class, fr.getParsedExpression().get(13).getClass());
FuncVarPtg choose = (FuncVarPtg)fr.getParsedExpression().get(13); FuncVarPtg choose = (FuncVarPtg)ptgs.get(8);
assertEquals("CHOOSE", choose.getName()); assertEquals("CHOOSE", choose.getName());
} }

View File

@ -17,18 +17,19 @@
package org.apache.poi.hssf.usermodel; package org.apache.poi.hssf.usermodel;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.ss.util.Region; import org.apache.poi.ss.util.Region;
import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.util.TempFile; import org.apache.poi.util.TempFile;
import java.io.*;
import java.util.Iterator;
/** /**
* Testcases for bugs entered in bugzilla * Testcases for bugs entered in bugzilla
* the Test name contains the bugzilla bug id * the Test name contains the bugzilla bug id
@ -81,13 +82,7 @@ public final class TestBugs extends TestCase {
HSSFRow r = s.createRow(0); HSSFRow r = s.createRow(0);
HSSFCell c = r.createCell((short)0); HSSFCell c = r.createCell((short)0);
c.setCellValue(10); c.setCellValue(10);
try { writeOutAndReadBack(wb);
writeOutAndReadBack(wb);
} catch (RecordFormatException e) {
if (false) { // TODO (Apr-2008) this file does not read back ok. create bugzilla bug & fix.
throw new AssertionFailedError("Identified bug XXXX");
}
}
} }
/**Test writing a hyperlink /**Test writing a hyperlink
* Open resulting sheet in Excel and check that A1 contains a hyperlink*/ * Open resulting sheet in Excel and check that A1 contains a hyperlink*/
@ -758,9 +753,13 @@ public final class TestBugs extends TestCase {
HSSFCell c2 = r2.getCell((short)1); HSSFCell c2 = r2.getCell((short)1);
assertEquals(25, (int)c2.getNumericCellValue()); assertEquals(25, (int)c2.getNumericCellValue());
if (false) { // TODO (Apr-2008) This will blow up with IllegalStateException (stack underflow) try {
// excel function "CHOOSE" probably needs some special handling in FormulaParser.toFormulaString() assertEquals("CHOOSE(2,A2,A3,A4)", c2.getCellFormula());
assertEquals("=CHOOSE(2,A2,A3,A4)", c2.getCellFormula()); } catch (IllegalStateException e) {
if (e.getMessage().startsWith("Too few arguments")
&& e.getMessage().indexOf("ConcatPtg") > 0) {
throw new AssertionFailedError("identified bug 44306");
}
} }
} }
@ -888,13 +887,13 @@ public final class TestBugs extends TestCase {
writeOutAndReadBack(wb); writeOutAndReadBack(wb);
assertTrue("no errors writing sample xls", true); assertTrue("no errors writing sample xls", true);
} }
/** /**
* Had a problem apparently, not sure what as it * Had a problem apparently, not sure what as it
* works just fine... * works just fine...
*/ */
public void test44891() throws Exception { public void test44891() throws Exception {
HSSFWorkbook wb = openSample("44891.xls"); HSSFWorkbook wb = openSample("44891.xls");
assertTrue("no errors reading sample xls", true); assertTrue("no errors reading sample xls", true);
writeOutAndReadBack(wb); writeOutAndReadBack(wb);
assertTrue("no errors writing sample xls", true); assertTrue("no errors writing sample xls", true);
@ -906,7 +905,7 @@ public final class TestBugs extends TestCase {
* Works fine with poi-3.1-beta1. * Works fine with poi-3.1-beta1.
*/ */
public void test44235() throws Exception { public void test44235() throws Exception {
HSSFWorkbook wb = openSample("44235.xls"); HSSFWorkbook wb = openSample("44235.xls");
assertTrue("no errors reading sample xls", true); assertTrue("no errors reading sample xls", true);
writeOutAndReadBack(wb); writeOutAndReadBack(wb);
assertTrue("no errors writing sample xls", true); assertTrue("no errors writing sample xls", true);
@ -930,7 +929,7 @@ public final class TestBugs extends TestCase {
} }
public void test36947() throws Exception { public void test36947() throws Exception {
HSSFWorkbook wb = openSample("36947.xls"); HSSFWorkbook wb = openSample("36947.xls");
assertTrue("no errors reading sample xls", true); assertTrue("no errors reading sample xls", true);
writeOutAndReadBack(wb); writeOutAndReadBack(wb);
assertTrue("no errors writing sample xls", true); assertTrue("no errors writing sample xls", true);
@ -947,7 +946,7 @@ public final class TestBugs extends TestCase {
} }
public void test39634() throws Exception { public void test39634() throws Exception {
HSSFWorkbook wb = openSample("39634.xls"); HSSFWorkbook wb = openSample("39634.xls");
assertTrue("no errors reading sample xls", true); assertTrue("no errors reading sample xls", true);
writeOutAndReadBack(wb); writeOutAndReadBack(wb);
assertTrue("no errors writing sample xls", true); assertTrue("no errors writing sample xls", true);

View File

@ -204,4 +204,64 @@ public final class TestHSSFRow extends TestCase {
row.createCell((short) 255); row.createCell((short) 255);
assertEquals(256, row.getLastCellNum()); assertEquals(256, row.getLastCellNum());
} }
/**
* Tests for the missing/blank cell policy stuff
*/
public void testGetCellPolicy() throws Exception {
HSSFWorkbook book = new HSSFWorkbook();
HSSFSheet sheet = book.createSheet("test");
HSSFRow row = sheet.createRow(0);
// 0 -> string
// 1 -> num
// 2 missing
// 3 missing
// 4 -> blank
// 5 -> num
row.createCell((short)0).setCellValue(new HSSFRichTextString("test"));
row.createCell((short)1).setCellValue(3.2);
row.createCell((short)4, HSSFCell.CELL_TYPE_BLANK);
row.createCell((short)5).setCellValue(4);
// First up, no policy
assertEquals(HSSFCell.CELL_TYPE_STRING, row.getCell(0).getCellType());
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(1).getCellType());
assertEquals(null, row.getCell(2));
assertEquals(null, row.getCell(3));
assertEquals(HSSFCell.CELL_TYPE_BLANK, row.getCell(4).getCellType());
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(5).getCellType());
// RETURN_NULL_AND_BLANK - same as default
assertEquals(HSSFCell.CELL_TYPE_STRING, row.getCell(0, HSSFRow.RETURN_NULL_AND_BLANK).getCellType());
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(1, HSSFRow.RETURN_NULL_AND_BLANK).getCellType());
assertEquals(null, row.getCell(2, HSSFRow.RETURN_NULL_AND_BLANK));
assertEquals(null, row.getCell(3, HSSFRow.RETURN_NULL_AND_BLANK));
assertEquals(HSSFCell.CELL_TYPE_BLANK, row.getCell(4, HSSFRow.RETURN_NULL_AND_BLANK).getCellType());
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(5, HSSFRow.RETURN_NULL_AND_BLANK).getCellType());
// RETURN_BLANK_AS_NULL - nearly the same
assertEquals(HSSFCell.CELL_TYPE_STRING, row.getCell(0, HSSFRow.RETURN_BLANK_AS_NULL).getCellType());
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(1, HSSFRow.RETURN_BLANK_AS_NULL).getCellType());
assertEquals(null, row.getCell(2, HSSFRow.RETURN_BLANK_AS_NULL));
assertEquals(null, row.getCell(3, HSSFRow.RETURN_BLANK_AS_NULL));
assertEquals(null, row.getCell(4, HSSFRow.RETURN_BLANK_AS_NULL));
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(5, HSSFRow.RETURN_BLANK_AS_NULL).getCellType());
// CREATE_NULL_AS_BLANK - creates as needed
assertEquals(HSSFCell.CELL_TYPE_STRING, row.getCell(0, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(1, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(HSSFCell.CELL_TYPE_BLANK, row.getCell(2, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(HSSFCell.CELL_TYPE_BLANK, row.getCell(3, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(HSSFCell.CELL_TYPE_BLANK, row.getCell(4, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, row.getCell(5, HSSFRow.CREATE_NULL_AS_BLANK).getCellType());
// Check created ones get the right column
assertEquals((short)0, row.getCell(0, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)1, row.getCell(1, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)2, row.getCell(2, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)3, row.getCell(3, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)4, row.getCell(4, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
assertEquals((short)5, row.getCell(5, HSSFRow.CREATE_NULL_AS_BLANK).getCellNum());
}
} }

View File

@ -130,7 +130,7 @@ public final class TestPOIFSFileSystem extends TestCase {
* The other is to fix the handling of the last block in * The other is to fix the handling of the last block in
* POIFS, since it seems to be slight wrong * POIFS, since it seems to be slight wrong
*/ */
public void DISABLEDtestShortLastBlock() throws Exception { public void testShortLastBlock() throws Exception {
String[] files = new String[] { String[] files = new String[] {
"ShortLastBlock.qwp", "ShortLastBlock.wps" "ShortLastBlock.qwp", "ShortLastBlock.wps"
}; };