diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java index 9b685ddca..a1a87d2cd 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java @@ -57,14 +57,23 @@ package org.apache.poi.hwpf; import java.io.InputStream; import java.io.FileInputStream; import java.io.IOException; +import java.io.OutputStream; +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.DocumentEntry; +import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.hwpf.model.hdftypes.*; +import org.apache.poi.hwpf.model.io.*; /** + * + * This class acts as the bucket that we throw all of the Word data structures + * into. + * * @author Ryan Ackley */ public class HWPFDocument @@ -76,11 +85,40 @@ public class HWPFDocument private FileInformationBlock _fib; /** main document stream buffer*/ - byte[] _mainStream; + private byte[] _mainStream; /** table stream buffer*/ - byte[] _tableStream; + private byte[] _tableStream; + /** Document wide Properties*/ + private DocumentProperties _dop; + + /** Contains text of the document wrapped in a obfuscated Wod data structure*/ + private ComplexFileTable _cft; + + /** Contains formatting properties for text*/ + private CHPBinTable _cbt; + + /** Contains formatting properties for paragraphs*/ + private PAPBinTable _pbt; + + /** Contains formatting properties for sections.*/ + private SectionTable _st; + + /** Holds styles for this document.*/ + private StyleSheet _ss; + + /** Holds fonts for this document.*/ + private FontTable _ft; + + + /** + * This constructor loads a Word document from an InputStream. + * + * @param istream The InputStream that contains the Word document. + * @throws IOException If there is an unexpected IOException from the passed + * in InputStream. + */ public HWPFDocument(InputStream istream) throws IOException { //do Ole stuff @@ -110,23 +148,148 @@ public class HWPFDocument // get the start of text in the main stream int fcMin = _fib.getFcMin(); - DocumentProperties dop = new DocumentProperties(_tableStream, _fib.getFcDop()); - ComplexFileTable cft = new ComplexFileTable(_mainStream, _tableStream, _fib.getFcClx(), fcMin); - CHPBinTable cbt = new CHPBinTable(_mainStream, _tableStream, _fib.getFcPlcfbteChpx(), _fib.getLcbPlcfbteChpx(), fcMin); - PAPBinTable pbt = new PAPBinTable(_mainStream, _tableStream, _fib.getFcPlcfbtePapx(), _fib.getLcbPlcfbtePapx(), fcMin); - SectionTable st = new SectionTable(_mainStream, _tableStream, _fib.getFcPlcfsed(), _fib.getLcbPlcfsed(), fcMin); - StyleSheet ss = new StyleSheet(_tableStream, _fib.getFcStshf()); + // load up our standard structures. + _dop = new DocumentProperties(_tableStream, _fib.getFcDop()); + _cft = new ComplexFileTable(_mainStream, _tableStream, _fib.getFcClx(), fcMin); + _cbt = new CHPBinTable(_mainStream, _tableStream, _fib.getFcPlcfbteChpx(), _fib.getLcbPlcfbteChpx(), fcMin); + _pbt = new PAPBinTable(_mainStream, _tableStream, _fib.getFcPlcfbtePapx(), _fib.getLcbPlcfbtePapx(), fcMin); + _st = new SectionTable(_mainStream, _tableStream, _fib.getFcPlcfsed(), _fib.getLcbPlcfsed(), fcMin); + _ss = new StyleSheet(_tableStream, _fib.getFcStshf()); + _ft = new FontTable(_tableStream, _fib.getFcSttbfffn(), _fib.getLcbSttbfffn()); int x = 0; } + /** + * Writes out the word file that is represented by an instance of this class. + * + * @param out The OutputStream to write to. + * @throws IOException If there is an unexpected IOException from the passed + * in OutputStream. + */ + public void write(OutputStream out) + throws IOException + { + // initialize our streams for writing. + HWPFFileSystem docSys = new HWPFFileSystem(); + HWPFOutputStream mainStream = docSys.getStream("WordDocument"); + HWPFOutputStream tableStream = docSys.getStream("1Table"); + int tableOffset = 0; + + // clear the offsets and sizes in our FileInformationBlock. + _fib.clearOffsetsSizes(); + + // determine the FileInformationBLock size + int fibSize = _fib.getSize(); + fibSize += POIFSConstants.BIG_BLOCK_SIZE - + (fibSize % POIFSConstants.BIG_BLOCK_SIZE); + + // preserve space for the FileInformationBlock because we will be writing + // it after we write everything else. + byte[] placeHolder = new byte[fibSize]; + mainStream.write(placeHolder); + int mainOffset = mainStream.getOffset(); + + // write out the StyleSheet. + _fib.setFcStshf(tableOffset); + _ss.writeTo(tableStream); + _fib.setLcbStshf(tableStream.getOffset() - tableOffset); + tableOffset = tableStream.getOffset(); + + // get fcMin and fcMac because we will be writing the actual text with the + // complex table. + int fcMin = mainOffset; + + // write out the Complex table, includes text. + _fib.setFcClx(tableOffset); + _cft.writeTo(docSys); + _fib.setLcbClx(tableStream.getOffset() - tableOffset); + tableOffset = tableStream.getOffset(); + int fcMac = mainStream.getOffset(); + + // write out the CHPBinTable. + _fib.setFcPlcfbteChpx(tableOffset); + _cbt.writeTo(docSys, fcMin); + _fib.setLcbPlcfbteChpx(tableStream.getOffset() - tableOffset); + tableOffset = tableStream.getOffset(); + + // write out the PAPBinTable. + _fib.setFcPlcfbtePapx(tableOffset); + _pbt.writeTo(docSys, fcMin); + _fib.setLcbPlcfbtePapx(tableStream.getOffset() - tableOffset); + tableOffset = tableStream.getOffset(); + + // write out the SectionTable. + _fib.setFcPlcfsed(tableOffset); + _st.writeTo(docSys, fcMin); + _fib.setLcbPlcfsed(tableStream.getOffset() - tableOffset); + tableOffset = tableStream.getOffset(); + + // write out the FontTable. + _fib.setFcSttbfffn(tableOffset); + _ft.writeTo(docSys); + _fib.setLcbSttbfffn(tableStream.getOffset() - tableOffset); + tableOffset = tableStream.getOffset(); + + // write out the DocumentProperties. + _fib.setFcDop(tableOffset); + byte[] buf = new byte[_dop.getSize()]; + _fib.setLcbDop(_dop.getSize()); + _dop.serialize(buf, 0); + tableStream.write(buf); + + // set some variables in the FileInformationBlock. + _fib.setFcMin(fcMin); + _fib.setFcMac(fcMac); + _fib.setCbMac(mainStream.getOffset()); + + // make sure that the table and doc stream use big blocks. + byte[] mainBuf = mainStream.toByteArray(); + if (mainBuf.length < 4096) + { + byte[] tempBuf = new byte[4096]; + System.arraycopy(mainBuf, 0, tempBuf, 0, mainBuf.length); + mainBuf = tempBuf; + } + byte[] tableBuf = tableStream.toByteArray(); + if (tableBuf.length < 4096) + { + byte[] tempBuf = new byte[4096]; + System.arraycopy(tableBuf, 0, tempBuf, 0, tableBuf.length); + tableBuf = tempBuf; + } + + // write out the FileInformationBlock. + _fib.serialize(mainBuf, 0); + + // spit out the Word document. + POIFSFileSystem pfs = new POIFSFileSystem(); + pfs.createDocument(new ByteArrayInputStream(mainBuf), "WordDocument"); + pfs.createDocument(new ByteArrayInputStream(tableBuf), "1Table"); + + pfs.writeFilesystem(out); + } + + /** + * Takes two arguments, 1) name of the Word file to read in 2) location to + * write it out at. + * @param args + */ public static void main(String[] args) { + try { HWPFDocument doc = new HWPFDocument(new FileInputStream(args[0])); + OutputStream out = new FileOutputStream(args[1]); + doc.write(out); + + out.flush(); + out.close(); + + } catch (Throwable t) { diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/CHPBinTable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/CHPBinTable.java index 0b74c23ce..c8614daf0 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/CHPBinTable.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/CHPBinTable.java @@ -142,7 +142,7 @@ public class CHPBinTable int end = endingFc; if (overflow != null) { - end = ((PropertyNode)overflow.get(0)).getStart(); + end = ((PropertyNode)overflow.get(0)).getStart() + fcMin; } byte[] intHolder = new byte[4]; diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/Ffn.java b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/Ffn.java index 92113a3ab..265ca7479 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/Ffn.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/Ffn.java @@ -56,93 +56,212 @@ package org.apache.poi.hwpf.model.hdftypes; import org.apache.poi.util.BitField; import org.apache.poi.util.LittleEndian; +import java.util.Arrays; - +/** + * FFN - Font Family Name. FFN is a data structure that stores the names of the Main + * Font and that of Alternate font as an array of characters. It has also a header + * that stores info about the whole structure and the fonts + * + * @author Praveen Mathew + */ public class Ffn { - private int field1_cbFfnM1; //total length of FFN - 1. - private byte field2; + private int field_1_cbFfnM1;//total length of FFN - 1. + private byte field_2; private static BitField _prq = new BitField(0x0003);// pitch request private static BitField _fTrueType = new BitField(0x0004);// when 1, font is a TrueType font private static BitField _ff = new BitField(0x0070); - private short field3_wWeight;// base weight of font - private byte field4_chs;// character set identifier - private byte field5_ixchSzAlt; // index into ffn.szFfn to the name of + private short field_3_wWeight;// base weight of font + private byte field_4_chs;// character set identifier + private byte field_5_ixchSzAlt; // index into ffn.szFfn to the name of // the alternate font - private byte [] panose = new byte[10]; //???? - private byte [] fontSig = new byte[24]; //???? + private byte [] field_6_panose = new byte[10];//???? + private byte [] field_7_fontSig = new byte[24];//???? // zero terminated string that records name of font, cuurently not // supporting Extended chars - private char [] xszFfn; + private char [] field_8_xszFfn; + + // extra facilitator members private int xszFfnLength; public Ffn(byte[] buf, int offset) { - field1_cbFfnM1 = LittleEndian.getUnsignedByte(buf, offset); + int offsetTmp = offset; + + field_1_cbFfnM1 = LittleEndian.getUnsignedByte(buf,offset); offset += LittleEndian.BYTE_SIZE; - field2 = buf[offset]; + field_2 = buf[offset]; offset += LittleEndian.BYTE_SIZE; - field3_wWeight = LittleEndian.getShort(buf, offset); + field_3_wWeight = LittleEndian.getShort(buf, offset); offset += LittleEndian.SHORT_SIZE; - field4_chs = buf[offset]; + field_4_chs = buf[offset]; offset += LittleEndian.BYTE_SIZE; - field5_ixchSzAlt = buf[offset]; + field_5_ixchSzAlt = buf[offset]; offset += LittleEndian.BYTE_SIZE; // read panose and fs so we can write them back out. - System.arraycopy(buf, offset, panose, 0, panose.length); - offset += panose.length; - System.arraycopy(buf, offset, fontSig, 0, fontSig.length); - offset += fontSig.length; + System.arraycopy(buf, offset, field_6_panose, 0, field_6_panose.length); + offset += field_6_panose.length; + System.arraycopy(buf, offset, field_7_fontSig, 0, field_7_fontSig.length); + offset += field_7_fontSig.length; - xszFfnLength = this.getSize() - offset; - xszFfn = new char[xszFfnLength]; + offsetTmp = offset - offsetTmp; + xszFfnLength = this.getSize() - offsetTmp; + field_8_xszFfn = new char[xszFfnLength]; for(int i = 0; i < xszFfnLength; i++) { - xszFfn[i] = (char)LittleEndian.getUnsignedByte(buf, offset); + field_8_xszFfn[i] = (char)LittleEndian.getUnsignedByte(buf, offset); offset += LittleEndian.BYTE_SIZE; } } - public int getField1_cbFfnM1() + public int getField_1_cbFfnM1() { - return field1_cbFfnM1; + return field_1_cbFfnM1; + } + + public byte getField_2() + { + return field_2; + } + + public short getField_3_wWeight() + { + return field_3_wWeight; + } + + public byte getField_4_chs() + { + return field_4_chs; + } + + public byte getField_5_ixchSzAlt() + { + return field_5_ixchSzAlt; + } + + public byte [] getField_6_panose() + { + return field_6_panose; + } + + public byte [] getField_7_fontSig() + { + return field_7_fontSig; + } + + public char [] getField_8_xszFfn() + { + return field_8_xszFfn; } public int getSize() { - return (field1_cbFfnM1 + 1); + return (field_1_cbFfnM1 + 1); } public char [] getMainFontName() { - char [] temp = new char[field5_ixchSzAlt]; - System.arraycopy(xszFfn,0,temp,0,temp.length); + char [] temp = new char[field_5_ixchSzAlt]; + System.arraycopy(field_8_xszFfn,0,temp,0,temp.length); return temp; } public char [] getAltFontName() { - char [] temp = new char[xszFfnLength - field5_ixchSzAlt]; - System.arraycopy(xszFfn, field5_ixchSzAlt, temp, 0, temp.length); + char [] temp = new char[xszFfnLength - field_5_ixchSzAlt]; + System.arraycopy(field_8_xszFfn, field_5_ixchSzAlt, temp, 0, temp.length); return temp; } - public void setField1_cbFfnM1(short field1_cbFfnM1) + public void setField_1_cbFfnM1(int field_1_cbFfnM1) { - this.field1_cbFfnM1 = field1_cbFfnM1; + this.field_1_cbFfnM1 = field_1_cbFfnM1; } - protected byte[] toByteArray() + // changed protected to public + public byte[] toByteArray() { - //return buf; - return null; + int offset = 0; + byte[] buf = new byte[this.getSize()]; + + buf[offset] = (byte)field_1_cbFfnM1; + offset += LittleEndian.BYTE_SIZE; + buf[offset] = field_2; + offset += LittleEndian.BYTE_SIZE; + LittleEndian.putShort(buf, offset, field_3_wWeight); + offset += LittleEndian.SHORT_SIZE; + buf[offset] = field_4_chs; + offset += LittleEndian.BYTE_SIZE; + buf[offset] = field_5_ixchSzAlt; + offset += LittleEndian.BYTE_SIZE; + + System.arraycopy(field_6_panose,0,buf, offset,field_6_panose.length); + offset += field_6_panose.length; + System.arraycopy(field_7_fontSig,0,buf, offset, field_7_fontSig.length); + offset += field_7_fontSig.length; + + for(int i = 0; i < field_8_xszFfn.length; i++) + { + buf[offset] = (byte)field_8_xszFfn[i]; + offset += LittleEndian.BYTE_SIZE; + } + + return buf; + + } + + public boolean equals(Object o) + { + boolean retVal = true; + + if (((Ffn)o).getField_1_cbFfnM1() == field_1_cbFfnM1) + { + if(((Ffn)o).getField_2() == field_2) + { + if(((Ffn)o).getField_3_wWeight() == field_3_wWeight) + { + if(((Ffn)o).getField_4_chs() == field_4_chs) + { + if(((Ffn)o).getField_5_ixchSzAlt() == field_5_ixchSzAlt) + { + if(Arrays.equals(((Ffn)o).getField_6_panose(),field_6_panose)) + { + if(Arrays.equals(((Ffn)o).getField_7_fontSig(),field_7_fontSig)) + { + if(!(Arrays.equals(((Ffn)o).getField_8_xszFfn(),field_8_xszFfn))) + retVal = false; + } + else + retVal = false; + } + else + retVal = false; + } + else + retVal = false; + } + else + retVal = false; + } + else + retVal = false; + } + else + retVal = false; + } + else + retVal = false; + + return retVal; } } + diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/FileInformationBlock.java b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/FileInformationBlock.java index b3ccca32b..ead8a4d36 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/FileInformationBlock.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/FileInformationBlock.java @@ -55,6 +55,9 @@ package org.apache.poi.hwpf.model.hdftypes; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; + import org.apache.poi.util.BitField; import org.apache.poi.util.LittleEndian; @@ -66,6 +69,7 @@ import org.apache.poi.hwpf.model.hdftypes.definitions.FIBAbstractType; * @author andy */ public class FileInformationBlock extends FIBAbstractType + implements Cloneable { /** Creates a new instance of FileInformationBlock */ @@ -74,5 +78,48 @@ public class FileInformationBlock extends FIBAbstractType fillFields(mainDocument, (short)0, (short)0); } + public void clearOffsetsSizes() + { + try + { + Field[] fields = FileInformationBlock.class.getSuperclass().getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + + for (int x = 0; x < fields.length; x++) + { + String name = fields[x].getName(); + int index = name.indexOf('_'); + if (index != -1) + { + int nextIndex = name.indexOf('_', index + 1); + if (nextIndex != -1) + { + // clear any field greater than field_53 + if (Integer.parseInt(name.substring(index + 1, nextIndex)) > 53) + { + fields[x].setInt(this, 0); + } + } + } + } + } + catch (IllegalAccessException iae) + { + iae.printStackTrace(); + } + } + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + e.printStackTrace(); + return null; + } + } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/FontTable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/FontTable.java index 7384be129..ad3dfdc5e 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/FontTable.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/FontTable.java @@ -54,67 +54,64 @@ package org.apache.poi.hwpf.model.hdftypes; -import org.apache.poi.util.BitField; +import java.io.IOException; +import org.apache.poi.hwpf.model.io.HWPFFileSystem; +import org.apache.poi.hwpf.model.io.HWPFOutputStream; import org.apache.poi.util.LittleEndian; - +/** + * FontTable or in MS terminology sttbfffn is a common data structure written in all + * Word files. The sttbfffn is an sttbf where each string is an FFN structure instead + * of pascal-style strings. An sttbf is a string Table stored in file. Thus sttbffn + * is like an Sttbf with an array of FFN structures that stores the font name strings + * + * @author Praveen Mathew + */ public class FontTable { - private short exntdChar; // strings are extended character if = 0xFFFF - private short stringCount; // how many strings are included in the string table - private short extraDataSz; // size in bytes of the extra data - - private int lcbSttbfffn; // count of bytes in sttbfffn - private boolean isExtndChar; + private short _stringCount;// how many strings are included in the string table + private short _extraDataSz;// size in bytes of the extra data + // added extra facilitator members + private int lcbSttbfffn;// count of bytes in sttbfffn + private int fcSttbfffn;// table stream offset for sttbfffn // FFN structure containing strings of font names - private Ffn [] fontNames = null; + private Ffn[] _fontNames = null; + public FontTable(byte[] buf, int offset, int lcbSttbfffn) { this.lcbSttbfffn = lcbSttbfffn; + this.fcSttbfffn = offset; - exntdChar = LittleEndian.getShort(buf, offset); + _stringCount = LittleEndian.getShort(buf, offset); offset += LittleEndian.SHORT_SIZE; - stringCount = LittleEndian.getShort(buf, offset); - offset += LittleEndian.SHORT_SIZE; - extraDataSz = LittleEndian.getShort(buf, offset); + _extraDataSz = LittleEndian.getShort(buf, offset); offset += LittleEndian.SHORT_SIZE; - if ((exntdChar & 0xFFFF) == 0xFFFF) + _fontNames = new Ffn[_stringCount]; //Ffn corresponds to a Pascal style String in STTBF. + + for(int i = 0;i<_stringCount; i++) { - isExtndChar = true; + _fontNames[i] = new Ffn(buf,offset); + offset += _fontNames[i].getSize(); } - else - { - isExtndChar = false; - } - - fontNames = new Ffn[stringCount]; //Ffn corresponds to a Pascal style String in STTBF. - - for(int i = 0;i= this.getSize()) - { - System.out.println("Total size of Sttbfn mismatched with calculated size"); - break; - } - - fontNames[i] = new Ffn(buf,offset); - offset += fontNames[i].getSize(); - } - } - - public boolean isExtndChar() - { - return isExtndChar; } public short getStringCount() { - return stringCount; + return _stringCount; + } + + public short getExtraDataSz() + { + return _extraDataSz; + } + + public Ffn[] getFontNames() + { + return _fontNames; } public int getSize() @@ -124,31 +121,76 @@ public class FontTable public char [] getMainFont(int chpFtc ) { - if(chpFtc >= stringCount) + if(chpFtc >= _stringCount) { System.out.println("Mismatch in chpFtc with stringCount"); return null; } - return fontNames[chpFtc].getMainFontName(); + return _fontNames[chpFtc].getMainFontName(); } public char [] getAltFont(int chpFtc ) { - if(chpFtc >= stringCount) + if(chpFtc >= _stringCount) { System.out.println("Mismatch in chpFtc with stringCount"); return null; } - return fontNames[chpFtc].getAltFontName(); + return _fontNames[chpFtc].getAltFontName(); } public void setStringCount(short stringCount) { - this.stringCount = stringCount; + this._stringCount = stringCount; } + public void writeTo(HWPFFileSystem sys) + throws IOException + { + HWPFOutputStream tableStream = sys.getStream("1Table"); + + byte[] buf = new byte[LittleEndian.SHORT_SIZE]; + LittleEndian.putShort(buf, _stringCount); + tableStream.write(buf); + LittleEndian.putShort(buf, _extraDataSz); + tableStream.write(buf); + + for(int i = 0; i < _fontNames.length; i++) + { + tableStream.write(_fontNames[i].toByteArray()); + } + + } + + public boolean equals(Object o) + { + boolean retVal = true; + + if(((FontTable)o).getStringCount() == _stringCount) + { + if(((FontTable)o).getExtraDataSz() == _extraDataSz) + { + Ffn[] fontNamesNew = ((FontTable)o).getFontNames(); + for(int i = 0;i<_stringCount; i++) + { + if(!(_fontNames[i].equals(fontNamesNew[i]))) + retVal = false; + } + } + else + retVal = false; + } + else + retVal = false; + + + return retVal; + } + + } + diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/PAPBinTable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/PAPBinTable.java index 18cb0d12e..92b4698f5 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/PAPBinTable.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/PAPBinTable.java @@ -141,7 +141,7 @@ public class PAPBinTable int end = endingFc; if (overflow != null) { - end = ((PropertyNode)overflow.get(0)).getStart(); + end = ((PropertyNode)overflow.get(0)).getStart() + fcMin; } byte[] intHolder = new byte[4]; diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/PAPFormattedDiskPage.java b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/PAPFormattedDiskPage.java index 4887c7dab..f9ca0e390 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/PAPFormattedDiskPage.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/PAPFormattedDiskPage.java @@ -58,6 +58,7 @@ import org.apache.poi.util.LittleEndian; import java.util.ArrayList; import java.util.List; +import java.util.Arrays; /** * Represents a PAP FKP. The style properties for paragraph and character runs @@ -152,6 +153,7 @@ public class PAPFormattedDiskPage extends FormattedDiskPage int grpprlOffset = 0; int bxOffset = 0; int fcOffset = 0; + byte[] lastGrpprl = new byte[0]; // total size is currently the size of one FC int totalSize = FC_SIZE; @@ -159,16 +161,28 @@ public class PAPFormattedDiskPage extends FormattedDiskPage int index = 0; for (; index < size; index++) { - int grpprlLength = ((PAPX)_papxList.get(index)).getGrpprl().length; + byte[] grpprl = ((PAPX)_papxList.get(index)).getGrpprl(); + int grpprlLength = grpprl.length; // check to see if we have enough room for an FC, a BX, and the grpprl // and the 1 byte size of the grpprl. - totalSize += (FC_SIZE + BX_SIZE + grpprlLength + 1); + int addition = 0; + if (!Arrays.equals(grpprl, lastGrpprl)) + { + addition = (FC_SIZE + BX_SIZE + grpprlLength + 1); + } + else + { + addition = (FC_SIZE + BX_SIZE); + } + + totalSize += addition; + // if size is uneven we will have to add one so the first grpprl falls // on a word boundary if (totalSize > 511 + (index % 2)) { - totalSize -= (FC_SIZE + BX_SIZE + grpprlLength); + totalSize -= addition; break; } @@ -181,6 +195,7 @@ public class PAPFormattedDiskPage extends FormattedDiskPage { totalSize += 2; } + lastGrpprl = grpprl; } // see if we couldn't fit some @@ -197,14 +212,19 @@ public class PAPFormattedDiskPage extends FormattedDiskPage grpprlOffset = 511; PAPX papx = null; + lastGrpprl = new byte[0]; for (int x = 0; x < index; x++) { papx = (PAPX)_papxList.get(x); byte[] phe = papx.getParagraphHeight().toByteArray(); byte[] grpprl = papx.getGrpprl(); - grpprlOffset -= (grpprl.length + (2 - grpprl.length % 2)); - grpprlOffset -= (grpprlOffset % 2); + boolean same = Arrays.equals(lastGrpprl, grpprl); + if (!same) + { + grpprlOffset -= (grpprl.length + (2 - grpprl.length % 2)); + grpprlOffset -= (grpprlOffset % 2); + } LittleEndian.putInt(buf, fcOffset, papx.getStart() + fcMin); buf[bxOffset] = (byte)(grpprlOffset/2); System.arraycopy(phe, 0, buf, bxOffset + 1, phe.length); @@ -212,20 +232,25 @@ public class PAPFormattedDiskPage extends FormattedDiskPage // refer to the section on PAPX in the spec. Places a size on the front // of the PAPX. Has to do with how the grpprl stays on word // boundaries. - int copyOffset = grpprlOffset; - if ((grpprl.length % 2) > 0) + if (!same) { - buf[copyOffset++] = (byte)((grpprl.length + 1)/2); + int copyOffset = grpprlOffset; + if ( (grpprl.length % 2) > 0) + { + buf[copyOffset++] = (byte) ( (grpprl.length + 1) / 2); + } + else + { + buf[++copyOffset] = (byte) ( (grpprl.length) / 2); + copyOffset++; + } + System.arraycopy(grpprl, 0, buf, copyOffset, grpprl.length); + lastGrpprl = grpprl; } - else - { - buf[++copyOffset] = (byte)((grpprl.length)/2); - copyOffset++; - } - System.arraycopy(grpprl, 0, buf, copyOffset, grpprl.length); bxOffset += BX_SIZE; fcOffset += FC_SIZE; + } // put the last papx's end in LittleEndian.putInt(buf, fcOffset, papx.getEnd() + fcMin); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/StyleDescription.java b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/StyleDescription.java index 483dd2ab6..a55c33a65 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/StyleDescription.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/StyleDescription.java @@ -146,38 +146,42 @@ public class StyleDescription implements HDFType // the spec only refers to two possible upxs but it mentions // that more may be added in the future - int add = 0; + int varOffset = grupxStart; int numUPX = _numUPX.getValue(_infoShort3); for(int x = 0; x < numUPX; x++) { - int upxSize = LittleEndian.getShort(std, grupxStart + add); + int upxSize = LittleEndian.getShort(std, varOffset); + varOffset += LittleEndian.SHORT_SIZE; if(_styleTypeCode.getValue(_infoShort2) == PARAGRAPH_STYLE) { if(x == 0) { - _istd = LittleEndian.getShort(std, grupxStart + add + 2); + _istd = LittleEndian.getShort(std, varOffset); + varOffset += LittleEndian.SHORT_SIZE; int grrprlSize = upxSize - 2; - _papx = new byte[upxSize]; - System.arraycopy(std, grupxStart + add + 4, _papx, 0, grrprlSize); + _papx = new byte[grrprlSize]; + System.arraycopy(std, varOffset, _papx, 0, grrprlSize); + varOffset += grrprlSize; } else if(x == 1) { _chpx = new byte[upxSize]; - System.arraycopy(std, grupxStart + add + 2, _chpx, 0, upxSize); + System.arraycopy(std, varOffset, _chpx, 0, upxSize); + varOffset += upxSize; } } else if(_styleTypeCode.getValue(_infoShort2) == CHARACTER_STYLE && x == 0) { _chpx = new byte[upxSize]; - System.arraycopy(std, grupxStart + add + 2, _chpx, 0, upxSize); + System.arraycopy(std, varOffset, _chpx, 0, upxSize); } // the upx will always start on a word boundary. if(upxSize % 2 == 1) { - ++upxSize; + ++varOffset; } - add += 2 + upxSize; + } @@ -259,7 +263,7 @@ public class StyleDescription implements HDFType //only worry about papx and chpx for upxs if(_styleTypeCode.getValue(_infoShort2) == PARAGRAPH_STYLE) { - LittleEndian.putShort(buf, offset, (short)_papx.length); + LittleEndian.putShort(buf, offset, (short)(_papx.length + 2)); offset += LittleEndian.SHORT_SIZE; LittleEndian.putShort(buf, offset, (short)_istd); offset += LittleEndian.SHORT_SIZE; diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/StyleSheet.java b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/StyleSheet.java index a4e7f8b44..a0c7cae13 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/StyleSheet.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/hdftypes/StyleSheet.java @@ -161,7 +161,7 @@ public class StyleSheet implements HDFType throws IOException { int offset = 0; - // add two bytes so we can prepend the styelsheet w/ its size + // add two bytes so we can prepend the stylesheet w/ its size byte[] buf = new byte[_stshiLength + 2]; LittleEndian.putShort(buf, offset, (short)_stshiLength); offset += LittleEndian.SHORT_SIZE; diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/model/hdftypes/TestDocumentProperties.java b/src/scratchpad/testcases/org/apache/poi/hwpf/model/hdftypes/TestDocumentProperties.java new file mode 100644 index 000000000..5e08844ce --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/model/hdftypes/TestDocumentProperties.java @@ -0,0 +1,75 @@ +package org.apache.poi.hwpf.model.hdftypes; + +import junit.framework.*; +import org.apache.poi.hwpf.*; + +import java.lang.reflect.*; +import java.util.Arrays; + +public class TestDocumentProperties + extends TestCase +{ + private DocumentProperties _documentProperties = null; + private HWPFDocFixture _hWPFDocFixture; + + public TestDocumentProperties(String name) + { + super(name); + } + + + public void testReadWrite() + throws Exception + { + int size = _documentProperties.getSize(); + byte[] buf = new byte[size]; + + _documentProperties.serialize(buf, 0); + + DocumentProperties newDocProperties = + new DocumentProperties(buf, 0); + + Field[] fields = DocumentProperties.class.getSuperclass().getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + + for (int x = 0; x < fields.length; x++) + { + if (!fields[x].getType().isArray()) + { + assertEquals(fields[x].get(_documentProperties), + fields[x].get(newDocProperties)); + } + else + { + byte[] buf1 = (byte[])fields[x].get(_documentProperties); + byte[] buf2 = (byte[])fields[x].get(newDocProperties); + Arrays.equals(buf1, buf2); + } + } + + } + + protected void setUp() + throws Exception + { + super.setUp(); + /**@todo verify the constructors*/ + + _hWPFDocFixture = new HWPFDocFixture(this); + + _hWPFDocFixture.setUp(); + + _documentProperties = new DocumentProperties(_hWPFDocFixture._tableStream, _hWPFDocFixture._fib.getFcDop()); + } + + protected void tearDown() + throws Exception + { + _documentProperties = null; + _hWPFDocFixture.tearDown(); + + _hWPFDocFixture = null; + super.tearDown(); + } + +} diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/model/hdftypes/TestFontTable.java b/src/scratchpad/testcases/org/apache/poi/hwpf/model/hdftypes/TestFontTable.java new file mode 100644 index 000000000..e17e07808 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/model/hdftypes/TestFontTable.java @@ -0,0 +1,121 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" and + * "Apache POI" must not be used to endorse or promote products + * derived from this software without prior written permission. For + * written permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * "Apache POI", nor may "Apache" appear in their name, without + * prior written permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.poi.hwpf.model.hdftypes; + +import junit.framework.*; +import org.apache.poi.hwpf.*; +import org.apache.poi.hwpf.model.io.*; + +import java.io.*; +import java.util.*; + +public class TestFontTable + extends TestCase +{ + private FontTable _fontTable = null; + private HWPFDocFixture _hWPFDocFixture; + + public TestFontTable(String name) + { + super(name); + } + + public void testReadWrite() + throws Exception + { + FileInformationBlock fib = _hWPFDocFixture._fib; + byte[] tableStream = _hWPFDocFixture._tableStream; + + int fcSttbfffn = fib.getFcSttbfffn(); + int lcbSttbfffn = fib.getLcbSttbfffn(); + + _fontTable = new FontTable(tableStream, fcSttbfffn, lcbSttbfffn); + + HWPFFileSystem fileSys = new HWPFFileSystem(); + + _fontTable.writeTo(fileSys); + HWPFOutputStream tableOut = fileSys.getStream("1Table"); + + + byte[] newTableStream = tableOut.toByteArray(); + + + FontTable newFontTable = new FontTable(newTableStream, 0, newTableStream.length); + + assertTrue(_fontTable.equals(newFontTable)); + + } + + protected void setUp() + throws Exception + { + super.setUp(); + /**@todo verify the constructors*/ + _hWPFDocFixture = new HWPFDocFixture(this); + + _hWPFDocFixture.setUp(); + } + + protected void tearDown() + throws Exception + { + _hWPFDocFixture.tearDown(); + + _hWPFDocFixture = null; + super.tearDown(); + } + +} +