From d8ca4323de72d4dbb787140203cd07836e264d21 Mon Sep 17 00:00:00 2001 From: Sergey Vladimirov Date: Tue, 6 Nov 2012 16:26:43 +0000 Subject: [PATCH] Bug 52583 - Conversion to html : Problem with combobox git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1406208 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../hwpf/converter/AbstractWordConverter.java | 35 +- .../hwpf/converter/HtmlDocumentFacade.java | 17 + .../hwpf/converter/WordToHtmlConverter.java | 14 + .../src/org/apache/poi/hwpf/model/FFData.java | 214 +++++++++ .../org/apache/poi/hwpf/model/FFDataBase.java | 33 ++ .../poi/hwpf/model/FIBFieldHandler.java | 3 +- .../poi/hwpf/model/NilPICFAndBinData.java | 59 +++ .../src/org/apache/poi/hwpf/model/Sttb.java | 232 ++++++++++ .../org/apache/poi/hwpf/model/SttbUtils.java | 155 +------ .../src/org/apache/poi/hwpf/model/Xstz.java | 69 +++ .../model/types/FFDataBaseAbstractType.java | 428 ++++++++++++++++++ .../hwpf/sprm/CharacterSprmUncompressor.java | 13 +- .../poi/hwpf/usermodel/CharacterRun.java | 41 ++ .../converter/TestWordToHtmlConverter.java | 8 + src/types/definitions/FFData_type.xml | 81 ++++ src/types/styles/hdftype.xsl | 67 ++- test-data/document/Bug52583.doc | Bin 0 -> 26112 bytes 18 files changed, 1290 insertions(+), 180 deletions(-) create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/FFData.java create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/FFDataBase.java create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/NilPICFAndBinData.java create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/Sttb.java create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/Xstz.java create mode 100644 src/scratchpad/src/org/apache/poi/hwpf/model/types/FFDataBaseAbstractType.java create mode 100644 src/types/definitions/FFData_type.xml create mode 100644 test-data/document/Bug52583.doc diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index ad3b0f62b..f297c5061 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 52583 - add support for drop-down lists in doc to html convertion 52863 - add workaround for files with broken CHP SPRMs 53182 - Reading combined character styling and direct formatting of a character run 52311 - Conversion to html : Problem in titles number diff --git a/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java b/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java index 64a1900ab..95da85020 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/converter/AbstractWordConverter.java @@ -167,6 +167,7 @@ public abstract class AbstractWordConverter } structures.add( structure ); } + private final Set bookmarkStack = new LinkedHashSet(); private FontReplacer fontReplacer = new DefaultFontReplacer(); @@ -778,6 +779,12 @@ public abstract class AbstractWordConverter CharacterRun characterRun, OfficeDrawing officeDrawing, String path, Element block ); + protected void processDropDownList( Element block, + CharacterRun characterRun, String[] values, int defaultIndex ) + { + outputCharacters( block, characterRun, values[defaultIndex] ); + } + protected abstract void processEndnoteAutonumbered( HWPFDocument wordDocument, int noteIndex, Element block, Range endnoteTextRange ); @@ -835,6 +842,22 @@ public abstract class AbstractWordConverter break; } + case 83: // drop down + { + Range fieldContent = field.firstSubrange( parentRange ); + CharacterRun cr = fieldContent.getCharacterRun( fieldContent + .numCharacterRuns() - 1 ); + String[] values = cr.getDropDownListValues(); + Integer defIndex = cr.getDropDownListDefaultItemIndex(); + + if ( values != null ) + { + processDropDownList( currentBlock, cr, values, + defIndex == null ? -1 : defIndex.intValue() ); + return; + } + break; + } case 88: // hyperlink { final Range firstSubrange = field.firstSubrange( parentRange ); @@ -1010,12 +1033,6 @@ public abstract class AbstractWordConverter return false; } } - - protected void processSymbol( HWPFDocument doc, CharacterRun characterRun, - Element block ) - { - - } @SuppressWarnings( "unused" ) protected boolean processOle2( HWPFDocument wordDocument, Element block, @@ -1109,6 +1126,12 @@ public abstract class AbstractWordConverter processSection( wordDocument, section, 0 ); } + protected void processSymbol( HWPFDocument doc, CharacterRun characterRun, + Element block ) + { + + } + protected abstract void processTable( HWPFDocumentCore wordDocument, Element flow, Table table ); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/converter/HtmlDocumentFacade.java b/src/scratchpad/src/org/apache/poi/hwpf/converter/HtmlDocumentFacade.java index b405b2cb6..67f6ea1cd 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/converter/HtmlDocumentFacade.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/converter/HtmlDocumentFacade.java @@ -161,11 +161,28 @@ public class HtmlDocumentFacade return document.createElement( "li" ); } + public Element createOption( String value, boolean selected ) + { + Element result = document.createElement( "option" ); + result.appendChild( createText( value ) ); + if ( selected ) + { + result.setAttribute( "selected", "selected" ); + } + return result; + } + public Element createParagraph() { return document.createElement( "p" ); } + public Element createSelect() + { + Element result = document.createElement( "select" ); + return result; + } + public Element createTable() { return document.createElement( "table" ); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/converter/WordToHtmlConverter.java b/src/scratchpad/src/org/apache/poi/hwpf/converter/WordToHtmlConverter.java index 3092625e8..d5314c9e7 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/converter/WordToHtmlConverter.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/converter/WordToHtmlConverter.java @@ -82,6 +82,7 @@ public class WordToHtmlConverter extends AbstractWordConverter private static final POILogger logger = POILogFactory .getLogger( WordToHtmlConverter.class ); + private static String getSectionStyle( Section section ) { float leftMargin = section.getMarginLeft() / TWIPS_PER_INCH; @@ -280,6 +281,19 @@ public class WordToHtmlConverter extends AbstractWordConverter afterProcess(); } + @Override + protected void processDropDownList( Element block, + CharacterRun characterRun, String[] values, int defaultIndex ) + { + Element select = htmlDocumentFacade.createSelect(); + for ( int i = 0; i < values.length; i++ ) + { + select.appendChild( htmlDocumentFacade.createOption( values[i], + defaultIndex == i ) ); + } + block.appendChild( select ); + } + @Override protected void processDrawnObject( HWPFDocument doc, CharacterRun characterRun, OfficeDrawing officeDrawing, diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FFData.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FFData.java new file mode 100644 index 000000000..7ea4b7938 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FFData.java @@ -0,0 +1,214 @@ +package org.apache.poi.hwpf.model; + +import org.apache.poi.util.Internal; +import org.apache.poi.util.LittleEndian; + +/** + * The FFData structure specifies form field data for a text box, check box, or + * drop-down list box. + *

