mirror of https://github.com/apache/poi.git
rewrite Sttb utils to handle complex cases
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1177643 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d81c410757
commit
164fc0c9aa
|
@ -21,12 +21,10 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
@Internal
|
||||
public class BookmarksTables
|
||||
|
@ -120,8 +118,8 @@ public class BookmarksTables
|
|||
int namesLength = fib.getLcbSttbfbkmk();
|
||||
|
||||
if ( namesStart != 0 && namesLength != 0 )
|
||||
this.names = new ArrayList<String>( Arrays.asList( SttbfUtils.read(
|
||||
tableStream, namesStart ) ) );
|
||||
this.names = new ArrayList<String>( Arrays.asList( SttbUtils
|
||||
.readSttbfBkmk( tableStream, namesStart ) ) );
|
||||
|
||||
int firstDescriptorsStart = fib.getFcPlcfbkf();
|
||||
int firstDescriptorsLength = fib.getLcbPlcfbkf();
|
||||
|
@ -196,8 +194,8 @@ public class BookmarksTables
|
|||
}
|
||||
|
||||
int start = tableStream.getOffset();
|
||||
SttbfUtils
|
||||
.write( tableStream, names.toArray( new String[names.size()] ) );
|
||||
SttbUtils.writeSttbfBkmk( names.toArray( new String[names.size()] ),
|
||||
tableStream );
|
||||
int end = tableStream.getOffset();
|
||||
|
||||
fib.setFcSttbfbkmk( start );
|
||||
|
|
|
@ -24,8 +24,6 @@ import java.util.List;
|
|||
|
||||
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;
|
||||
|
||||
/**
|
||||
* String table containing the names of authors of revision marks, e-mails and
|
||||
|
@ -35,21 +33,6 @@ import org.apache.poi.util.StringUtil;
|
|||
*/
|
||||
@Internal
|
||||
public final class RevisionMarkAuthorTable {
|
||||
/**
|
||||
* must be 0xFFFF
|
||||
*/
|
||||
private short fExtend = (short) 0xFFFF;
|
||||
|
||||
/**
|
||||
* the number of entries in the table
|
||||
*/
|
||||
private short cData = 0;
|
||||
|
||||
/**
|
||||
* must be 0
|
||||
*/
|
||||
private short cbExtra = 0;
|
||||
|
||||
/**
|
||||
* Array of entries.
|
||||
*/
|
||||
|
@ -62,36 +45,11 @@ public final class RevisionMarkAuthorTable {
|
|||
* @param offset the offset into the byte array.
|
||||
* @param size the size of the table in the byte array.
|
||||
*/
|
||||
public RevisionMarkAuthorTable(byte[] tableStream, int offset, int size) throws IOException {
|
||||
// Read fExtend - it isn't used
|
||||
fExtend = LittleEndian.getShort(tableStream, offset);
|
||||
if(fExtend != 0xFFFF) {
|
||||
//TODO: throw an exception here?
|
||||
}
|
||||
offset += 2;
|
||||
|
||||
// Read the number of entries
|
||||
cData = LittleEndian.getShort(tableStream, offset);
|
||||
offset += 2;
|
||||
|
||||
// Read cbExtra - it isn't used
|
||||
cbExtra = LittleEndian.getShort(tableStream, offset);
|
||||
if(cbExtra != 0) {
|
||||
//TODO: throw an exception here?
|
||||
}
|
||||
offset += 2;
|
||||
|
||||
entries = new String[cData];
|
||||
for (int i = 0; i < cData; i++) {
|
||||
int len = LittleEndian.getShort(tableStream, offset);
|
||||
offset += 2;
|
||||
|
||||
String name = StringUtil.getFromUnicodeLE(tableStream, offset, len);
|
||||
offset += len * 2;
|
||||
|
||||
entries[i] = name;
|
||||
}
|
||||
}
|
||||
public RevisionMarkAuthorTable( byte[] tableStream, int offset, int size )
|
||||
throws IOException
|
||||
{
|
||||
entries = SttbUtils.readSttbfRMark( tableStream, offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entries. The returned list cannot be modified.
|
||||
|
@ -121,7 +79,7 @@ public final class RevisionMarkAuthorTable {
|
|||
* @return the number of entries.
|
||||
*/
|
||||
public int getSize() {
|
||||
return cData;
|
||||
return entries.length;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,19 +88,9 @@ public final class RevisionMarkAuthorTable {
|
|||
* @param tableStream the table stream to write to.
|
||||
* @throws IOException if an error occurs while writing.
|
||||
*/
|
||||
public void writeTo(HWPFOutputStream tableStream) throws IOException {
|
||||
byte[] header = new byte[6];
|
||||
LittleEndian.putShort(header, 0, fExtend);
|
||||
LittleEndian.putShort(header, 2, cData);
|
||||
LittleEndian.putShort(header, 4, cbExtra);
|
||||
tableStream.write(header);
|
||||
|
||||
for (String name : entries) {
|
||||
byte[] buf = new byte[name.length() * 2 + 2];
|
||||
LittleEndian.putShort(buf, 0, (short) name.length());
|
||||
StringUtil.putUnicodeLE(name, buf, 2);
|
||||
tableStream.write(buf);
|
||||
}
|
||||
}
|
||||
public void writeTo( HWPFOutputStream tableStream ) throws IOException
|
||||
{
|
||||
SttbUtils.writeSttbfRMark( entries, tableStream );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hwpf.model;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -26,55 +25,32 @@ import org.apache.poi.hwpf.model.io.HWPFOutputStream;
|
|||
import org.apache.poi.util.Internal;
|
||||
|
||||
/**
|
||||
* String table containing the history of the last few revisions ("saves") of the document.
|
||||
* Read-only for the time being.
|
||||
*
|
||||
* String table containing the history of the last few revisions ("saves") of
|
||||
* the document. Read-only for the time being.
|
||||
*
|
||||
* @author Daniel Noll
|
||||
*/
|
||||
@Internal
|
||||
public final class SavedByTable
|
||||
{
|
||||
/**
|
||||
* Array of entries.
|
||||
*/
|
||||
private SavedByEntry[] entries;
|
||||
|
||||
/**
|
||||
* Array of entries.
|
||||
*/
|
||||
private SavedByEntry[] entries;
|
||||
|
||||
/**
|
||||
* Constructor to read the table from the table stream.
|
||||
*
|
||||
* @param tableStream the table stream.
|
||||
* @param offset the offset into the byte array.
|
||||
* @param size the size of the table in the byte array.
|
||||
*/
|
||||
public SavedByTable(byte[] tableStream, int offset, int size)
|
||||
{
|
||||
// // Read the value that I don't know what it does. :-)
|
||||
// unknownValue = LittleEndian.getShort(tableStream, offset);
|
||||
// offset += 2;
|
||||
//
|
||||
// // The stored int is the number of strings, and there are two strings per entry.
|
||||
// int numEntries = LittleEndian.getInt(tableStream, offset) / 2;
|
||||
// offset += 4;
|
||||
//
|
||||
// entries = new SavedByEntry[numEntries];
|
||||
// for (int i = 0; i < numEntries; i++)
|
||||
// {
|
||||
// int len = LittleEndian.getShort(tableStream, offset);
|
||||
// offset += 2;
|
||||
// String userName = StringUtil.getFromUnicodeLE(tableStream, offset, len);
|
||||
// offset += len * 2;
|
||||
// len = LittleEndian.getShort(tableStream, offset);
|
||||
// offset += 2;
|
||||
// String saveLocation = StringUtil.getFromUnicodeLE(tableStream, offset, len);
|
||||
// offset += len * 2;
|
||||
//
|
||||
// entries[i] = new SavedByEntry(userName, saveLocation);
|
||||
// }
|
||||
|
||||
// first value is mark for extended STTBF ;) -- sergey
|
||||
String[] strings = SttbfUtils.read( tableStream, offset );
|
||||
|
||||
/**
|
||||
* Constructor to read the table from the table stream.
|
||||
*
|
||||
* @param tableStream
|
||||
* the table stream.
|
||||
* @param offset
|
||||
* the offset into the byte array.
|
||||
* @param size
|
||||
* the size of the table in the byte array.
|
||||
*/
|
||||
public SavedByTable( byte[] tableStream, int offset, int size )
|
||||
{
|
||||
String[] strings = SttbUtils.readSttbSavedBy( tableStream, offset );
|
||||
int numEntries = strings.length / 2;
|
||||
entries = new SavedByEntry[numEntries];
|
||||
for ( int i = 0; i < numEntries; i++ )
|
||||
|
@ -83,15 +59,15 @@ public final class SavedByTable
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entries. The returned list cannot be modified.
|
||||
*
|
||||
* @return the list of entries.
|
||||
*/
|
||||
public List<SavedByEntry> getEntries()
|
||||
{
|
||||
return Collections.unmodifiableList(Arrays.asList(entries));
|
||||
}
|
||||
/**
|
||||
* Gets the entries. The returned list cannot be modified.
|
||||
*
|
||||
* @return the list of entries.
|
||||
*/
|
||||
public List<SavedByEntry> getEntries()
|
||||
{
|
||||
return Collections.unmodifiableList( Arrays.asList( entries ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes this table to the table stream.
|
||||
|
@ -110,7 +86,7 @@ public final class SavedByTable
|
|||
toSave[counter++] = entry.getUserName();
|
||||
toSave[counter++] = entry.getSaveLocation();
|
||||
}
|
||||
SttbfUtils.write( tableStream, toSave );
|
||||
SttbUtils.writeSttbSavedBy( toSave, tableStream );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
package org.apache.poi.hwpf.model;
|
||||
|
||||
import java.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"
|
||||
*
|
||||
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
||||
*/
|
||||
@Internal
|
||||
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;
|
||||
}
|
||||
|
||||
static String[] readSttbfRMark( byte[] buffer, int startOffset )
|
||||
{
|
||||
return read( CDATA_SIZE_STTBF_R_MARK, buffer, startOffset ).data;
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
package org.apache.poi.hwpf.model;
|
||||
|
||||
import java.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 for storing and reading "STring TaBle stored in File"
|
||||
*
|
||||
* @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
|
||||
*/
|
||||
@Internal
|
||||
class SttbfUtils
|
||||
{
|
||||
public static String[] read( byte[] data, int startOffset )
|
||||
{
|
||||
short ffff = LittleEndian.getShort( data, startOffset );
|
||||
|
||||
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 offset = startOffset + 2;
|
||||
int numEntries = LittleEndian.getInt( data, offset );
|
||||
offset += 4;
|
||||
|
||||
String[] entries = new String[numEntries];
|
||||
for ( int i = 0; i < numEntries; i++ )
|
||||
{
|
||||
int len = LittleEndian.getShort( data, offset );
|
||||
offset += 2;
|
||||
String value = StringUtil.getFromUnicodeLE( data, offset, len );
|
||||
offset += len * 2;
|
||||
entries[i] = value;
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static int write( HWPFOutputStream tableStream, String[] entries )
|
||||
throws IOException
|
||||
{
|
||||
byte[] header = new byte[6];
|
||||
LittleEndian.putShort( header, 0, (short) 0xffff );
|
||||
|
||||
if ( entries == null || entries.length == 0 )
|
||||
{
|
||||
LittleEndian.putInt( header, 2, 0 );
|
||||
tableStream.write( header );
|
||||
return 6;
|
||||
}
|
||||
|
||||
LittleEndian.putInt( header, 2, entries.length );
|
||||
tableStream.write( header );
|
||||
int size = 6;
|
||||
|
||||
for ( String entry : entries )
|
||||
{
|
||||
byte[] buf = new byte[entry.length() * 2 + 2];
|
||||
LittleEndian.putShort( buf, 0, (short) entry.length() );
|
||||
StringUtil.putUnicodeLE( entry, buf, 2 );
|
||||
tableStream.write( buf );
|
||||
size += buf.length;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue