From a754ae3ea62ead35a42c39c8f5f1b1d94c9f3b94 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sun, 26 Aug 2007 15:03:13 +0000 Subject: [PATCH] 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 --- .../poi/hssf/record/RecordInputStream.java | 14 +- .../poi/hssf/record/TextObjectRecord.java | 95 +++++++++----- .../poi/hssf/record/TestTextObjectRecord.java | 120 ++++++++++++++++++ 3 files changed, 191 insertions(+), 38 deletions(-) create mode 100644 src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java index fc861f3900..399e0f566d 100755 --- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java +++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java @@ -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 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(); diff --git a/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java b/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java new file mode 100644 index 0000000000..63b9db356f --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java @@ -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()); + } + + } +} -- 2.39.5