From 3f5640f8f215e0f0d19c440d965cd57ceeaaf8d0 Mon Sep 17 00:00:00 2001 From: Sergey Vladimirov Date: Sun, 23 Sep 2012 13:29:31 +0000 Subject: [PATCH] fix LVL#xst definition and processing git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1389053 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hwpf/model/ListLevel.java | 96 ++++--------- .../src/org/apache/poi/hwpf/model/Xst.java | 131 ++++++++++++++++++ .../apache/poi/hwpf/usermodel/Paragraph.java | 2 +- 3 files changed, 162 insertions(+), 67 deletions(-) create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/Xst.java diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/ListLevel.java b/src/scratchpad/src/org/apache/poi/hwpf/model/ListLevel.java index e33ccb2d9..7bdd1fcd9 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/ListLevel.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/ListLevel.java @@ -20,7 +20,6 @@ package org.apache.poi.hwpf.model; import java.util.Arrays; import org.apache.poi.util.Internal; -import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -44,7 +43,22 @@ public final class ListLevel private byte[] _grpprlChpx; private byte[] _grpprlPapx; private LVLF _lvlf; - private char[] _xst = {}; + /** + * An Xst that specifies the number text that begins each paragraph in this + * level. This can contain placeholders for level numbers that are inherited + * from the other paragraphs in the list. Any element in the rgtchar field + * of this Xst can be a placeholder. Each placeholder is an unsigned 2-byte + * integer that specifies the zero-based level that the placeholder is for. + * + * Each placeholder MUST have a value that is less than or equal to the + * zero-based level of the list that this LVL represents. The indexes of the + * placeholders are specified by lvlf.rgbxchNums. Placeholders that + * correspond to levels that do not have a number sequence (see lvlf.nfc) + * MUST be ignored. If this level uses bullets (see lvlf.nfc), the cch field + * of this Xst MUST be equal to 0x0001, and this MUST NOT contain any + * placeholders. + */ + private Xst _xst = new Xst(); ListLevel() { @@ -67,11 +81,11 @@ public final class ListLevel if ( numbered ) { _lvlf.getRgbxchNums()[0] = 1; - _xst = new char[] { (char) level, '.' }; + _xst = new Xst("" + (char) level + "."); } else { - _xst = new char[] { '\u2022' }; + _xst = new Xst("\u2022"); } } @@ -84,7 +98,7 @@ public final class ListLevel _lvlf.setJc( (byte) alignment ); _grpprlChpx = numberProperties; _grpprlPapx = entryProperties; - _xst = numberText.toCharArray(); + _xst = new Xst(numberText); } public boolean equals( Object obj ) @@ -96,7 +110,7 @@ public final class ListLevel return lvl._lvlf.equals( this._lvlf ) && Arrays.equals( lvl._grpprlChpx, _grpprlChpx ) && Arrays.equals( lvl._grpprlPapx, _grpprlPapx ) - && Arrays.equals( lvl._xst, _xst ); + && lvl._xst.equals( this._xst ); } /** @@ -132,17 +146,13 @@ public final class ListLevel public String getNumberText() { - if ( _xst.length < 2 ) - return null; - - return new String( _xst, 0, _xst.length - 1 ); + return _xst.getAsJavaString(); } public int getSizeInBytes() { return LVLF.getSize() + _lvlf.getCbGrpprlChpx() - + _lvlf.getCbGrpprlPapx() + LittleEndian.SHORT_SIZE - + _xst.length * LittleEndian.SHORT_SIZE; + + _lvlf.getCbGrpprlPapx() + _xst.getSize(); } public int getStartAt() @@ -173,6 +183,9 @@ public final class ListLevel System.arraycopy( data, offset, _grpprlChpx, 0, _lvlf.getCbGrpprlChpx() ); offset += _lvlf.getCbGrpprlChpx(); + _xst = new Xst( data, offset ); + offset += _xst.getSize(); + /* * "If this level uses bullets (see lvlf.nfc), the cch field of this Xst * MUST be equal to 0x0001, and this MUST NOT contain any placeholders." @@ -181,45 +194,12 @@ public final class ListLevel */ if ( _lvlf.getNfc() == 0x17 ) { - int cch = LittleEndian.getUShort( data, offset ); - offset += LittleEndian.SHORT_SIZE; - - if ( cch != 1 ) + if ( _xst.getCch() != 1 ) { logger.log( POILogger.WARN, "LVL at offset ", Integer.valueOf( startOffset ), " has nfc == 0x17 (bullets), but cch != 1 (", - Integer.valueOf( cch ), ")" ); - } - - _xst = new char[cch]; - for ( int x = 0; x < cch; x++ ) - { - _xst[x] = (char) LittleEndian.getShort( data, offset ); - offset += LittleEndian.SHORT_SIZE; - } - } - else - { - int cch = LittleEndian.getUShort( data, offset ); - offset += LittleEndian.SHORT_SIZE; - - if ( cch > 0 ) - { - _xst = new char[cch]; - for ( int x = 0; x < cch; x++ ) - { - _xst[x] = (char) LittleEndian.getShort( data, offset ); - offset += LittleEndian.SHORT_SIZE; - } - } - else - { - logger.log( POILogger.WARN, "LVL.xst.cch <= 0: ", - Integer.valueOf( cch ) ); - /* sometimes numberTextLength<0 */ - /* by derjohng */ - _xst = new char[] {}; + Integer.valueOf( _xst.getCch() ), ")" ); } } @@ -271,24 +251,8 @@ public final class ListLevel System.arraycopy( _grpprlChpx, 0, buf, offset, _grpprlChpx.length ); offset += _grpprlChpx.length; - if ( _lvlf.getNfc() == 0x17 ) - { - LittleEndian.putUShort( buf, offset, 1 ); - offset += LittleEndian.SHORT_SIZE; - - LittleEndian.putUShort( buf, offset, _xst[0] ); - offset += LittleEndian.SHORT_SIZE; - } - else - { - LittleEndian.putUShort( buf, offset, _xst.length ); - offset += LittleEndian.SHORT_SIZE; - for ( char c : _xst ) - { - LittleEndian.putUShort( buf, offset, c ); - offset += LittleEndian.SHORT_SIZE; - } - } + _xst.serialize( buf, offset ); + offset += _xst.getSize(); return buf; } @@ -300,6 +264,6 @@ public final class ListLevel + "\n" + ( "PAPX's grpprl: " + Arrays.toString( _grpprlPapx ) + "\n" ) + ( "CHPX's grpprl: " + Arrays.toString( _grpprlChpx ) + "\n" ) - + ( "xst: " + Arrays.toString( _xst ) + "\n" ); + + ( "xst: " + _xst + "\n" ); } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/Xst.java b/src/scratchpad/src/org/apache/poi/hwpf/model/Xst.java new file mode 100644 index 000000000..6a596a495 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/Xst.java @@ -0,0 +1,131 @@ +package org.apache.poi.hwpf.model; + +import java.util.Arrays; + +import org.apache.poi.util.LittleEndian; + +/** + * The Xst structure is a string. The string is prepended by its length and is + * not null-terminated. + *