+ * Class and fields descriptions are quoted from [MS-DOC] -- v20121003 Word + * (.doc) Binary File Format; Copyright (c) 2012 Microsoft Corporation; Release: + * October 8, 2012 + *

+ * This class is internal. It content or properties may change without notice + * due to changes in our knowledge of internal Microsoft Word binary structures. + * + * @author Sergey Vladimirov; according to [MS-DOC] -- v20121003 Word (.doc) + * Binary File Format; Copyright (c) 2012 Microsoft Corporation; + * Release: October 8, 2012 + */ +@Internal +public class FFData +{ + private FFDataBase _base; + + /** + * An optional STTB that specifies the entries in the dropdown list box. + * This MUST exist if and only if bits.iType is iTypeDrop (2). The entries + * are Unicode strings and do not have extra data. This MUST NOT exceed 25 + * elements. + */ + private Sttb _hsttbDropList; + + /** + * An optional unsigned integer that specifies the default state of the + * checkbox or dropdown list box. This value MUST exist if and only if + * bits.iType is iTypeChck (1) or iTypeDrop (2). If bits.iType is iTypeChck + * (1), wDef MUST be 0 or 1 and specify the default state of the checkbox as + * unchecked or checked, respectively. If bits.iType is iTypeDrop (2), wDef + * MUST be less than the number of items in the dropdown list box and + * specify the default item selected (zero-based index). + */ + private Integer _wDef; + + private Xstz _xstzEntryMcr; + + private Xstz _xstzExitMcr; + + private Xstz _xstzHelpText; + + /** + * An Xstz that specifies the name of this form field. xstzName.cch MUST NOT + * exceed 20. + */ + private Xstz _xstzName; + + private Xstz _xstzStatText; + + /** + * An optional Xstz that specifies the default text of this textbox. This + * structure MUST exist if and only if bits.iType is iTypeTxt (0). + * xstzTextDef.cch MUST NOT exceed 255. If bits.iTypeTxt is either + * iTypeTxtCurDate (3) or iTypeTxtCurTime (4), xstzTextDef MUST be an empty + * string. If bits.iTypeTxt is iTypeTxtCalc (5), xstzTextDef specifies an + * expression to calculate. + */ + private Xstz _xstzTextDef; + + private Xstz _xstzTextFormat; + + public FFData( byte[] std, int offset ) + { + fillFields( std, offset ); + } + + public void fillFields( final byte[] std, final int startOffset ) + { + int offset = startOffset; + + this._base = new FFDataBase( std, offset ); + offset += FFDataBase.getSize(); + + this._xstzName = new Xstz( std, offset ); + offset += this._xstzName.getSize(); + + if ( _base.getIType() == FFDataBase.ITYPE_TEXT ) + { + _xstzTextDef = new Xstz( std, offset ); + offset += this._xstzTextDef.getSize(); + } + else + { + this._xstzTextDef = null; + } + + if ( _base.getIType() == FFDataBase.ITYPE_CHCK + || _base.getIType() == FFDataBase.ITYPE_DROP ) + { + this._wDef = Integer + .valueOf( LittleEndian.getUShort( std, offset ) ); + offset += LittleEndian.SHORT_SIZE; + } + else + { + this._wDef = null; + } + + _xstzTextFormat = new Xstz( std, offset ); + offset += this._xstzTextFormat.getSize(); + + _xstzHelpText = new Xstz( std, offset ); + offset += this._xstzHelpText.getSize(); + + _xstzStatText = new Xstz( std, offset ); + offset += this._xstzStatText.getSize(); + + _xstzEntryMcr = new Xstz( std, offset ); + offset += this._xstzEntryMcr.getSize(); + + _xstzExitMcr = new Xstz( std, offset ); + offset += this._xstzExitMcr.getSize(); + + if ( _base.getIType() == FFDataBase.ITYPE_DROP ) + { + _hsttbDropList = new Sttb( std, offset ); + offset += _hsttbDropList.getSize(); + } + } + + /** + * specify the default item selected (zero-based index). + */ + public int getDefaultDropDownItemIndex() + { + return _wDef.intValue(); + } + + public String[] getDropList() + { + return _hsttbDropList.getData(); + } + + public int getSize() + { + int size = FFDataBase.getSize(); + + size += _xstzName.getSize(); + + if ( _base.getIType() == FFDataBase.ITYPE_TEXT ) + { + size += _xstzTextDef.getSize(); + } + + if ( _base.getIType() == FFDataBase.ITYPE_CHCK + || _base.getIType() == FFDataBase.ITYPE_DROP ) + { + size += LittleEndian.SHORT_SIZE; + } + + size += _xstzTextFormat.getSize(); + size += _xstzHelpText.getSize(); + size += _xstzStatText.getSize(); + size += _xstzEntryMcr.getSize(); + size += _xstzExitMcr.getSize(); + + if ( _base.getIType() == FFDataBase.ITYPE_DROP ) + { + size += _hsttbDropList.getSize(); + } + + return size; + } + + public String getTextDef() + { + return _xstzTextDef.getAsJavaString(); + } + + public byte[] serialize() + { + byte[] buffer = new byte[getSize()]; + int offset = 0; + + _base.serialize( buffer, offset ); + offset += FFDataBase.getSize(); + + offset += _xstzName.serialize( buffer, offset ); + + if ( _base.getIType() == FFDataBase.ITYPE_TEXT ) + { + offset += _xstzTextDef.serialize( buffer, offset ); + } + + if ( _base.getIType() == FFDataBase.ITYPE_CHCK + || _base.getIType() == FFDataBase.ITYPE_DROP ) + { + LittleEndian.putUShort( buffer, offset, _wDef ); + offset += LittleEndian.SHORT_SIZE; + } + + offset += _xstzTextFormat.serialize( buffer, offset ); + offset += _xstzHelpText.serialize( buffer, offset ); + offset += _xstzStatText.serialize( buffer, offset ); + offset += _xstzEntryMcr.serialize( buffer, offset ); + offset += _xstzExitMcr.serialize( buffer, offset ); + + if ( _base.getIType() == FFDataBase.ITYPE_DROP ) + { + offset += _hsttbDropList.serialize( buffer, offset ); + } + + return buffer; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FFDataBase.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FFDataBase.java new file mode 100644 index 000000000..999dca551 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FFDataBase.java @@ -0,0 +1,33 @@ +package org.apache.poi.hwpf.model; + +import org.apache.poi.hwpf.model.types.FFDataBaseAbstractType; +import org.apache.poi.util.Internal; + +/** + * The FFData structure specifies form field data for a text box, check box, or + * drop-down list box. + *

+ * Class and fields descriptions are quoted from [MS-DOC] -- v20121003 Word + * (.doc) Binary File Format; Copyright (c) 2012 Microsoft Corporation; Release: + * October 8, 2012 + *

+ * This class is internal. It content or properties may change without notice + * due to changes in our knowledge of internal Microsoft Word binary structures. + * + * @author Sergey Vladimirov; according to [MS-DOC] -- v20121003 Word (.doc) + * Binary File Format; Copyright (c) 2012 Microsoft Corporation; + * Release: October 8, 2012 + */ +@Internal +public class FFDataBase extends FFDataBaseAbstractType +{ + public FFDataBase() + { + super(); + } + + public FFDataBase( byte[] std, int offset ) + { + fillFields( std, offset ); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FIBFieldHandler.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FIBFieldHandler.java index 9d61d8abb..ff55082af 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/FIBFieldHandler.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FIBFieldHandler.java @@ -90,7 +90,8 @@ public final class FIBFieldHandler public static final int PLCFATNBKL = 43; // 506 == 0x01FA; 510 == 0x01FE public static final int PMS = 44; - public static final int FORMFLDSTTBS = 45; + // 514 == 0x0202; 518 == 0x0206 + public static final int FORMFLDSTTBS = 45; public static final int PLCFENDREF = 46; public static final int PLCFENDTXT = 47; public static final int PLCFFLDEDN = 48; diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/NilPICFAndBinData.java b/src/scratchpad/src/org/apache/poi/hwpf/model/NilPICFAndBinData.java new file mode 100644 index 000000000..ea98db50f --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/NilPICFAndBinData.java @@ -0,0 +1,59 @@ +package org.apache.poi.hwpf.model; + +import org.apache.poi.util.ArrayUtil; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +public class NilPICFAndBinData +{ + + private static final POILogger log = POILogFactory + .getLogger( NilPICFAndBinData.class ); + + private byte[] _binData; + + public NilPICFAndBinData( byte[] data, int offset ) + { + fillFields( data, offset ); + } + + public void fillFields( byte[] data, int offset ) + { + int lcb = LittleEndian.getInt( data, offset ); + int cbHeader = LittleEndian.getUShort( data, offset + + LittleEndian.INT_SIZE ); + + if ( cbHeader != 0x44 ) + { + log.log( POILogger.WARN, "NilPICFAndBinData at offset ", offset, + " cbHeader 0x" + Integer.toHexString( cbHeader ) + + " != 0x44" ); + } + + // skip the 62 ignored bytes + int binaryLength = lcb - cbHeader; + this._binData = ArrayUtil.copyOfRange( data, offset + cbHeader, + offset + cbHeader + binaryLength ); + } + + public byte[] getBinData() + { + return _binData; + } + + public byte[] serialize() + { + byte[] bs = new byte[_binData.length + 0x44]; + LittleEndian.putInt( bs, 0, _binData.length + 0x44 ); + System.arraycopy( _binData, 0, bs, 0x44, _binData.length ); + return bs; + } + + public int serialize( byte[] data, int offset ) + { + LittleEndian.putInt( data, offset, _binData.length + 0x44 ); + System.arraycopy( _binData, 0, data, offset + 0x44, _binData.length ); + return 0x44 + _binData.length; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/Sttb.java b/src/scratchpad/src/org/apache/poi/hwpf/model/Sttb.java new file mode 100644 index 000000000..fabd1e233 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/Sttb.java @@ -0,0 +1,232 @@ +package org.apache.poi.hwpf.model; + +import org.apache.poi.util.ArrayUtil; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.StringUtil; + +/** + * The STTB is a string table that is made up of a header that is followed by an + * array of elements. The cData value specifies the number of elements that are + * contained in the array. + *

+ * Class and fields descriptions are quoted from [MS-DOC] -- v20121003 Word + * (.doc) Binary File Format; Copyright (c) 2012 Microsoft Corporation; Release: + * October 8, 2012 + *

+ * This class is internal. It content or properties may change without notice + * due to changes in our knowledge of internal Microsoft Word binary structures. + * + * @author Sergey Vladimirov; according to [MS-DOC] -- v20121003 Word (.doc) + * Binary File Format; Copyright (c) 2012 Microsoft Corporation; + * Release: October 8, 2012 + */ +public class Sttb +{ + + private int _cbExtra; + + private final int _cDataLength; + + private String[] _data; + + private byte[][] _extraData; + + private final boolean _fExtend = true; + + public Sttb( byte[] buffer, int startOffset ) + { + this( 2, buffer, startOffset ); + } + + public Sttb( int cDataLength, byte[] buffer, int startOffset ) + { + this._cDataLength = cDataLength; + fillFields( buffer, startOffset ); + } + + public Sttb( int cDataLength, String[] data ) + { + this._cDataLength = cDataLength; + + this._data = ArrayUtil.copyOf( data, new String[data.length] ); + + this._cbExtra = 0; + this._extraData = null; + } + + public void fillFields( byte[] buffer, int startOffset ) + { + short ffff = LittleEndian.getShort( buffer, startOffset ); + int offset = startOffset + LittleEndian.SHORT_SIZE; + + if ( ffff != (short) 0xffff ) + { + // Non-extended character Pascal strings + throw new UnsupportedOperationException( + "Non-extended character Pascal strings are not supported right now. " + + "Please, contact POI developers for update." ); + } + // strings are extended character strings + + int cData = _cDataLength == 2 ? LittleEndian.getUShort( buffer, offset ) + : LittleEndian.getInt( buffer, offset ); + offset += _cDataLength; + + this._cbExtra = LittleEndian.getUShort( buffer, offset ); + offset += 2; + + _data = new String[cData]; + _extraData = new byte[cData][]; + + for ( int i = 0; i < cData; i++ ) + { + int cchData = LittleEndian.getShort( buffer, offset ); + offset += 2; + + if ( cchData < 0 ) + continue; + + _data[i] = StringUtil.getFromUnicodeLE( buffer, offset, cchData ); + offset += cchData * 2; + + _extraData[i] = LittleEndian + .getByteArray( buffer, offset, _cbExtra ); + offset += _cbExtra; + } + } + + /** + * The definition of each STTB specifies the meaning of this field. If this + * STTB uses extended characters, the size of this field is 2*cchData bytes + * and it is a Unicode string unless otherwise specified by the STTB + * definition. If this STTB does not use extended characters, then the size + * of this field is cchData bytes and it is an ANSI string, unless otherwise + * specified by the STTB definition. + */ + public String[] getData() + { + return _data; + } + + public int getSize() + { + // ffff + int size = LittleEndian.SHORT_SIZE; + + // cData + size += _cDataLength; + + // cbExtra + size += LittleEndian.SHORT_SIZE; + + if ( this._fExtend ) + { + for ( String data : _data ) + { + // cchData + size += LittleEndian.SHORT_SIZE; + // data + size += 2 * data.length(); + } + } + else + { + for ( String data : _data ) + { + // cchData + size += LittleEndian.BYTE_SIZE; + // data + size += 1 * data.length(); + } + } + + // extraData + if ( _extraData != null ) + { + size += _cbExtra * _data.length; + } + + return size; + } + + public byte[] serialize() + { + final byte[] buffer = new byte[getSize()]; + + LittleEndian.putShort( buffer, 0, (short) 0xffff ); + + if ( _data == null || _data.length == 0 ) + { + if ( _cDataLength == 4 ) + { + LittleEndian.putInt( buffer, 2, 0 ); + LittleEndian.putUShort( buffer, 6, _cbExtra ); + return buffer; + } + + LittleEndian.putUShort( buffer, 2, 0 ); + LittleEndian.putUShort( buffer, 4, _cbExtra ); + return buffer; + } + + int offset; + if ( _cDataLength == 4 ) + { + LittleEndian.putInt( buffer, 2, _data.length ); + LittleEndian.putUShort( buffer, 6, _cbExtra ); + offset = 2 + LittleEndian.INT_SIZE + LittleEndian.SHORT_SIZE; + } + else + { + LittleEndian.putUShort( buffer, 2, _data.length ); + LittleEndian.putUShort( buffer, 4, _cbExtra ); + offset = 2 + LittleEndian.SHORT_SIZE + LittleEndian.SHORT_SIZE; + } + + for ( int i = 0; i < _data.length; i++ ) + { + String entry = _data[i]; + if ( entry == null ) + { + // is it correct? + buffer[offset] = -1; + buffer[offset + 1] = 0; + offset += 2; + continue; + } + + if ( _fExtend ) + { + LittleEndian.putUShort( buffer, offset, (int) entry.length() ); + offset += LittleEndian.SHORT_SIZE; + + StringUtil.putUnicodeLE( entry, buffer, offset ); + offset += 2 * entry.length(); + } + else + { + throw new UnsupportedOperationException( + "ANSI STTB is not supported yet" ); + } + + if ( _cbExtra != 0 ) + { + if ( _extraData[i] != null && _extraData[i].length != 0 ) + { + System.arraycopy( _extraData[i], 0, buffer, offset, + Math.min( _extraData[i].length, _cbExtra ) ); + } + offset += _cbExtra; + } + } + + return buffer; + } + + public int serialize( byte[] buffer, int offset ) + { + byte[] bs = serialize(); + System.arraycopy( bs, 0, buffer, offset, bs.length ); + return bs.length; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/SttbUtils.java b/src/scratchpad/src/org/apache/poi/hwpf/model/SttbUtils.java index 1cc11e0cd..fba473f9c 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/SttbUtils.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/SttbUtils.java @@ -20,8 +20,6 @@ import java.io.IOException; import org.apache.poi.hwpf.model.io.HWPFOutputStream; import org.apache.poi.util.Internal; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.StringUtil; /** * Utils class for storing and reading "STring TaBle stored in File" @@ -32,180 +30,47 @@ import org.apache.poi.util.StringUtil; class SttbUtils { - static class Sttb - { - public int cbExtra; - - public int cDataLength; - - public String[] data; - - public byte[][] extraData; - } - - private static final int CBEXTRA_STTB_SAVED_BY = 0; // bytes - - private static final int CBEXTRA_STTBF_BKMK = 0; // bytes - - private static final int CBEXTRA_STTBF_R_MARK = 0; // bytes - private static final int CDATA_SIZE_STTB_SAVED_BY = 2; // bytes private static final int CDATA_SIZE_STTBF_BKMK = 2; // bytes private static final int CDATA_SIZE_STTBF_R_MARK = 2; // bytes - static Sttb read( int cDataLength, byte[] buffer, int startOffset ) - { - short ffff = LittleEndian.getShort( buffer, startOffset ); - int offset = startOffset + 2; - - if ( ffff != (short) 0xffff ) - { - // Non-extended character Pascal strings - throw new UnsupportedOperationException( - "Non-extended character Pascal strings are not supported right now. " - + "Please, contact POI developers for update." ); - } - // strings are extended character strings - - int cData = cDataLength == 2 ? LittleEndian.getUShort( buffer, offset ) - : LittleEndian.getInt( buffer, offset ); - offset += cDataLength; - - Sttb sttb = new Sttb(); - sttb.cDataLength = cDataLength; - sttb.cbExtra = LittleEndian.getUShort( buffer, offset ); - offset += 2; - - sttb.data = new String[cData]; - sttb.extraData = new byte[cData][]; - - for ( int i = 0; i < cData; i++ ) - { - int cchData = LittleEndian.getShort( buffer, offset ); - offset += 2; - - if ( cchData < 0 ) - continue; - - sttb.data[i] = StringUtil - .getFromUnicodeLE( buffer, offset, cchData ); - offset += cchData * 2; - - sttb.extraData[i] = LittleEndian.getByteArray( buffer, offset, - sttb.cbExtra ); - offset += sttb.cbExtra; - } - - return sttb; - } - static String[] readSttbfBkmk( byte[] buffer, int startOffset ) { - return read( CDATA_SIZE_STTBF_BKMK, buffer, startOffset ).data; + return new Sttb( CDATA_SIZE_STTBF_BKMK, buffer, startOffset ).getData(); } static String[] readSttbfRMark( byte[] buffer, int startOffset ) { - return read( CDATA_SIZE_STTBF_R_MARK, buffer, startOffset ).data; + return new Sttb( CDATA_SIZE_STTBF_R_MARK, buffer, startOffset ) + .getData(); } static String[] readSttbSavedBy( byte[] buffer, int startOffset ) { - return read( CDATA_SIZE_STTB_SAVED_BY, buffer, startOffset ).data; - } - - static void write( Sttb sttb, HWPFOutputStream tableStream ) - throws IOException - { - final int headerSize = sttb.cDataLength == 2 ? 6 : 8; - - byte[] header = new byte[headerSize]; - LittleEndian.putShort( header, 0, (short) 0xffff ); - - if ( sttb.data == null || sttb.data.length == 0 ) - { - if ( sttb.cDataLength == 4 ) - { - LittleEndian.putInt( header, 2, 0 ); - LittleEndian.putUShort( header, 6, sttb.cbExtra ); - tableStream.write( header ); - return; - } - - LittleEndian.putUShort( header, 2, 0 ); - LittleEndian.putUShort( header, 4, sttb.cbExtra ); - tableStream.write( header ); - return; - } - - if ( sttb.cDataLength == 4 ) - { - LittleEndian.putInt( header, 2, sttb.data.length ); - LittleEndian.putUShort( header, 6, sttb.cbExtra ); - tableStream.write( header ); - } - else - { - LittleEndian.putUShort( header, 2, sttb.data.length ); - LittleEndian.putUShort( header, 4, sttb.cbExtra ); - tableStream.write( header ); - } - - for ( int i = 0; i < sttb.data.length; i++ ) - { - String entry = sttb.data[i]; - if ( entry == null ) - { - // is it correct? - tableStream.write( new byte[] { -1, 0 } ); - continue; - } - - byte[] buf = new byte[entry.length() * 2 + sttb.cbExtra + 2]; - - LittleEndian.putShort( buf, 0, (short) entry.length() ); - StringUtil.putUnicodeLE( entry, buf, 2 ); - - if ( sttb.extraData != null && i < sttb.extraData.length - && sttb.extraData[i] != null ) - System.arraycopy( sttb.extraData[i], 0, buf, - entry.length() * 2, - Math.min( sttb.extraData[i].length, sttb.cbExtra ) ); - - tableStream.write( buf ); - } + return new Sttb( CDATA_SIZE_STTB_SAVED_BY, buffer, startOffset ) + .getData(); } static void writeSttbfBkmk( String[] data, HWPFOutputStream tableStream ) throws IOException { - Sttb sttb = new Sttb(); - sttb.cDataLength = CDATA_SIZE_STTBF_BKMK; - sttb.data = data; - sttb.cbExtra = CBEXTRA_STTBF_BKMK; - write( sttb, tableStream ); + tableStream.write( new Sttb( CDATA_SIZE_STTBF_BKMK, data ).serialize() ); } static void writeSttbfRMark( String[] data, HWPFOutputStream tableStream ) throws IOException { - Sttb sttb = new Sttb(); - sttb.cDataLength = CDATA_SIZE_STTBF_R_MARK; - sttb.data = data; - sttb.cbExtra = CBEXTRA_STTBF_R_MARK; - write( sttb, tableStream ); + tableStream.write( new Sttb( CDATA_SIZE_STTBF_R_MARK, data ) + .serialize() ); } static void writeSttbSavedBy( String[] data, HWPFOutputStream tableStream ) throws IOException { - Sttb sttb = new Sttb(); - sttb.cDataLength = CDATA_SIZE_STTB_SAVED_BY; - sttb.data = data; - sttb.cbExtra = CBEXTRA_STTB_SAVED_BY; - write( sttb, tableStream ); + tableStream.write( new Sttb( CDATA_SIZE_STTB_SAVED_BY, data ) + .serialize() ); } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/Xstz.java b/src/scratchpad/src/org/apache/poi/hwpf/model/Xstz.java new file mode 100644 index 000000000..fb351e9cb --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/Xstz.java @@ -0,0 +1,69 @@ +package org.apache.poi.hwpf.model; + +import org.apache.poi.util.Internal; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +@Internal +public class Xstz +{ + private static final POILogger log = POILogFactory.getLogger( Xstz.class ); + + private final short _chTerm = 0; + private Xst _xst; + + public Xstz() + { + _xst = new Xst(); + } + + public Xstz( byte[] data, int startOffset ) + { + fillFields( data, startOffset ); + } + + public void fillFields( byte[] data, int startOffset ) + { + int offset = startOffset; + + _xst = new Xst( data, offset ); + offset += _xst.getSize(); + + short term = LittleEndian.getShort( data, offset ); + if ( term != 0 ) + { + log.log( POILogger.WARN, "chTerm at the end of Xstz at offset ", + offset, " is not 0" ); + } + } + + public String getAsJavaString() + { + return _xst.getAsJavaString(); + } + + public int getSize() + { + return _xst.getSize() + LittleEndian.SHORT_SIZE; + } + + public int serialize( byte[] data, int startOffset ) + { + int offset = startOffset; + + _xst.serialize( data, offset ); + offset += _xst.getSize(); + + LittleEndian.putUShort( data, offset, _chTerm ); + offset += LittleEndian.SHORT_SIZE; + + return offset - startOffset; + } + + @Override + public String toString() + { + return "[Xstz]" + _xst.getAsJavaString() + "[/Xstz]"; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/types/FFDataBaseAbstractType.java b/src/scratchpad/src/org/apache/poi/hwpf/model/types/FFDataBaseAbstractType.java new file mode 100644 index 000000000..f582018b2 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/types/FFDataBaseAbstractType.java @@ -0,0 +1,428 @@ +/* ==================================================================== + 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.types; + + +import org.apache.poi.util.BitField; +import org.apache.poi.util.Internal; +import org.apache.poi.util.LittleEndian; + +/** + * The FFData structure specifies form field data for a text + box, check box, or drop-down list box.

Class and fields + descriptions are quoted from [MS-DOC] -- v20121003 Word (.doc) Binary + File Format; Copyright (c) 2012 Microsoft Corporation; Release: + October 8, 2012 + + *

+ * NOTE: This source is automatically generated please do not modify this file. Either subclass or + * remove the record in src/types/definitions. + *

+ * This class is internal. It content or properties may change without notice + * due to changes in our knowledge of internal Microsoft Word binary structures. + + * @author Sergey Vladimirov; according to [MS-DOC] -- v20121003 Word + (.doc) Binary File Format; Copyright (c) 2012 Microsoft Corporation; + Release: October 8, 2012 + + */ +@Internal +public abstract class FFDataBaseAbstractType +{ + + protected long field_1_version; + protected short field_2_bits; + /**/private static final BitField iType = new BitField(0x0003); + /** Specifies that the form field is a textbox. */ + /* */public final static byte ITYPE_TEXT = 0; + /** Specifies that the form field is a checkbox. */ + /* */public final static byte ITYPE_CHCK = 1; + /** Specifies that the form field is a dropdown list box. */ + /* */public final static byte ITYPE_DROP = 2; + /**/private static final BitField iRes = new BitField(0x007C); + /**/private static final BitField fOwnHelp = new BitField(0x0080); + /**/private static final BitField fOwnStat = new BitField(0x0100); + /**/private static final BitField fProt = new BitField(0x0200); + /**/private static final BitField iSize = new BitField(0x0400); + /**/private static final BitField iTypeTxt = new BitField(0x3800); + /** Specifies that the textbox value is regular text. */ + /* */public final static byte ITYPETXT_REG = 0; + /** Specifies that the textbox value is a number. */ + /* */public final static byte ITYPETXT_NUM = 0; + /** Specifies that the textbox value is a date or time. */ + /* */public final static byte ITYPETXT_DATE = 0; + /** Specifies that the textbox value is the current date. */ + /* */public final static byte ITYPETXT_CURDATE = 0; + /** Specifies that the textbox value is the current time. */ + /* */public final static byte ITYPETXT_CURTIME = 0; + /** Specifies that the textbox value is calculated from an expression. The expression is given by FFData.xstzTextDef. */ + /* */protected final static byte ITYPETXT_CALC = 0; + /**/private static final BitField fRecalc = new BitField(0x4000); + /**/private static final BitField fHasListBox = new BitField(0x8000); + protected int field_3_cch; + protected int field_4_hps; + + protected FFDataBaseAbstractType() + { + } + + protected void fillFields( byte[] data, int offset ) + { + field_1_version = LittleEndian.getUInt( data, 0x0 + offset ); + field_2_bits = LittleEndian.getShort( data, 0x4 + offset ); + field_3_cch = LittleEndian.getShort( data, 0x6 + offset ); + field_4_hps = LittleEndian.getShort( data, 0x8 + offset ); + } + + public void serialize( byte[] data, int offset ) + { + LittleEndian.putUInt( data, 0x0 + offset, field_1_version ); + LittleEndian.putShort( data, 0x4 + offset, field_2_bits ); + LittleEndian.putUShort( data, 0x6 + offset, field_3_cch ); + LittleEndian.putUShort( data, 0x8 + offset, field_4_hps ); + } + + public byte[] serialize() + { + final byte[] result = new byte[ getSize() ]; + serialize( result, 0 ); + return result; + } + + /** + * Size of record + */ + public static int getSize() + { + return 0 + 4 + 2 + 2 + 2; + } + + @Override + public boolean equals( Object obj ) + { + if ( this == obj ) + return true; + if ( obj == null ) + return false; + if ( getClass() != obj.getClass() ) + return false; + FFDataBaseAbstractType other = (FFDataBaseAbstractType) obj; + if ( field_1_version != other.field_1_version ) + return false; + if ( field_2_bits != other.field_2_bits ) + return false; + if ( field_3_cch != other.field_3_cch ) + return false; + if ( field_4_hps != other.field_4_hps ) + return false; + return true; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + + (int) ( field_1_version ^ ( field_1_version >>> 32 ) ); + result = prime * result + field_2_bits; + result = prime * result + field_3_cch; + result = prime * result + field_4_hps; + return result; + } + + public String toString() + { + StringBuilder builder = new StringBuilder(); + + builder.append("[FFDataBase]\n"); + builder.append( " .version = " ); + builder.append(" ( ").append( field_1_version ).append( " )\n" ); + builder.append( " .bits = " ); + builder.append(" ( ").append( field_2_bits ).append( " )\n" ); + builder.append(" .iType = ").append(getIType()).append('\n'); + builder.append(" .iRes = ").append(getIRes()).append('\n'); + builder.append(" .fOwnHelp = ").append(isFOwnHelp()).append('\n'); + builder.append(" .fOwnStat = ").append(isFOwnStat()).append('\n'); + builder.append(" .fProt = ").append(isFProt()).append('\n'); + builder.append(" .iSize = ").append(isISize()).append('\n'); + builder.append(" .iTypeTxt = ").append(getITypeTxt()).append('\n'); + builder.append(" .fRecalc = ").append(isFRecalc()).append('\n'); + builder.append(" .fHasListBox = ").append(isFHasListBox()).append('\n'); + builder.append( " .cch = " ); + builder.append(" ( ").append( field_3_cch ).append( " )\n" ); + builder.append( " .hps = " ); + builder.append(" ( ").append( field_4_hps ).append( " )\n" ); + + builder.append("[/FFDataBase]"); + return builder.toString(); + } + + /** + * An unsigned integer that MUST be 0xFFFFFFFF. + */ + @Internal + public long getVersion() + { + return field_1_version; + } + + /** + * An unsigned integer that MUST be 0xFFFFFFFF. + */ + @Internal + public void setVersion( long field_1_version ) + { + this.field_1_version = field_1_version; + } + + /** + * An FFDataBits that specifies the type and state of this form field. + */ + @Internal + public short getBits() + { + return field_2_bits; + } + + /** + * An FFDataBits that specifies the type and state of this form field. + */ + @Internal + public void setBits( short field_2_bits ) + { + this.field_2_bits = field_2_bits; + } + + /** + * An unsigned integer that specifies the maximum length, in characters, of the value of the textbox. This value MUST NOT exceed 32767. A value of 0 means there is no maximum length of the value of the textbox. If bits.iType is not iTypeText (0), this value MUST be 0.. + */ + @Internal + public int getCch() + { + return field_3_cch; + } + + /** + * An unsigned integer that specifies the maximum length, in characters, of the value of the textbox. This value MUST NOT exceed 32767. A value of 0 means there is no maximum length of the value of the textbox. If bits.iType is not iTypeText (0), this value MUST be 0.. + */ + @Internal + public void setCch( int field_3_cch ) + { + this.field_3_cch = field_3_cch; + } + + /** + * An unsigned integer. If bits.iType is iTypeChck (1), hps specifies the size, in half-points, of the checkbox and MUST be between 2 and 3168, inclusive. If bits.iType is not iTypeChck (1), hps is undefined and MUST be ignored.. + */ + @Internal + public int getHps() + { + return field_4_hps; + } + + /** + * An unsigned integer. If bits.iType is iTypeChck (1), hps specifies the size, in half-points, of the checkbox and MUST be between 2 and 3168, inclusive. If bits.iType is not iTypeChck (1), hps is undefined and MUST be ignored.. + */ + @Internal + public void setHps( int field_4_hps ) + { + this.field_4_hps = field_4_hps; + } + + /** + * Sets the iType field value. + * An unsigned integer that specifies the type of the form field. + */ + @Internal + public void setIType( byte value ) + { + field_2_bits = (short)iType.setValue(field_2_bits, value); + } + + /** + * An unsigned integer that specifies the type of the form field. + * @return the iType field value. + */ + @Internal + public byte getIType() + { + return ( byte )iType.getValue(field_2_bits); + } + + /** + * Sets the iRes field value. + * An unsigned integer. If iType is iTypeText (0), then iRes MUST be 0. If iType is iTypeChck (1), iRes specifies the state of the checkbox and MUST be 0 (unchecked), 1 (checked), or 25 (undefined). Undefined checkboxes are treated as unchecked. If iType is iTypeDrop (2), iRes specifies the current selected list box item. A value of 25 specifies the selection is undefined. Otherwise, iRes is a zero-based index into FFData.hsttbDropList. + */ + @Internal + public void setIRes( byte value ) + { + field_2_bits = (short)iRes.setValue(field_2_bits, value); + } + + /** + * An unsigned integer. If iType is iTypeText (0), then iRes MUST be 0. If iType is iTypeChck (1), iRes specifies the state of the checkbox and MUST be 0 (unchecked), 1 (checked), or 25 (undefined). Undefined checkboxes are treated as unchecked. If iType is iTypeDrop (2), iRes specifies the current selected list box item. A value of 25 specifies the selection is undefined. Otherwise, iRes is a zero-based index into FFData.hsttbDropList. + * @return the iRes field value. + */ + @Internal + public byte getIRes() + { + return ( byte )iRes.getValue(field_2_bits); + } + + /** + * Sets the fOwnHelp field value. + * A bit that specifies whether the form field has custom help text in FFData.xstzHelpText. If fOwnHelp is 0, FFData.xstzHelpText contains an empty or auto-generated string. + */ + @Internal + public void setFOwnHelp( boolean value ) + { + field_2_bits = (short)fOwnHelp.setBoolean(field_2_bits, value); + } + + /** + * A bit that specifies whether the form field has custom help text in FFData.xstzHelpText. If fOwnHelp is 0, FFData.xstzHelpText contains an empty or auto-generated string. + * @return the fOwnHelp field value. + */ + @Internal + public boolean isFOwnHelp() + { + return fOwnHelp.isSet(field_2_bits); + } + + /** + * Sets the fOwnStat field value. + * A bit that specifies whether the form field has custom status bar text in FFData.xstzStatText. If fOwnStat is 0, FFData.xstzStatText contains an empty or auto-generated string. + */ + @Internal + public void setFOwnStat( boolean value ) + { + field_2_bits = (short)fOwnStat.setBoolean(field_2_bits, value); + } + + /** + * A bit that specifies whether the form field has custom status bar text in FFData.xstzStatText. If fOwnStat is 0, FFData.xstzStatText contains an empty or auto-generated string. + * @return the fOwnStat field value. + */ + @Internal + public boolean isFOwnStat() + { + return fOwnStat.isSet(field_2_bits); + } + + /** + * Sets the fProt field value. + * A bit that specifies whether the form field is protected and its value cannot be changed. + */ + @Internal + public void setFProt( boolean value ) + { + field_2_bits = (short)fProt.setBoolean(field_2_bits, value); + } + + /** + * A bit that specifies whether the form field is protected and its value cannot be changed. + * @return the fProt field value. + */ + @Internal + public boolean isFProt() + { + return fProt.isSet(field_2_bits); + } + + /** + * Sets the iSize field value. + * A bit that specifies whether the size of a checkbox is automatically determined by the text size where the checkbox is located. This value MUST be 0 if iType is not iTypeChck (1). + */ + @Internal + public void setISize( boolean value ) + { + field_2_bits = (short)iSize.setBoolean(field_2_bits, value); + } + + /** + * A bit that specifies whether the size of a checkbox is automatically determined by the text size where the checkbox is located. This value MUST be 0 if iType is not iTypeChck (1). + * @return the iSize field value. + */ + @Internal + public boolean isISize() + { + return iSize.isSet(field_2_bits); + } + + /** + * Sets the iTypeTxt field value. + * An unsigned integer that specifies the type of the textbox. If iType is not iTypeText (0), iTypeTxt MUST be 0 and MUST be ignored. + */ + @Internal + public void setITypeTxt( byte value ) + { + field_2_bits = (short)iTypeTxt.setValue(field_2_bits, value); + } + + /** + * An unsigned integer that specifies the type of the textbox. If iType is not iTypeText (0), iTypeTxt MUST be 0 and MUST be ignored. + * @return the iTypeTxt field value. + */ + @Internal + public byte getITypeTxt() + { + return ( byte )iTypeTxt.getValue(field_2_bits); + } + + /** + * Sets the fRecalc field value. + * A bit that specifies whether the value of the field is automatically calculated after the field is modified. + */ + @Internal + public void setFRecalc( boolean value ) + { + field_2_bits = (short)fRecalc.setBoolean(field_2_bits, value); + } + + /** + * A bit that specifies whether the value of the field is automatically calculated after the field is modified. + * @return the fRecalc field value. + */ + @Internal + public boolean isFRecalc() + { + return fRecalc.isSet(field_2_bits); + } + + /** + * Sets the fHasListBox field value. + * A bit that specifies that the form field has a list box. This value MUST be 1 if iType is iTypeDrop (2). Otherwise, this value MUST be 0. + */ + @Internal + public void setFHasListBox( boolean value ) + { + field_2_bits = (short)fHasListBox.setBoolean(field_2_bits, value); + } + + /** + * A bit that specifies that the form field has a list box. This value MUST be 1 if iType is iTypeDrop (2). Otherwise, this value MUST be 0. + * @return the fHasListBox field value. + */ + @Internal + public boolean isFHasListBox() + { + return fHasListBox.isSet(field_2_bits); + } + +} // END OF CLASS diff --git a/src/scratchpad/src/org/apache/poi/hwpf/sprm/CharacterSprmUncompressor.java b/src/scratchpad/src/org/apache/poi/hwpf/sprm/CharacterSprmUncompressor.java index 6bb3785f4..d3fe6613a 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/sprm/CharacterSprmUncompressor.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/sprm/CharacterSprmUncompressor.java @@ -180,16 +180,13 @@ public final class CharacterSprmUncompressor extends SprmUncompressor case 0x3: // sprmCPicLocation -- 0x6A03 /* - * Microsoft Office Word 97-2007 Binary File Format (.doc) - * Specification + * [MS-DOC] * - * Page 75 of 210 + * Page 104 of 622 * - * sprmCPicLocation (opcode 0x6A03) is used ONLY IN CHPX FKPs. This - * sprm moves the 4-byte operand of the sprm into the chp.fcPic - * field. It simultaneously sets chp.fSpec to 1. This sprm is also - * used when the chp.lTagObj field that is unioned with chp.fcPic is - * to be set for OLE objects. + * A signed 32-bit integer that specifies either the position in the + * Data Stream of a picture or binary data or the name of an OLE + * object storage. */ newCHP.setFcPic( sprm.getOperand() ); newCHP.setFSpec( true ); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/CharacterRun.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/CharacterRun.java index 77ec0ecd3..97875ee24 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/CharacterRun.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/CharacterRun.java @@ -17,8 +17,11 @@ package org.apache.poi.hwpf.usermodel; +import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.model.CHPX; +import org.apache.poi.hwpf.model.FFData; import org.apache.poi.hwpf.model.Ffn; +import org.apache.poi.hwpf.model.NilPICFAndBinData; import org.apache.poi.hwpf.model.StyleSheet; import org.apache.poi.hwpf.sprm.SprmBuffer; @@ -631,4 +634,42 @@ public final class CharacterRun String text = text(); return "CharacterRun of " + text.length() + " characters - " + text; } + + public String[] getDropDownListValues() + { + if ( getDocument() instanceof HWPFDocument ) + { + char c = _text.charAt( _start ); + if ( c == 0x01 ) + { + NilPICFAndBinData data = new NilPICFAndBinData( + ( (HWPFDocument) getDocument() ).getDataStream(), + getPicOffset() ); + FFData ffData = new FFData( data.getBinData(), 0 ); + + String[] values = ffData.getDropList(); + return values; + } + } + return null; + } + + public Integer getDropDownListDefaultItemIndex() + { + if ( getDocument() instanceof HWPFDocument ) + { + char c = _text.charAt( _start ); + if ( c == 0x01 ) + { + NilPICFAndBinData data = new NilPICFAndBinData( + ( (HWPFDocument) getDocument() ).getDataStream(), + getPicOffset() ); + FFData ffData = new FFData( data.getBinData(), 0 ); + + return Integer.valueOf( ffData.getDefaultDropDownItemIndex() ); + } + } + return null; + } + } diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/converter/TestWordToHtmlConverter.java b/src/scratchpad/testcases/org/apache/poi/hwpf/converter/TestWordToHtmlConverter.java index 74be67b28..6d748cd3c 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwpf/converter/TestWordToHtmlConverter.java +++ b/src/scratchpad/testcases/org/apache/poi/hwpf/converter/TestWordToHtmlConverter.java @@ -141,6 +141,14 @@ public class TestWordToHtmlConverter extends TestCase getHtmlText( "Bug48075.doc" ); } + public void testBug52583() throws Exception + { + String result = getHtmlText( "Bug52583.doc" ); + assertContains( + result, + "" ); + } + public void testBug53182() throws Exception { String result = getHtmlText( "Bug53182.doc" ); diff --git a/src/types/definitions/FFData_type.xml b/src/types/definitions/FFData_type.xml new file mode 100644 index 000000000..2016adb6d --- /dev/null +++ b/src/types/definitions/FFData_type.xml @@ -0,0 +1,81 @@ + + + + AbstractType + The FFData structure specifies form field data for a text + box, check box, or drop-down list box. <p>Class and fields + descriptions are quoted from [MS-DOC] -- v20121003 Word (.doc) Binary + File Format; Copyright (c) 2012 Microsoft Corporation; Release: + October 8, 2012 + + Sergey Vladimirov; according to [MS-DOC] -- v20121003 Word + (.doc) Binary File Format; Copyright (c) 2012 Microsoft Corporation; + Release: October 8, 2012 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/types/styles/hdftype.xsl b/src/types/styles/hdftype.xsl index ab923d192..d8c90e752 100644 --- a/src/types/styles/hdftype.xsl +++ b/src/types/styles/hdftype.xsl @@ -352,10 +352,22 @@ public abstract class ? 1231 : 1237 ) - + + + + + + + + + + (int) ( + + ^ ( + + >>> 32 ) ) + @@ -440,27 +452,42 @@ public abstract class ); + - - - - /** - - */ - - - - /**/ - protected final static - - - - = - - ; - - + + + + + + /** + + + /** + + + + */ + + + + + + /* */ + + + /**/ + + + protected final static + + + + = + + ; + + diff --git a/test-data/document/Bug52583.doc b/test-data/document/Bug52583.doc new file mode 100644 index 0000000000000000000000000000000000000000..b6e8646e6e81cfc9b18b3733f6aa9054fb340031 GIT binary patch literal 26112 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 ew1