fixed: Bug 43088: Excel file can't be loaded if comments exceed a size of 4111 characters
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@569821 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3d40a25d36
commit
22350f3b21
@ -230,11 +230,14 @@ public class RecordInputStream extends InputStream
|
||||
if ((length < 0) || (((remaining() / 2) < length) && !isContinueNext())) {
|
||||
throw new IllegalArgumentException("Illegal length");
|
||||
}
|
||||
|
||||
|
||||
StringBuffer buf = new StringBuffer(length);
|
||||
for (int i=0;i<length;i++) {
|
||||
if ((remaining() == 0) && (isContinueNext()))
|
||||
if ((remaining() == 0) && (isContinueNext())){
|
||||
nextRecord();
|
||||
int compressByte = readByte();
|
||||
if(compressByte != 1) throw new IllegalArgumentException("compressByte in continue records must be 1 while reading unicode LE string");
|
||||
}
|
||||
char ch = (char)readShort();
|
||||
buf.append(ch);
|
||||
}
|
||||
@ -242,14 +245,17 @@ public class RecordInputStream extends InputStream
|
||||
}
|
||||
|
||||
public String readCompressedUnicode(int length) {
|
||||
if ((length < 0) || (remaining() < length)) {
|
||||
if ((length < 0) || ((remaining() < length) && !isContinueNext())) {
|
||||
throw new IllegalArgumentException("Illegal length");
|
||||
}
|
||||
|
||||
StringBuffer buf = new StringBuffer(length);
|
||||
for (int i=0;i<length;i++) {
|
||||
if ((remaining() == 0) && (isContinueNext()))
|
||||
if ((remaining() == 0) && (isContinueNext())) {
|
||||
nextRecord();
|
||||
int compressByte = readByte();
|
||||
if(compressByte != 0) throw new IllegalArgumentException("compressByte in continue records must be 0 while reading compressed unicode");
|
||||
}
|
||||
byte b = readByte();
|
||||
//Typecast direct to char from byte with high bit set causes all ones
|
||||
//in the high byte of the char (which is of course incorrect)
|
||||
|
@ -21,6 +21,7 @@ import org.apache.poi.hssf.usermodel.HSSFRichTextString;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.HexDump;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
public class TextObjectRecord
|
||||
extends TextObjectBaseRecord
|
||||
@ -40,21 +41,21 @@ public class TextObjectRecord
|
||||
|
||||
protected void fillFields(RecordInputStream in)
|
||||
{
|
||||
super.fillFields(in);
|
||||
if (getTextLength() > 0) {
|
||||
if (in.isContinueNext() && in.remaining() == 0) {
|
||||
//1st Continue
|
||||
in.nextRecord();
|
||||
processRawString(in);
|
||||
} else
|
||||
throw new RecordFormatException("Expected Continue record to hold string data for TextObjectRecord");
|
||||
}
|
||||
if (getFormattingRunLength() > 0) {
|
||||
super.fillFields(in);
|
||||
if (getTextLength() > 0) {
|
||||
if (in.isContinueNext() && in.remaining() == 0) {
|
||||
in.nextRecord();
|
||||
processFontRuns(in);
|
||||
} else throw new RecordFormatException("Expected Continue Record to hold font runs for TextObjectRecord");
|
||||
}
|
||||
//1st Continue
|
||||
in.nextRecord();
|
||||
processRawString(in);
|
||||
} else
|
||||
throw new RecordFormatException("Expected Continue record to hold string data for TextObjectRecord");
|
||||
}
|
||||
if (getFormattingRunLength() > 0) {
|
||||
if (in.isContinueNext() && in.remaining() == 0) {
|
||||
in.nextRecord();
|
||||
processFontRuns(in);
|
||||
} else throw new RecordFormatException("Expected Continue Record to hold font runs for TextObjectRecord");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -64,7 +65,15 @@ public class TextObjectRecord
|
||||
int continue2Size = 0;
|
||||
if (str.length() != 0)
|
||||
{
|
||||
continue1Size = str.length() * 2 + 1 + 4;
|
||||
int length = str.length() * 2;
|
||||
while(length > 0){
|
||||
int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE-2, length);
|
||||
length -= chunkSize;
|
||||
|
||||
continue1Size += chunkSize;
|
||||
continue1Size += 1 + 4;
|
||||
}
|
||||
|
||||
continue2Size = (str.numFormattingRuns() + 1) * 8 + 4;
|
||||
}
|
||||
return super.getRecordSize() + continue1Size + continue2Size;
|
||||
@ -83,9 +92,44 @@ public class TextObjectRecord
|
||||
int pos = offset + bytesWritten1;
|
||||
if ( str.getString().equals( "" ) == false )
|
||||
{
|
||||
ContinueRecord c1 = createContinue1();
|
||||
ContinueRecord c2 = createContinue2();
|
||||
int bytesWritten2 = c1.serialize( pos, data );
|
||||
int bytesWritten2 = 0;
|
||||
|
||||
try
|
||||
{
|
||||
byte[] c1Data = str.getString().getBytes( "UTF-16LE" );
|
||||
int length = c1Data.length;
|
||||
|
||||
int charsWritten = 0;
|
||||
int spos = pos;
|
||||
while(length > 0){
|
||||
int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE-2 , length);
|
||||
length -= chunkSize;
|
||||
|
||||
//continue header
|
||||
LittleEndian.putShort(data, spos, ContinueRecord.sid);
|
||||
spos += LittleEndian.SHORT_SIZE;
|
||||
LittleEndian.putShort(data, spos, (short)(chunkSize+1));
|
||||
spos += LittleEndian.SHORT_SIZE;
|
||||
|
||||
//The first byte specifies if the text is compressed unicode or unicode.
|
||||
//(regardless what was read, we always serialize double-byte unicode characters (UTF-16LE).
|
||||
data[spos] = 1;
|
||||
spos += LittleEndian.BYTE_SIZE;
|
||||
|
||||
//copy characters data
|
||||
System.arraycopy(c1Data, charsWritten, data, spos, chunkSize);
|
||||
spos += chunkSize;
|
||||
charsWritten += chunkSize;
|
||||
}
|
||||
|
||||
bytesWritten2 = (spos-pos);
|
||||
}
|
||||
catch ( UnsupportedEncodingException e )
|
||||
{
|
||||
throw new RuntimeException( e.getMessage(), e );
|
||||
}
|
||||
|
||||
pos += bytesWritten2;
|
||||
int bytesWritten3 = c2.serialize( pos, data );
|
||||
pos += bytesWritten3;
|
||||
@ -100,23 +144,6 @@ public class TextObjectRecord
|
||||
return bytesWritten1;
|
||||
}
|
||||
|
||||
private ContinueRecord createContinue1()
|
||||
{
|
||||
ContinueRecord c1 = new ContinueRecord();
|
||||
byte[] c1Data = new byte[str.length() * 2 + 1];
|
||||
try
|
||||
{
|
||||
c1Data[0] = 1;
|
||||
System.arraycopy( str.getString().getBytes( "UTF-16LE" ), 0, c1Data, 1, str.length() * 2 );
|
||||
}
|
||||
catch ( UnsupportedEncodingException e )
|
||||
{
|
||||
throw new RuntimeException( e.getMessage() );
|
||||
}
|
||||
c1.setData( c1Data );
|
||||
return c1;
|
||||
}
|
||||
|
||||
private ContinueRecord createContinue2()
|
||||
{
|
||||
ContinueRecord c2 = new ContinueRecord();
|
||||
|
@ -0,0 +1,120 @@
|
||||
|
||||
/* ====================================================================
|
||||
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.hssf.record;
|
||||
|
||||
import junit.framework.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
|
||||
|
||||
/**
|
||||
* Tests that serialization and deserialization of the TextObjectRecord .
|
||||
* Test data taken directly from a real Excel file.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestTextObjectRecord extends TestCase {
|
||||
|
||||
byte[] data = {(byte)0xB6, 0x01, 0x12, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x3C, 0x00, 0x1B, 0x00, 0x01, 0x48, 0x00, 0x65, 0x00, 0x6C,
|
||||
0x00, 0x6C, 0x00, 0x6F, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x57, 0x00,
|
||||
0x6F, 0x00, 0x72, 0x00, 0x6C, 0x00, 0x64, 0x00, 0x21, 0x00, 0x3C,
|
||||
0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
|
||||
public void testRead()
|
||||
throws Exception
|
||||
{
|
||||
|
||||
|
||||
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));
|
||||
is.nextRecord();
|
||||
TextObjectRecord record = new TextObjectRecord(is);
|
||||
|
||||
assertEquals(TextObjectRecord.sid, record.getSid());
|
||||
record.validateSid(TextObjectRecord.sid);
|
||||
assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED, record.getHorizontalTextAlignment());
|
||||
assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP, record.getVerticalTextAlignment());
|
||||
assertEquals(TextObjectRecord.TEXT_ORIENTATION_NONE, record.getTextOrientation());
|
||||
assertEquals(0, record.getReserved7());
|
||||
assertEquals("Hello, World!", record.getStr().getString());
|
||||
|
||||
}
|
||||
|
||||
public void testWrite()
|
||||
{
|
||||
HSSFRichTextString str = new HSSFRichTextString("Hello, World!");
|
||||
|
||||
TextObjectRecord record = new TextObjectRecord();
|
||||
int frLength = ( str.numFormattingRuns() + 1 ) * 8;
|
||||
record.setFormattingRunLength( (short) frLength );
|
||||
record.setTextLength( (short) str.length() );
|
||||
record.setStr( str );
|
||||
record.setHorizontalTextAlignment( TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED );
|
||||
record.setVerticalTextAlignment( TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP );
|
||||
record.setTextLocked( true );
|
||||
record.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );
|
||||
record.setReserved7( 0 );
|
||||
|
||||
byte [] ser = record.serialize();
|
||||
assertEquals(ser.length , data.length);
|
||||
|
||||
assertTrue(Arrays.equals(data, ser));
|
||||
|
||||
//read again
|
||||
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));
|
||||
is.nextRecord();
|
||||
record = new TextObjectRecord(is);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that TextObjectRecord serializes logs records properly.
|
||||
*/
|
||||
public void testLongRecords() {
|
||||
int[] length = {1024, 2048, 4096, 8192, 16384}; //test against strings of different length
|
||||
for (int i = 0; i < length.length; i++) {
|
||||
StringBuffer buff = new StringBuffer(length[i]);
|
||||
for (int j = 0; j < length[i]; j++) {
|
||||
buff.append("x");
|
||||
}
|
||||
HSSFRichTextString str = new HSSFRichTextString(buff.toString());
|
||||
|
||||
TextObjectRecord obj = new TextObjectRecord();
|
||||
int frLength = ( str.numFormattingRuns() + 1 ) * 8;
|
||||
obj.setFormattingRunLength( (short) frLength );
|
||||
obj.setTextLength( (short) str.length() );
|
||||
obj.setStr( str );
|
||||
|
||||
byte [] data = obj.serialize();
|
||||
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));
|
||||
is.nextRecord();
|
||||
TextObjectRecord record = new TextObjectRecord(is);
|
||||
str = record.getStr();
|
||||
|
||||
assertEquals(buff.length(), str.length());
|
||||
assertEquals(buff.toString(), str.getString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user