+ * Documentation quoted from Page 424 of 621. [MS-DOC] -- v20110315 Word (.doc) + * Binary File Format + * + * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com) + */ +public class Xst +{ + + /** + * An unsigned integer that specifies the number of characters that are + * contained in the rgtchar array. + */ + private int _cch; + + /** + * An array of 16-bit Unicode characters that make up a string. + */ + private char[] _rgtchar; + + public Xst() + { + _cch = 0; + _rgtchar = new char[0]; + } + + public Xst( byte[] data, int startOffset ) + { + int offset = startOffset; + + _cch = LittleEndian.getUShort( data, offset ); + offset += LittleEndian.SHORT_SIZE; + + _rgtchar = new char[_cch]; + for ( int x = 0; x < _cch; x++ ) + { + _rgtchar[x] = (char) LittleEndian.getShort( data, offset ); + offset += LittleEndian.SHORT_SIZE; + } + + } + + public Xst( String str ) + { + _cch = str.length(); + _rgtchar = str.toCharArray(); + } + + @Override + public boolean equals( Object obj ) + { + if ( this == obj ) + return true; + if ( obj == null ) + return false; + if ( getClass() != obj.getClass() ) + return false; + Xst other = (Xst) obj; + if ( _cch != other._cch ) + return false; + if ( !Arrays.equals( _rgtchar, other._rgtchar ) ) + return false; + return true; + } + + public String getAsJavaString() + { + return new String( _rgtchar ); + } + + /** + * An unsigned integer that specifies the number of characters that are + * contained in the rgtchar array. + */ + public int getCch() + { + return _cch; + } + + /** + * An array of 16-bit Unicode characters that make up a string. + */ + public char[] getRgtchar() + { + return _rgtchar; + } + + public int getSize() + { + return LittleEndian.SHORT_SIZE + _rgtchar.length * 2; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + _cch; + result = prime * result + Arrays.hashCode( _rgtchar ); + return result; + } + + public void serialize( byte[] data, int startOffset ) + { + int offset = startOffset; + + LittleEndian.putUShort( data, offset, _cch ); + offset += LittleEndian.SHORT_SIZE; + + for ( char c : _rgtchar ) + { + LittleEndian.putShort( data, offset, (short) c ); + offset += LittleEndian.SHORT_SIZE; + } + } + + @Override + public String toString() + { + return new String( "Xst [" + _cch + "; " + _rgtchar + "]" ); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java index 6f2d21fdd..65f907691 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Paragraph.java @@ -607,7 +607,7 @@ public short getStyleIndex() public boolean isInList() { - return getIlvl() != 0 && getIlfo() != 0x000 && getIlfo() != 0xF801; + return getIlfo() != 0x000 && getIlfo() != 0xF801; } /**