improved handling of StyleTextPropAtom bit masks, added more read-write roundtrip tests

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@691180 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2008-09-02 09:59:53 +00:00
parent d3f7cc38ef
commit 7c1b5604d9
4 changed files with 677 additions and 681 deletions

View File

@ -35,19 +35,19 @@ public class CharFlagsTextProp extends BitMaskTextProp {
public static String NAME = "char_flags"; public static String NAME = "char_flags";
public CharFlagsTextProp() { public CharFlagsTextProp() {
super(2,0xffff, NAME, new String[] { super(2,0xffff, NAME, new String[] {
"bold", // 0x0001 "bold", // 0x0001 A bit that specifies whether the characters are bold.
"italic", // 0x0002 "italic", // 0x0002 A bit that specifies whether the characters are italicized.
"underline", // 0x0004 "underline", // 0x0004 A bit that specifies whether the characters are underlined.
"char_unknown_1",// 0x0008 "char_unknown_1", // 0x0008 Undefined and MUST be ignored.
"shadow", // 0x0010 "shadow", // 0x0010 A bit that specifies whether the characters have a shadow effect.
"char_unknown_2",// 0x0020 "fehint", // 0x0020 A bit that specifies whether characters originated from double-byte input.
"char_unknown_3",// 0x0040 "char_unknown_2", // 0x0040 Undefined and MUST be ignored.
"char_unknown_4",// 0x0080 "kumi", // 0x0080 A bit that specifies whether Kumimoji are used for vertical text.
"strikethrough", // 0x0100 "strikethrough", // 0x0100 Undefined and MUST be ignored.
"relief", // 0x0200 "emboss", // 0x0200 A bit that specifies whether the characters are embossed.
"reset_numbering", // 0x0400 "char_unknown_3", // 0x0400 Undefined and MUST be ignored.
"enable_numbering_1", // 0x0800 "char_unknown_4", // 0x0800 Undefined and MUST be ignored.
"enable_numbering_2", // 0x1000 "char_unknown_5", // 0x1000 Undefined and MUST be ignored.
} }
); );
} }

View File

@ -34,6 +34,7 @@ public class TextPropCollection {
private int charactersCovered; private int charactersCovered;
private short reservedField; private short reservedField;
private LinkedList textPropList; private LinkedList textPropList;
private int maskSpecial = 0;
/** Fetch the number of characters this styling applies to */ /** Fetch the number of characters this styling applies to */
public int getCharactersCovered() { return charactersCovered; } public int getCharactersCovered() { return charactersCovered; }
@ -94,20 +95,27 @@ public class TextPropCollection {
// If we do, decode that, save it, and shuffle on // If we do, decode that, save it, and shuffle on
for(int i=0; i<potentialProperties.length; i++) { for(int i=0; i<potentialProperties.length; i++) {
// Check there's still data left to read // Check there's still data left to read
if(dataOffset+bytesPassed >= data.length) {
// Out of data, can't be any more properties to go
return bytesPassed;
}
// Check if this property is found in the mask // Check if this property is found in the mask
if((containsField & potentialProperties[i].getMask()) != 0) { if((containsField & potentialProperties[i].getMask()) != 0) {
if(dataOffset+bytesPassed >= data.length) {
// Out of data, can't be any more properties to go
// remember the mask and return
maskSpecial |= potentialProperties[i].getMask();
return bytesPassed;
}
// Bingo, data contains this property // Bingo, data contains this property
TextProp prop = (TextProp)potentialProperties[i].clone(); TextProp prop = (TextProp)potentialProperties[i].clone();
int val = 0; int val = 0;
if(prop.getSize() == 2) { if(prop.getSize() == 2) {
val = LittleEndian.getShort(data,dataOffset+bytesPassed); val = LittleEndian.getShort(data,dataOffset+bytesPassed);
} else { } else if(prop.getSize() == 4){
val = LittleEndian.getInt(data,dataOffset+bytesPassed); val = LittleEndian.getInt(data,dataOffset+bytesPassed);
} else if (prop.getSize() == 0){
//remember "special" bits.
maskSpecial |= potentialProperties[i].getMask();
continue;
} }
prop.setValue(val); prop.setValue(val);
bytesPassed += prop.getSize(); bytesPassed += prop.getSize();
@ -161,14 +169,17 @@ public class TextPropCollection {
} }
// Then the mask field // Then the mask field
int mask = 0; int mask = maskSpecial;
for(int i=0; i<textPropList.size(); i++) { for(int i=0; i<textPropList.size(); i++) {
TextProp textProp = (TextProp)textPropList.get(i); TextProp textProp = (TextProp)textPropList.get(i);
//sometimes header indicates that the bitmask is present but its value is 0 //sometimes header indicates that the bitmask is present but its value is 0
if (textProp instanceof BitMaskTextProp)
mask |= (textProp.getWriteMask() == 0 ? 1 : textProp.getWriteMask()); if (textProp instanceof BitMaskTextProp) {
else if(mask == 0) mask |= textProp.getWriteMask();
}
else {
mask |= textProp.getWriteMask(); mask |= textProp.getWriteMask();
}
} }
StyleTextPropAtom.writeLittleEndian(mask,o); StyleTextPropAtom.writeLittleEndian(mask,o);
@ -178,7 +189,7 @@ public class TextPropCollection {
int val = textProp.getValue(); int val = textProp.getValue();
if(textProp.getSize() == 2) { if(textProp.getSize() == 2) {
StyleTextPropAtom.writeLittleEndian((short)val,o); StyleTextPropAtom.writeLittleEndian((short)val,o);
} else { } else if(textProp.getSize() == 4){
StyleTextPropAtom.writeLittleEndian(val,o); StyleTextPropAtom.writeLittleEndian(val,o);
} }
} }

View File

@ -124,6 +124,10 @@ public class StyleTextPropAtom extends RecordAtom
/** All the different kinds of paragraph properties we might handle */ /** All the different kinds of paragraph properties we might handle */
public static TextProp[] paragraphTextPropTypes = new TextProp[] { public static TextProp[] paragraphTextPropTypes = new TextProp[] {
new TextProp(0, 0x1, "hasBullet"),
new TextProp(0, 0x2, "hasBulletFont"),
new TextProp(0, 0x4, "hasBulletColor"),
new TextProp(0, 0x8, "hasBulletSize"),
new ParagraphFlagsTextProp(), new ParagraphFlagsTextProp(),
new TextProp(2, 0x80, "bullet.char"), new TextProp(2, 0x80, "bullet.char"),
new TextProp(2, 0x10, "bullet.font"), new TextProp(2, 0x10, "bullet.font"),
@ -131,35 +135,38 @@ public class StyleTextPropAtom extends RecordAtom
new TextProp(4, 0x20, "bullet.color"), new TextProp(4, 0x20, "bullet.color"),
new AlignmentTextProp(), new AlignmentTextProp(),
new TextProp(2, 0x100, "text.offset"), new TextProp(2, 0x100, "text.offset"),
new TextProp(2, 0x200, "para_unknown_2"),
new TextProp(2, 0x400, "bullet.offset"), new TextProp(2, 0x400, "bullet.offset"),
new TextProp(2, 0x1000, "linespacing"), new TextProp(2, 0x1000, "linespacing"),
new TextProp(2, 0x2000, "spacebefore"), new TextProp(2, 0x2000, "spacebefore"),
new TextProp(2, 0x4000, "spaceafter"), new TextProp(2, 0x4000, "spaceafter"),
new TextProp(2, 0x8000, "para_unknown_4"), new TextProp(2, 0x8000, "defaultTabSize"),
new TextProp(2, 0x10000, "para_unknown_5"), new TextProp(2, 0x100000, "tabStops"),
new TextProp(2, 0xA0000, "para_unknown_6"), new TextProp(2, 0x10000, "fontAlign"),
new TextProp(2, 0x200000, "para_unknown_7") new TextProp(2, 0xA0000, "wrapFlags"),
new TextProp(2, 0x200000, "textDirection")
}; };
/** All the different kinds of character properties we might handle */ /** All the different kinds of character properties we might handle */
public static TextProp[] characterTextPropTypes = new TextProp[] { public static TextProp[] characterTextPropTypes = new TextProp[] {
new TextProp(0, 0x1, "bold"),
new TextProp(0, 0x2, "italic"),
new TextProp(0, 0x4, "underline"),
new TextProp(0, 0x8, "unused1"),
new TextProp(0, 0x10, "shadow"),
new TextProp(0, 0x20, "fehint"),
new TextProp(0, 0x40, "unused2"),
new TextProp(0, 0x80, "kumi"),
new TextProp(0, 0x100, "unused3"),
new TextProp(0, 0x200, "emboss"),
new CharFlagsTextProp(), new CharFlagsTextProp(),
new TextProp(2, 0x10000, "font.index"), new TextProp(2, 0x10000, "font.index"),
new TextProp(2, 0x200000, "asian_or_complex"), new TextProp(0, 0x100000, "pp10ext"),
new TextProp(2, 0x400000, "char_unknown_2"), new TextProp(2, 0x200000, "asian.font.index"),
new TextProp(2, 0x800000, "symbol"), new TextProp(2, 0x400000, "ansi.font.index"),
new TextProp(2, 0x800000, "symbol.font.index"),
new TextProp(2, 0x20000, "font.size"), new TextProp(2, 0x20000, "font.size"),
new TextProp(4, 0x40000, "font.color"), new TextProp(4, 0x40000, "font.color"),
new TextProp(2, 0x80000, "superscript"), new TextProp(2, 0x80000, "superscript"),
new TextProp(2, 0x100000, "char_unknown_1"),
new TextProp(2, 0x1000000, "char_unknown_3"),
new TextProp(2, 0x2000000, "char_unknown_4"),
new TextProp(2, 0x4000000, "char_unknown_5"),
new TextProp(2, 0x8000000, "char_unknown_6"),
new TextProp(2, 0x10000000, "char_unknown_7"),
new TextProp(2, 0x20000000, "char_unknown_8"),
new TextProp(2, 0x40000000, "char_unknown_9"),
new TextProp(2, 0x80000000, "char_unknown_10"),
}; };
/* *************** record code follows ********************** */ /* *************** record code follows ********************** */
@ -269,8 +276,7 @@ public class StyleTextPropAtom extends RecordAtom
textHandled += textLen; textHandled += textLen;
pos += 4; pos += 4;
// Fetch the 2 byte value that is safe to ignore as 0 short indent = LittleEndian.getShort(rawContents,pos);
short paraIgn = LittleEndian.getShort(rawContents,pos);
pos += 2; pos += 2;
// Grab the 4 byte value that tells us what properties follow // Grab the 4 byte value that tells us what properties follow
@ -278,7 +284,7 @@ public class StyleTextPropAtom extends RecordAtom
pos += 4; pos += 4;
// Now make sense of those properties // Now make sense of those properties
TextPropCollection thisCollection = new TextPropCollection(textLen, paraIgn); TextPropCollection thisCollection = new TextPropCollection(textLen, indent);
int plSize = thisCollection.buildTextPropList( int plSize = thisCollection.buildTextPropList(
paraFlags, paragraphTextPropTypes, rawContents, pos); paraFlags, paragraphTextPropTypes, rawContents, pos);
pos += plSize; pos += plSize;

View File

@ -31,6 +31,7 @@ import org.apache.poi.util.HexDump;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Arrays;
/** /**
* Tests that StyleTextPropAtom works properly * Tests that StyleTextPropAtom works properly
@ -642,60 +643,6 @@ public class TestStyleTextPropAtom extends TestCase {
stpa.writeOut(baos); stpa.writeOut(baos);
byte[] b = baos.toByteArray(); byte[] b = baos.toByteArray();
assertEquals(data_b.length, b.length);
for(int i=0; i<data_b.length; i++) {
System.out.println(i + "\t" + b[i] + "\t" + data_b[i] + "\t" + Integer.toHexString(b[i]) );
assertEquals(data_b[i],b[i]);
}
}
public void testWriteA() throws Exception {
StyleTextPropAtom stpa = new StyleTextPropAtom(data_a,0,data_a.length);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
stpa.writeOut(baos);
byte[] b = baos.toByteArray();
assertEquals(data_a.length, b.length);
for(int i=0; i<data_a.length; i++) {
assertEquals(data_a[i],b[i]);
}
}
public void testLoadWriteA() throws Exception {
StyleTextPropAtom stpa = new StyleTextPropAtom(data_a,0,data_a.length);
stpa.setParentTextSize(data_a_text_len);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
stpa.writeOut(baos);
byte[] b = baos.toByteArray();
assertEquals(data_a.length, b.length);
for(int i=0; i<data_a.length; i++) {
assertEquals(data_a[i],b[i]);
}
}
public void testWriteB() throws Exception {
StyleTextPropAtom stpb = new StyleTextPropAtom(data_b,0,data_b.length);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
stpb.writeOut(baos);
byte[] b = baos.toByteArray();
assertEquals(data_b.length, b.length);
for(int i=0; i<data_b.length; i++) {
assertEquals(data_b[i],b[i]);
}
}
public void testLoadWriteB() throws Exception {
StyleTextPropAtom stpb = new StyleTextPropAtom(data_b,0,data_b.length);
stpb.setParentTextSize(data_b_text_len);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
stpb.writeOut(baos);
byte[] b = baos.toByteArray();
assertEquals(data_b.length, b.length); assertEquals(data_b.length, b.length);
for(int i=0; i<data_b.length; i++) { for(int i=0; i<data_b.length; i++) {
//System.out.println(i + "\t" + b[i] + "\t" + data_b[i] + "\t" + Integer.toHexString(b[i]) ); //System.out.println(i + "\t" + b[i] + "\t" + data_b[i] + "\t" + Integer.toHexString(b[i]) );
@ -703,6 +650,47 @@ public class TestStyleTextPropAtom extends TestCase {
} }
} }
public void testWriteA() throws Exception {
doReadWrite(data_a, -1);
}
public void testLoadWriteA() throws Exception {
doReadWrite(data_b, data_b_text_len);
}
public void testWriteB() throws Exception {
doReadWrite(data_b, -1);
}
public void testLoadWriteB() throws Exception {
doReadWrite(data_b, data_b_text_len);
}
public void testLoadWriteC() throws Exception {
doReadWrite(data_c, data_c_text_len);
}
public void testLoadWriteD() throws Exception {
doReadWrite(data_d, data_d_text_len);
}
protected void doReadWrite(byte[] data, int textlen) throws Exception {
StyleTextPropAtom stpb = new StyleTextPropAtom(data, 0,data.length);
if(textlen != -1) stpb.setParentTextSize(textlen);
ByteArrayOutputStream out = new ByteArrayOutputStream();
stpb.writeOut(out);
byte[] bytes = out.toByteArray();
assertEquals(data.length, bytes.length);
try {
assertTrue(Arrays.equals(data, bytes));
} catch (Throwable e){
//print hex dump if failed
assertEquals(HexDump.toHex(data), HexDump.toHex(bytes));
}
}
public void testNotEnoughDataProp() throws Exception { public void testNotEnoughDataProp() throws Exception {
// We don't have enough data in the record to cover // We don't have enough data in the record to cover
@ -732,8 +720,8 @@ public class TestStyleTextPropAtom extends TestCase {
assertEquals(1, chprops.findByName("char_flags").getValue()); assertEquals(1, chprops.findByName("char_flags").getValue());
assertEquals(1, chprops.findByName("font.index").getValue()); assertEquals(1, chprops.findByName("font.index").getValue());
assertEquals(20, chprops.findByName("font.size").getValue()); assertEquals(20, chprops.findByName("font.size").getValue());
assertEquals(0, chprops.findByName("asian_or_complex").getValue()); assertEquals(0, chprops.findByName("asian.font.index").getValue());
assertEquals(1, chprops.findByName("char_unknown_2").getValue()); assertEquals(1, chprops.findByName("ansi.font.index").getValue());
} }
/** /**
@ -747,16 +735,7 @@ public class TestStyleTextPropAtom extends TestCase {
0x00 , 0x00 , 0x13 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x63 , 0x00 , 0x00 , 0x00 , 0x13 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x63 , 0x00 ,
0x00 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x0F , 0x00 0x00 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x0F , 0x00
}; };
StyleTextPropAtom stpa = new StyleTextPropAtom(data,0,data.length); doReadWrite(data, length);
stpa.setParentTextSize(length);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
stpa.writeOut(baos);
byte[] b = baos.toByteArray();
assertEquals(data.length, b.length);
for(int i=0; i<data.length; i++) {
assertEquals(data[i],b[i]);
}
} }
} }