You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ye...@apache.org on 2007/08/26 17:03:14 UTC

svn commit: r569821 - in /poi/trunk/src: java/org/apache/poi/hssf/record/RecordInputStream.java java/org/apache/poi/hssf/record/TextObjectRecord.java testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java

Author: yegor
Date: Sun Aug 26 08:03:13 2007
New Revision: 569821

URL: http://svn.apache.org/viewvc?rev=569821&view=rev
Log:
fixed: Bug 43088: Excel file can't be loaded if comments exceed a size of 4111 characters

Added:
    poi/trunk/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java
Modified:
    poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java
    poi/trunk/src/java/org/apache/poi/hssf/record/TextObjectRecord.java

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java?rev=569821&r1=569820&r2=569821&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/RecordInputStream.java Sun Aug 26 08:03:13 2007
@@ -230,11 +230,14 @@
     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 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)

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/TextObjectRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/TextObjectRecord.java?rev=569821&r1=569820&r2=569821&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/TextObjectRecord.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/TextObjectRecord.java Sun Aug 26 08:03:13 2007
@@ -21,6 +21,7 @@
 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 @@
 
     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 @@
         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 @@
         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;
@@ -98,23 +142,6 @@
         if ( bytesWritten1 != getRecordSize() )
             throw new RecordFormatException(bytesWritten1 + " bytes written but getRecordSize() reports " + getRecordSize());
         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()

Added: poi/trunk/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java?rev=569821&view=auto
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java (added)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java Sun Aug 26 08:03:13 2007
@@ -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());
+        }
+
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org