]> source.dussan.org Git - poi.git/commitdiff
fixed: Bug 43088: Excel file can't be loaded if comments exceed a size of 4111 characters
authorYegor Kozlov <yegor@apache.org>
Sun, 26 Aug 2007 15:03:13 +0000 (15:03 +0000)
committerYegor Kozlov <yegor@apache.org>
Sun, 26 Aug 2007 15:03:13 +0000 (15:03 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@569821 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/record/RecordInputStream.java
src/java/org/apache/poi/hssf/record/TextObjectRecord.java
src/testcases/org/apache/poi/hssf/record/TestTextObjectRecord.java [new file with mode: 0644]

index fc861f39004e7796d9a1a9559821ae1ede2f05c0..399e0f566d52a17f725f60e24937a1e9f4294a3b 100755 (executable)
@@ -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)
index 0239434614b2fd36ba90a5934352d0e342caf0ad..97685c9ca21bc7e133b15a08c7bdd6a0eef8cef8 100644 (file)
@@ -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();
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 (file)
index 0000000..63b9db3
--- /dev/null
@@ -0,0 +1,120 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hssf.record;\r
+\r
+import junit.framework.*;\r
+\r
+import java.util.Arrays;\r
+import java.util.List;\r
+import java.io.ByteArrayInputStream;\r
+\r
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;\r
+\r
+/**\r
+ * Tests that serialization and deserialization of the TextObjectRecord .\r
+ * Test data taken directly from a real Excel file.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestTextObjectRecord extends TestCase {\r
+\r
+    byte[] data = {(byte)0xB6, 0x01, 0x12, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00,\r
+                   0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,\r
+                   0x00, 0x3C, 0x00, 0x1B, 0x00, 0x01, 0x48, 0x00, 0x65, 0x00, 0x6C,\r
+                   0x00, 0x6C, 0x00, 0x6F, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x57, 0x00,\r
+                   0x6F, 0x00, 0x72, 0x00, 0x6C, 0x00, 0x64, 0x00, 0x21, 0x00, 0x3C,\r
+                   0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };\r
+\r
+\r
+    public void testRead()\r
+            throws Exception\r
+    {\r
+\r
+\r
+        RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));\r
+        is.nextRecord();\r
+        TextObjectRecord record = new TextObjectRecord(is);\r
+\r
+        assertEquals(TextObjectRecord.sid, record.getSid());\r
+        record.validateSid(TextObjectRecord.sid);\r
+        assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED, record.getHorizontalTextAlignment());\r
+        assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP, record.getVerticalTextAlignment());\r
+        assertEquals(TextObjectRecord.TEXT_ORIENTATION_NONE, record.getTextOrientation());\r
+        assertEquals(0, record.getReserved7());\r
+        assertEquals("Hello, World!", record.getStr().getString());\r
+\r
+    }\r
+\r
+    public void testWrite()\r
+    {\r
+        HSSFRichTextString str = new HSSFRichTextString("Hello, World!");\r
+\r
+        TextObjectRecord record = new TextObjectRecord();\r
+        int frLength = ( str.numFormattingRuns() + 1 ) * 8;\r
+        record.setFormattingRunLength( (short) frLength );\r
+        record.setTextLength( (short) str.length() );\r
+        record.setStr( str );\r
+        record.setHorizontalTextAlignment( TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED );\r
+        record.setVerticalTextAlignment( TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP );\r
+        record.setTextLocked( true );\r
+        record.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );\r
+        record.setReserved7( 0 );\r
+\r
+        byte [] ser = record.serialize();\r
+        assertEquals(ser.length , data.length);\r
+\r
+        assertTrue(Arrays.equals(data, ser));\r
+\r
+        //read again\r
+        RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));\r
+        is.nextRecord();\r
+        record = new TextObjectRecord(is);\r
+\r
+    }\r
+\r
+    /**\r
+     * Test that TextObjectRecord serializes logs records properly.\r
+     */\r
+    public void testLongRecords() {\r
+        int[] length = {1024, 2048, 4096, 8192, 16384}; //test against strings of different length\r
+        for (int i = 0; i < length.length; i++) {\r
+            StringBuffer buff = new StringBuffer(length[i]);\r
+            for (int j = 0; j < length[i]; j++) {\r
+                buff.append("x");\r
+            }\r
+            HSSFRichTextString str = new HSSFRichTextString(buff.toString());\r
+\r
+            TextObjectRecord obj = new TextObjectRecord();\r
+            int frLength = ( str.numFormattingRuns() + 1 ) * 8;\r
+            obj.setFormattingRunLength( (short) frLength );\r
+            obj.setTextLength( (short) str.length() );\r
+            obj.setStr( str );\r
+\r
+            byte [] data = obj.serialize();\r
+            RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));\r
+            is.nextRecord();\r
+            TextObjectRecord record = new TextObjectRecord(is);\r
+            str = record.getStr();\r
+\r
+            assertEquals(buff.length(), str.length());\r
+            assertEquals(buff.toString(), str.getString());\r
+        }\r
+\r
+    }\r
+}\r