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-ffa450edef68pull/6/head
@@ -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 Excel files, irrespective of if they are .xls or .xlsx</action> | |||
</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">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> |
@@ -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 Excel files, irrespective of if they are .xls or .xlsx</action> | |||
</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">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> |
@@ -65,20 +65,27 @@ public class EscherClientAnchorRecord | |||
int size = 0; | |||
// Always find 4 two byte entries. Sometimes find 9 | |||
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; | |||
if(bytesRemaining >= 18) { | |||
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; | |||
shortRecord = false; | |||
} else { | |||
shortRecord = true; | |||
} | |||
if (bytesRemaining == 4) // Word format only 4 bytes | |||
{ | |||
// Not sure exactly what the format is quite yet, likely a reference to a PLC | |||
} | |||
else | |||
{ | |||
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; | |||
if(bytesRemaining >= 18) { | |||
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; | |||
shortRecord = false; | |||
} else { | |||
shortRecord = true; | |||
} | |||
} | |||
bytesRemaining -= size; | |||
remainingData = new byte[bytesRemaining]; | |||
System.arraycopy( data, pos + size, remainingData, 0, bytesRemaining ); |
@@ -134,7 +134,10 @@ public final class FormulaParser { | |||
/** Report What Was Expected */ | |||
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) { | |||
AttrPtg attrPtg = ((AttrPtg) ptg); | |||
if (attrPtg.isOptimizedIf()) { | |||
if (attrPtg.isOptimizedIf() || attrPtg.isOptimizedChoose() || attrPtg.isGoto()) { | |||
continue; | |||
} | |||
if (attrPtg.isSpace()) { | |||
@@ -1014,6 +1017,9 @@ end; | |||
// similar to tAttrSpace - RPN is violated | |||
continue; | |||
} | |||
if (!attrPtg.isSum()) { | |||
throw new RuntimeException("Unexpected tAttr: " + attrPtg.toString()); | |||
} | |||
} | |||
final OperationPtg o = (OperationPtg) ptg; |
@@ -39,6 +39,11 @@ public final class AttrPtg extends OperationPtg { | |||
private byte field_1_options; | |||
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. | |||
// OOO spec says other combinations are theoretically possible but not likely to occur. | |||
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) */ | |||
public static final int CR_BEFORE_OPEN_PAREN = 0x03; | |||
/** 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) */ | |||
public static final int CR_BEFORE_CLOSE_PAREN = 0x05; | |||
/** 06H = Spaces following the equality sign (only in macro sheets) */ | |||
@@ -71,16 +76,33 @@ public final class AttrPtg extends OperationPtg { | |||
} | |||
public AttrPtg() { | |||
_jumpTable = null; | |||
_chooseFuncOffset = -1; | |||
} | |||
public AttrPtg(RecordInputStream in) | |||
{ | |||
field_1_options = in.readByte(); | |||
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_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) { | |||
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) | |||
@@ -136,14 +158,14 @@ public final class AttrPtg extends OperationPtg { | |||
field_1_options=optiIf.setByteBoolean(field_1_options,bif); | |||
} | |||
/** | |||
* Flags this ptg as a goto/jump | |||
* @param isGoto | |||
*/ | |||
public void setGoto(boolean isGoto) { | |||
field_1_options=optGoto.setByteBoolean(field_1_options, isGoto); | |||
} | |||
/** | |||
* Flags this ptg as a goto/jump | |||
* @param isGoto | |||
*/ | |||
public void setGoto(boolean isGoto) { | |||
field_1_options=optGoto.setByteBoolean(field_1_options, isGoto); | |||
} | |||
// lets hope no one uses this anymore | |||
public boolean isBaxcel() | |||
{ | |||
@@ -181,7 +203,7 @@ public final class AttrPtg extends OperationPtg { | |||
if(isOptimizedIf()) { | |||
sb.append("if dist=").append(getData()); | |||
} else if(isOptimizedChoose()) { | |||
sb.append("choose dist=").append(getData()); | |||
sb.append("choose nCases=").append(getData()); | |||
} else if(isGoto()) { | |||
sb.append("skip dist=").append(getData()); | |||
} else if(isSum()) { | |||
@@ -195,13 +217,28 @@ public final class AttrPtg extends OperationPtg { | |||
public void writeBytes(byte [] array, int offset) | |||
{ | |||
array[offset]=sid; | |||
array[offset+1]=field_1_options; | |||
LittleEndian.putShort(array,offset+2,field_2_data); | |||
LittleEndian.putByte(array, offset+0, sid); | |||
LittleEndian.putByte(array, offset+1, field_1_options); | |||
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() | |||
{ | |||
if (_jumpTable != null) { | |||
return SIZE + (_jumpTable.length + 1) * LittleEndian.SHORT_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() { | |||
AttrPtg ptg = new AttrPtg(); | |||
ptg.field_1_options = field_1_options; | |||
ptg.field_2_data = field_2_data; | |||
return ptg; | |||
int[] jt; | |||
if (_jumpTable == null) { | |||
jt = null; | |||
} else { | |||
jt = (int[]) _jumpTable.clone(); | |||
} | |||
return new AttrPtg(field_1_options, field_2_data, jt, _chooseFuncOffset); | |||
} | |||
} |
@@ -319,6 +319,36 @@ public final class HSSFRow implements Comparable, Row { | |||
if(cellnum<0||cellnum>=cells.length) return null; | |||
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. | |||
@@ -485,6 +515,7 @@ public final class HSSFRow implements Comparable, Row { | |||
return cellnum; | |||
} | |||
/** | |||
* @return cell iterator of the physically defined cells. | |||
* 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(); | |||
} | |||
/** | |||
* An iterator over the (physical) cells in the row. | |||
*/ | |||
private class CellIterator implements Iterator | |||
{ | |||
int thisId=-1; |
@@ -37,6 +37,7 @@ public class RawDataBlock | |||
{ | |||
private byte[] _data; | |||
private boolean _eof; | |||
private boolean _hasData; | |||
private static POILogger log = POILogFactory.getLogger(RawDataBlock.class); | |||
/** | |||
@@ -66,6 +67,7 @@ public class RawDataBlock | |||
throws IOException { | |||
_data = new byte[ blockSize ]; | |||
int count = IOUtils.readFully(stream, _data); | |||
_hasData = (count > 0); | |||
if (count == -1) { | |||
_eof = true; | |||
@@ -94,16 +96,21 @@ public class RawDataBlock | |||
/** | |||
* 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 | |||
* the file, else false | |||
* | |||
* @exception IOException | |||
* @return true if the EoF was hit during this block, or | |||
* false if not. If you have a dodgy short last block, then | |||
* it's possible to both have data, and also hit EoF... | |||
*/ | |||
public boolean eof() | |||
throws IOException | |||
{ | |||
public boolean 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 ********** */ | |||
@@ -117,7 +124,7 @@ public class RawDataBlock | |||
public byte [] getData() | |||
throws IOException | |||
{ | |||
if (eof()) | |||
if (! hasData()) | |||
{ | |||
throw new IOException("Cannot return empty data"); | |||
} |
@@ -51,12 +51,16 @@ public class RawDataBlockList | |||
while (true) | |||
{ | |||
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; | |||
} | |||
blocks.add(block); | |||
} | |||
setBlocks(( RawDataBlock [] ) blocks.toArray(new RawDataBlock[ 0 ])); | |||
} |
@@ -33,4 +33,22 @@ public interface Row { | |||
HSSFCell getCell(int cellnum); | |||
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(); | |||
} |
@@ -88,9 +88,20 @@ public interface Row extends Iterable<Cell> { | |||
* ask for a cell that is not defined....you get a null. | |||
* | |||
* @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); | |||
/** | |||
* 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. | |||
@@ -170,4 +181,22 @@ public interface Row extends Iterable<Cell> { | |||
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(); | |||
} |
@@ -21,6 +21,7 @@ import java.util.Iterator; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import org.apache.poi.hssf.usermodel.HSSFCell; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.Row; | |||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; | |||
@@ -138,6 +139,27 @@ public class XSSFRow implements Row { | |||
} | |||
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() { | |||
for (Iterator<Cell> it = cellIterator() ; it.hasNext() ; ) { |
@@ -22,6 +22,7 @@ import java.util.Iterator; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.Row; | |||
import org.apache.poi.xssf.usermodel.TestXSSFCell.DummySharedStringSource; | |||
/** | |||
@@ -166,6 +167,64 @@ public final class TestXSSFRow extends TestCase { | |||
row.setZeroHeight(true); | |||
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 |
@@ -53,10 +53,10 @@ public class HWPFDocument extends POIDocument | |||
protected FileInformationBlock _fib; | |||
/** main document stream buffer*/ | |||
private byte[] _mainStream; | |||
protected byte[] _mainStream; | |||
/** table stream buffer*/ | |||
private byte[] _tableStream; | |||
protected byte[] _tableStream; | |||
/** data stream buffer*/ | |||
protected byte[] _dataStream; | |||
@@ -93,6 +93,12 @@ public class HWPFDocument extends POIDocument | |||
/** Holds pictures table */ | |||
protected PicturesTable _pictures; | |||
/** Holds FSBA (shape) information */ | |||
protected FSPATable _fspa; | |||
/** Escher Drawing Group information */ | |||
protected EscherRecordHolder _dgg; | |||
protected HWPFDocument() | |||
{ | |||
@@ -204,9 +210,6 @@ public class HWPFDocument extends POIDocument | |||
{ | |||
_dataStream = new byte[0]; | |||
} | |||
// read in the pictures stream | |||
_pictures = new PicturesTable(this, _dataStream); | |||
// get the start of text in the main stream | |||
int fcMin = _fib.getFcMin(); | |||
@@ -226,6 +229,20 @@ public class HWPFDocument extends POIDocument | |||
_cbt.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()); | |||
_ss = new StyleSheet(_tableStream, _fib.getFcStshf()); |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -308,6 +308,26 @@ public class FileInformationBlock extends FIBAbstractType | |||
{ | |||
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) | |||
throws IOException |
@@ -26,7 +26,12 @@ import org.apache.poi.hwpf.usermodel.Range; | |||
import java.util.List; | |||
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 | |||
@@ -57,6 +62,9 @@ public class PicturesTable | |||
private HWPFDocument _document; | |||
private byte[] _dataStream; | |||
private byte[] _mainStream; | |||
private FSPATable _fspa; | |||
private EscherRecordHolder _dgg; | |||
/** @link dependency | |||
* @stereotype instantiate*/ | |||
@@ -67,10 +75,13 @@ public class PicturesTable | |||
* @param document | |||
* @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._mainStream = _mainStream; | |||
this._fspa = fspa; | |||
this._dgg = dgg; | |||
} | |||
/** | |||
@@ -83,6 +94,13 @@ public class PicturesTable | |||
} | |||
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 | |||
@@ -122,6 +140,46 @@ public class PicturesTable | |||
} | |||
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 | |||
@@ -136,12 +194,13 @@ public class PicturesTable | |||
for (int i = 0; i < range.numCharacterRuns(); i++) { | |||
CharacterRun run = range.getCharacterRun(i); | |||
String text = run.text(); | |||
int j = text.charAt(0); | |||
Picture picture = extractPicture(run, false); | |||
if (picture != null) { | |||
pictures.add(picture); | |||
} | |||
} | |||
searchForPictures(_dgg.getEscherRecords(), pictures); | |||
return pictures; | |||
} |
@@ -98,6 +98,15 @@ public class Picture | |||
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() | |||
{ | |||
@@ -363,6 +372,7 @@ public class Picture | |||
do { | |||
firstByte = _dataStream[pointer]; | |||
secondByte = _dataStream[pointer+1]; | |||
pointer += 2; | |||
} while (!(firstByte==(byte)0xFF) && pointer<endOfPicture-1); | |||
if (firstByte==((byte)0xFF) && pointer<endOfPicture-1) { |
@@ -35,10 +35,12 @@ public class TestHWPFPictures extends TestCase { | |||
private String docAFile; | |||
private String docBFile; | |||
private String docCFile; | |||
private String docDFile; | |||
private String imgAFile; | |||
private String imgBFile; | |||
private String imgCFile; | |||
private String imgDFile; | |||
protected void setUp() throws Exception { | |||
String dirname = System.getProperty("HWPF.testdata.path"); | |||
@@ -46,10 +48,12 @@ public class TestHWPFPictures extends TestCase { | |||
docAFile = dirname + "/testPictures.doc"; | |||
docBFile = dirname + "/two_images.doc"; | |||
docCFile = dirname + "/vector_image.doc"; | |||
docDFile = dirname + "/GaiaTest.doc"; | |||
imgAFile = dirname + "/simple_image.jpg"; | |||
imgBFile = dirname + "/simple_image.png"; | |||
imgCFile = dirname + "/vector_image.emf"; | |||
imgDFile = dirname + "/GaiaTestImg.png"; | |||
} | |||
/** | |||
@@ -126,7 +130,26 @@ public class TestHWPFPictures extends TestCase { | |||
assertEquals(picBytes.length, pic.getContent().length); | |||
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) { | |||
assertEquals(a.length, b.length); |
@@ -38,6 +38,8 @@ public final class TestExternalNameRecord extends TestCase { | |||
// data taken from bugzilla 44774 att 21790 | |||
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 | |||
// 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) { |
@@ -19,16 +19,14 @@ package org.apache.poi.hssf.record; | |||
import java.io.ByteArrayInputStream; | |||
import java.util.List; | |||
import junit.framework.TestCase; | |||
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; | |||
/** | |||
* Tests the serialization and deserialization of the FormulaRecord | |||
@@ -57,7 +55,7 @@ public final class TestFormulaRecord extends TestCase { | |||
*/ | |||
public void testCheckNanPreserve() { | |||
byte[] formulaByte = new byte[29]; | |||
for (int i = 0; i < formulaByte.length; i++) formulaByte[i] = (byte)0; | |||
formulaByte[4] = (byte)0x0F; | |||
formulaByte[6] = (byte)0x02; | |||
formulaByte[8] = (byte)0x07; | |||
@@ -91,8 +89,6 @@ public final class TestFormulaRecord extends TestCase { | |||
public void testExpFormula() { | |||
byte[] formulaByte = new byte[27]; | |||
for (int i = 0; i < formulaByte.length; i++) formulaByte[i] = (byte)0; | |||
formulaByte[4] =(byte)0x0F; | |||
formulaByte[14]=(byte)0x08; | |||
formulaByte[18]=(byte)0xE0; | |||
@@ -109,15 +105,14 @@ public final class TestFormulaRecord extends TestCase { | |||
public void testWithConcat() throws Exception { | |||
// =CHOOSE(2,A2,A3,A4) | |||
byte[] data = new byte[] { | |||
byte[] data = { | |||
6, 0, 68, 0, | |||
1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57, | |||
64, 0, 0, 12, 0, 12, -4, 46, 0, | |||
30, 2, 0, // Int - 2 | |||
25, 4, 3, 0, // Attr | |||
8, 0, // Concat | |||
17, 0, // Range | |||
26, 0, 35, 0, // Bit like an attr | |||
8, 0, 17, 0, 26, 0, // jumpTable | |||
35, 0, // chooseOffset | |||
36, 1, 0, 0, -64, // Ref - A2 | |||
25, 8, 21, 0, // Attr | |||
36, 2, 0, 0, -64, // Ref - A3 | |||
@@ -126,30 +121,24 @@ public final class TestFormulaRecord extends TestCase { | |||
25, 8, 3, 0, // Attr | |||
66, 4, 100, 0 // CHOOSE | |||
}; | |||
RecordInputStream inp = new RecordInputStream( | |||
new ByteArrayInputStream(data) | |||
); | |||
RecordInputStream inp = new RecordInputStream( new ByteArrayInputStream(data)); | |||
inp.nextRecord(); | |||
FormulaRecord fr = new FormulaRecord(inp); | |||
assertEquals(14, fr.getNumberOfExpressionTokens()); | |||
assertEquals(IntPtg.class, fr.getParsedExpression().get(0).getClass()); | |||
assertEquals(AttrPtg.class, fr.getParsedExpression().get(1).getClass()); | |||
assertEquals(ConcatPtg.class, fr.getParsedExpression().get(2).getClass()); | |||
assertEquals(UnknownPtg.class, fr.getParsedExpression().get(3).getClass()); | |||
assertEquals(RangePtg.class, fr.getParsedExpression().get(4).getClass()); | |||
assertEquals(UnknownPtg.class, fr.getParsedExpression().get(5).getClass()); | |||
assertEquals(AttrPtg.class, fr.getParsedExpression().get(6).getClass()); | |||
assertEquals(ReferencePtg.class, fr.getParsedExpression().get(7).getClass()); | |||
assertEquals(AttrPtg.class, fr.getParsedExpression().get(8).getClass()); | |||
assertEquals(ReferencePtg.class, fr.getParsedExpression().get(9).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()); | |||
List ptgs = fr.getParsedExpression(); | |||
assertEquals(9, ptgs.size()); | |||
assertEquals(IntPtg.class, ptgs.get(0).getClass()); | |||
assertEquals(AttrPtg.class, ptgs.get(1).getClass()); | |||
assertEquals(ReferencePtg.class, ptgs.get(2).getClass()); | |||
assertEquals(AttrPtg.class, ptgs.get(3).getClass()); | |||
assertEquals(ReferencePtg.class, ptgs.get(4).getClass()); | |||
assertEquals(AttrPtg.class, ptgs.get(5).getClass()); | |||
assertEquals(ReferencePtg.class, ptgs.get(6).getClass()); | |||
assertEquals(AttrPtg.class, ptgs.get(7).getClass()); | |||
assertEquals(FuncVarPtg.class, ptgs.get(8).getClass()); | |||
FuncVarPtg choose = (FuncVarPtg)fr.getParsedExpression().get(13); | |||
FuncVarPtg choose = (FuncVarPtg)ptgs.get(8); | |||
assertEquals("CHOOSE", choose.getName()); | |||
} | |||
@@ -17,18 +17,19 @@ | |||
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.TestCase; | |||
import org.apache.poi.ss.util.Region; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
import org.apache.poi.hssf.record.RecordFormatException; | |||
import org.apache.poi.util.TempFile; | |||
import java.io.*; | |||
import java.util.Iterator; | |||
/** | |||
* Testcases for bugs entered in bugzilla | |||
* the Test name contains the bugzilla bug id | |||
@@ -81,13 +82,7 @@ public final class TestBugs extends TestCase { | |||
HSSFRow r = s.createRow(0); | |||
HSSFCell c = r.createCell((short)0); | |||
c.setCellValue(10); | |||
try { | |||
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"); | |||
} | |||
} | |||
writeOutAndReadBack(wb); | |||
} | |||
/**Test writing 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); | |||
assertEquals(25, (int)c2.getNumericCellValue()); | |||
if (false) { // TODO (Apr-2008) This will blow up with IllegalStateException (stack underflow) | |||
// excel function "CHOOSE" probably needs some special handling in FormulaParser.toFormulaString() | |||
assertEquals("=CHOOSE(2,A2,A3,A4)", c2.getCellFormula()); | |||
try { | |||
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); | |||
assertTrue("no errors writing sample xls", true); | |||
} | |||
/** | |||
* Had a problem apparently, not sure what as it | |||
* works just fine... | |||
*/ | |||
public void test44891() throws Exception { | |||
HSSFWorkbook wb = openSample("44891.xls"); | |||
HSSFWorkbook wb = openSample("44891.xls"); | |||
assertTrue("no errors reading sample xls", true); | |||
writeOutAndReadBack(wb); | |||
assertTrue("no errors writing sample xls", true); | |||
@@ -906,7 +905,7 @@ public final class TestBugs extends TestCase { | |||
* Works fine with poi-3.1-beta1. | |||
*/ | |||
public void test44235() throws Exception { | |||
HSSFWorkbook wb = openSample("44235.xls"); | |||
HSSFWorkbook wb = openSample("44235.xls"); | |||
assertTrue("no errors reading sample xls", true); | |||
writeOutAndReadBack(wb); | |||
assertTrue("no errors writing sample xls", true); | |||
@@ -930,7 +929,7 @@ public final class TestBugs extends TestCase { | |||
} | |||
public void test36947() throws Exception { | |||
HSSFWorkbook wb = openSample("36947.xls"); | |||
HSSFWorkbook wb = openSample("36947.xls"); | |||
assertTrue("no errors reading sample xls", true); | |||
writeOutAndReadBack(wb); | |||
assertTrue("no errors writing sample xls", true); | |||
@@ -947,7 +946,7 @@ public final class TestBugs extends TestCase { | |||
} | |||
public void test39634() throws Exception { | |||
HSSFWorkbook wb = openSample("39634.xls"); | |||
HSSFWorkbook wb = openSample("39634.xls"); | |||
assertTrue("no errors reading sample xls", true); | |||
writeOutAndReadBack(wb); | |||
assertTrue("no errors writing sample xls", true); |
@@ -204,4 +204,64 @@ public final class TestHSSFRow extends TestCase { | |||
row.createCell((short) 255); | |||
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()); | |||
} | |||
} |
@@ -130,7 +130,7 @@ public final class TestPOIFSFileSystem extends TestCase { | |||
* The other is to fix the handling of the last block in | |||
* POIFS, since it seems to be slight wrong | |||
*/ | |||
public void DISABLEDtestShortLastBlock() throws Exception { | |||
public void testShortLastBlock() throws Exception { | |||
String[] files = new String[] { | |||
"ShortLastBlock.qwp", "ShortLastBlock.wps" | |||
}; |