Fix for bug 45964 - support for link formulas in Text Objects

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@703302 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-10-10 00:40:58 +00:00
parent f7907b3e5f
commit b86b4b5ef2
11 changed files with 611 additions and 830 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! -->
<release version="3.2-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
<action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
<action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
<action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.2-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45964 - support for link formulas in Text Objects</action>
<action dev="POI-DEVELOPERS" type="fix">43354 - support for evalating formulas with missing args</action>
<action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
<action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>

View File

@ -138,15 +138,11 @@ public class TextboxShape
HSSFTextbox shape = hssfShape;
TextObjectRecord obj = new TextObjectRecord();
obj.setHorizontalTextAlignment( hssfShape.getHorizontalAlignment() );
obj.setVerticalTextAlignment( hssfShape.getVerticalAlignment());
obj.setTextLocked( true );
obj.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );
int frLength = ( shape.getString().numFormattingRuns() + 1 ) * 8;
obj.setFormattingRunLength( (short) frLength );
obj.setTextLength( (short) shape.getString().length() );
obj.setStr( shape.getString() );
obj.setReserved7( 0 );
obj.setHorizontalTextAlignment(hssfShape.getHorizontalAlignment());
obj.setVerticalTextAlignment(hssfShape.getVerticalAlignment());
obj.setTextLocked(true);
obj.setTextOrientation(TextObjectRecord.TEXT_ORIENTATION_NONE);
obj.setStr(shape.getString());
return obj;
}

View File

@ -22,76 +22,44 @@ package org.apache.poi.hssf.record;
import org.apache.poi.util.LittleEndian;
/**
* Title: Continue Record - Helper class used primarily for SST Records <P>
* Title: Continue Record(0x003C) - Helper class used primarily for SST Records <P>
* Description: handles overflow for prior record in the input
* stream; content is tailored to that prior record<P>
* @author Marc Johnson (mjohnson at apache dot org)
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Csaba Nagy (ncsaba at yahoo dot com)
* @version 2.0-pre
*/
public class ContinueRecord
extends Record
{
public final class ContinueRecord extends Record {
public final static short sid = 0x003C;
private byte[] field_1_data;
private byte[] _data;
/**
* default constructor
*/
public ContinueRecord()
{
public ContinueRecord(byte[] data) {
_data = data;
}
/**
* USE ONLY within "processContinue"
*/
public byte [] serialize()
{
byte[] retval = new byte[ field_1_data.length + 4 ];
byte[] retval = new byte[ _data.length + 4 ];
serialize(0, retval);
return retval;
}
public int serialize(int offset, byte [] data)
{
LittleEndian.putShort(data, offset, sid);
LittleEndian.putShort(data, offset + 2, ( short ) field_1_data.length);
System.arraycopy(field_1_data, 0, data, offset + 4, field_1_data.length);
return field_1_data.length + 4;
// throw new RecordFormatException(
// "You're not supposed to serialize Continue records like this directly");
}
/*
* @param data raw data
*/
public void setData(byte [] data)
{
field_1_data = data;
public int serialize(int offset, byte[] data) {
return write(data, offset, null, _data);
}
/**
* get the data for continuation
* @return byte array containing all of the continued data
*/
public byte [] getData()
{
return field_1_data;
return _data;
}
/**
* Debugging toString
*
* @return string representation
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
@ -113,19 +81,36 @@ public class ContinueRecord
*
* @param in the RecordInputstream to read the record from
*/
public ContinueRecord(RecordInputStream in)
{
field_1_data = in.readRemainder();
_data = in.readRemainder();
}
public Object clone() {
return new ContinueRecord(_data);
}
/**
* Clone this record.
* Writes the full encoding of a Continue record without making an instance
*/
public Object clone() {
ContinueRecord clone = new ContinueRecord();
clone.setData(field_1_data);
return clone;
public static int write(byte[] destBuf, int destOffset, Byte initialDataByte, byte[] srcData) {
return write(destBuf, destOffset, initialDataByte, srcData, 0, srcData.length);
}
/**
* @param initialDataByte (optional - often used for unicode flag).
* If supplied, this will be written before srcData
* @return the total number of bytes written
*/
public static int write(byte[] destBuf, int destOffset, Byte initialDataByte, byte[] srcData, int srcOffset, int len) {
int totalLen = len + (initialDataByte == null ? 0 : 1);
LittleEndian.putUShort(destBuf, destOffset, sid);
LittleEndian.putUShort(destBuf, destOffset + 2, totalLen);
int pos = destOffset + 4;
if (initialDataByte != null) {
LittleEndian.putByte(destBuf, pos, initialDataByte.byteValue());
pos += 1;
}
System.arraycopy(srcData, srcOffset, destBuf, pos, len);
return 4 + totalLen;
}
}

View File

@ -321,51 +321,54 @@ public final class RecordFactory {
Record lastRecord = null;
while (recStream.hasNextRecord()) {
recStream.nextRecord();
if (recStream.getSid() != 0) {
Record[] recs = createRecord(recStream); // handle MulRK records
if (recStream.getSid() == 0) {
// After EOF, Excel seems to pad block with zeros
continue;
}
Record[] recs = createRecord(recStream); // handle MulRK records
if (recs.length > 1) {
for (int k = 0; k < recs.length; k++) {
records.add(recs[ k ]); // these will be number records
}
} else {
Record record = recs[ 0 ];
if (record != null) {
if (record.getSid() == DrawingGroupRecord.sid
&& lastRecord instanceof DrawingGroupRecord) {
DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
lastDGRecord.join((AbstractEscherHolderRecord) record);
} else if (record.getSid() == ContinueRecord.sid &&
((lastRecord instanceof ObjRecord) || (lastRecord instanceof TextObjectRecord))) {
// Drawing records have a very strange continue behaviour.
//There can actually be OBJ records mixed between the continues.
lastDrawingRecord.processContinueRecord( ((ContinueRecord)record).getData() );
//we must remember the position of the continue record.
//in the serialization procedure the original structure of records must be preserved
records.add(record);
} else if (record.getSid() == ContinueRecord.sid &&
(lastRecord instanceof DrawingGroupRecord)) {
((DrawingGroupRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
} else if (record.getSid() == ContinueRecord.sid &&
(lastRecord instanceof StringRecord)) {
((StringRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
} else if (record.getSid() == ContinueRecord.sid) {
if (lastRecord instanceof UnknownRecord) {
//Gracefully handle records that we dont know about,
//that happen to be continued
records.add(record);
} else
throw new RecordFormatException("Unhandled Continue Record");
} else {
lastRecord = record;
if (record instanceof DrawingRecord) {
lastDrawingRecord = (DrawingRecord) record;
}
records.add(record);
}
}
if (recs.length > 1) {
for (int k = 0; k < recs.length; k++) {
records.add(recs[ k ]); // these will be number records
}
continue;
}
Record record = recs[ 0 ];
if (record == null) {
continue;
}
if (record.getSid() == DrawingGroupRecord.sid
&& lastRecord instanceof DrawingGroupRecord) {
DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
lastDGRecord.join((AbstractEscherHolderRecord) record);
} else if (record.getSid() == ContinueRecord.sid) {
ContinueRecord contRec = (ContinueRecord)record;
if (lastRecord instanceof ObjRecord || lastRecord instanceof TextObjectRecord) {
// Drawing records have a very strange continue behaviour.
//There can actually be OBJ records mixed between the continues.
lastDrawingRecord.processContinueRecord(contRec.getData() );
//we must remember the position of the continue record.
//in the serialization procedure the original structure of records must be preserved
records.add(record);
} else if (lastRecord instanceof DrawingGroupRecord) {
((DrawingGroupRecord)lastRecord).processContinueRecord(contRec.getData());
} else if (lastRecord instanceof StringRecord) {
((StringRecord)lastRecord).processContinueRecord(contRec.getData());
} else if (lastRecord instanceof UnknownRecord) {
//Gracefully handle records that we don't know about,
//that happen to be continued
records.add(record);
} else {
throw new RecordFormatException("Unhandled Continue Record");
}
} else {
lastRecord = record;
if (record instanceof DrawingRecord) {
lastDrawingRecord = (DrawingRecord) record;
}
records.add(record);
}
}
return records;

View File

@ -1,427 +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.hssf.record;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
/**
* The TXO record is used to define the properties of a text box. It is followed
* by two continue records unless there is no actual text. The first continue record contains
* the text data and the next continue record contains the formatting runs.<p/>
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class TextObjectBaseRecord extends Record {
// TODO - don't instantiate superclass
public final static short sid = 0x01B6;
private static final BitField reserved1 = BitFieldFactory.getInstance(0x0001);
private static final BitField HorizontalTextAlignment = BitFieldFactory.getInstance(0x000E);
private static final BitField VerticalTextAlignment = BitFieldFactory.getInstance(0x0070);
private static final BitField reserved2 = BitFieldFactory.getInstance(0x0180);
private static final BitField textLocked = BitFieldFactory.getInstance(0x0200);
private static final BitField reserved3 = BitFieldFactory.getInstance(0xFC00);
private short field_1_options;
public final static short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
public final static short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
public final static short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
public final static short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
public final static short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
public final static short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
public final static short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
public final static short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
private short field_2_textOrientation;
public final static short TEXT_ORIENTATION_NONE = 0;
public final static short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
public final static short TEXT_ORIENTATION_ROT_RIGHT = 2;
public final static short TEXT_ORIENTATION_ROT_LEFT = 3;
private short field_3_reserved4;
private short field_4_reserved5;
private short field_5_reserved6;
private short field_6_textLength;
private short field_7_formattingRunLength;
private int field_8_reserved7;
public TextObjectBaseRecord()
{
}
public TextObjectBaseRecord(RecordInputStream in)
{
field_1_options = in.readShort();
field_2_textOrientation = in.readShort();
field_3_reserved4 = in.readShort();
field_4_reserved5 = in.readShort();
field_5_reserved6 = in.readShort();
field_6_textLength = in.readShort();
field_7_formattingRunLength = in.readShort();
field_8_reserved7 = in.readInt();
}
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append("[TXO]\n");
buffer.append(" .options = ")
.append("0x").append(HexDump.toHex( getOptions ()))
.append(" (").append( getOptions() ).append(" )");
buffer.append(System.getProperty("line.separator"));
buffer.append(" .reserved1 = ").append(isReserved1()).append('\n');
buffer.append(" .HorizontalTextAlignment = ").append(getHorizontalTextAlignment()).append('\n');
buffer.append(" .VerticalTextAlignment = ").append(getVerticalTextAlignment()).append('\n');
buffer.append(" .reserved2 = ").append(getReserved2()).append('\n');
buffer.append(" .textLocked = ").append(isTextLocked()).append('\n');
buffer.append(" .reserved3 = ").append(getReserved3()).append('\n');
buffer.append(" .textOrientation = ")
.append("0x").append(HexDump.toHex( getTextOrientation ()))
.append(" (").append( getTextOrientation() ).append(" )");
buffer.append(System.getProperty("line.separator"));
buffer.append(" .reserved4 = ")
.append("0x").append(HexDump.toHex( getReserved4 ()))
.append(" (").append( getReserved4() ).append(" )");
buffer.append(System.getProperty("line.separator"));
buffer.append(" .reserved5 = ")
.append("0x").append(HexDump.toHex( getReserved5 ()))
.append(" (").append( getReserved5() ).append(" )");
buffer.append(System.getProperty("line.separator"));
buffer.append(" .reserved6 = ")
.append("0x").append(HexDump.toHex( getReserved6 ()))
.append(" (").append( getReserved6() ).append(" )");
buffer.append(System.getProperty("line.separator"));
buffer.append(" .textLength = ")
.append("0x").append(HexDump.toHex( getTextLength ()))
.append(" (").append( getTextLength() ).append(" )");
buffer.append(System.getProperty("line.separator"));
buffer.append(" .formattingRunLength = ")
.append("0x").append(HexDump.toHex( getFormattingRunLength ()))
.append(" (").append( getFormattingRunLength() ).append(" )");
buffer.append(System.getProperty("line.separator"));
buffer.append(" .reserved7 = ")
.append("0x").append(HexDump.toHex( getReserved7 ()))
.append(" (").append( getReserved7() ).append(" )");
buffer.append(System.getProperty("line.separator"));
buffer.append("[/TXO]\n");
return buffer.toString();
}
public int serialize(int offset, byte[] data)
{
int pos = 0;
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
LittleEndian.putShort(data, 4 + offset + pos, field_1_options);
LittleEndian.putShort(data, 6 + offset + pos, field_2_textOrientation);
LittleEndian.putShort(data, 8 + offset + pos, field_3_reserved4);
LittleEndian.putShort(data, 10 + offset + pos, field_4_reserved5);
LittleEndian.putShort(data, 12 + offset + pos, field_5_reserved6);
LittleEndian.putShort(data, 14 + offset + pos, field_6_textLength);
LittleEndian.putShort(data, 16 + offset + pos, field_7_formattingRunLength);
LittleEndian.putInt(data, 18 + offset + pos, field_8_reserved7);
return getRecordSize();
}
public int getRecordSize()
{
return 4 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 4;
}
public short getSid()
{
return sid;
}
public Object clone() {
TextObjectBaseRecord rec = new TextObjectBaseRecord();
rec.field_1_options = field_1_options;
rec.field_2_textOrientation = field_2_textOrientation;
rec.field_3_reserved4 = field_3_reserved4;
rec.field_4_reserved5 = field_4_reserved5;
rec.field_5_reserved6 = field_5_reserved6;
rec.field_6_textLength = field_6_textLength;
rec.field_7_formattingRunLength = field_7_formattingRunLength;
rec.field_8_reserved7 = field_8_reserved7;
return rec;
}
/**
* Get the options field for the TextObjectBase record.
*/
public short getOptions()
{
return field_1_options;
}
/**
* Set the options field for the TextObjectBase record.
*/
public void setOptions(short field_1_options)
{
this.field_1_options = field_1_options;
}
/**
* Get the text orientation field for the TextObjectBase record.
*
* @return One of
* TEXT_ORIENTATION_NONE
* TEXT_ORIENTATION_TOP_TO_BOTTOM
* TEXT_ORIENTATION_ROT_RIGHT
* TEXT_ORIENTATION_ROT_LEFT
*/
public short getTextOrientation()
{
return field_2_textOrientation;
}
/**
* Set the text orientation field for the TextObjectBase record.
*
* @param field_2_textOrientation
* One of
* TEXT_ORIENTATION_NONE
* TEXT_ORIENTATION_TOP_TO_BOTTOM
* TEXT_ORIENTATION_ROT_RIGHT
* TEXT_ORIENTATION_ROT_LEFT
*/
public void setTextOrientation(short field_2_textOrientation)
{
this.field_2_textOrientation = field_2_textOrientation;
}
/**
* Get the reserved4 field for the TextObjectBase record.
*/
public short getReserved4()
{
return field_3_reserved4;
}
/**
* Set the reserved4 field for the TextObjectBase record.
*/
public void setReserved4(short field_3_reserved4)
{
this.field_3_reserved4 = field_3_reserved4;
}
/**
* Get the reserved5 field for the TextObjectBase record.
*/
public short getReserved5()
{
return field_4_reserved5;
}
/**
* Set the reserved5 field for the TextObjectBase record.
*/
public void setReserved5(short field_4_reserved5)
{
this.field_4_reserved5 = field_4_reserved5;
}
/**
* Get the reserved6 field for the TextObjectBase record.
*/
public short getReserved6()
{
return field_5_reserved6;
}
/**
* Set the reserved6 field for the TextObjectBase record.
*/
public void setReserved6(short field_5_reserved6)
{
this.field_5_reserved6 = field_5_reserved6;
}
/**
* Get the text length field for the TextObjectBase record.
*/
public short getTextLength()
{
return field_6_textLength;
}
/**
* Set the text length field for the TextObjectBase record.
*/
public void setTextLength(short field_6_textLength)
{
this.field_6_textLength = field_6_textLength;
}
/**
* Get the formatting run length field for the TextObjectBase record.
*/
public short getFormattingRunLength()
{
return field_7_formattingRunLength;
}
/**
* Set the formatting run length field for the TextObjectBase record.
*/
public void setFormattingRunLength(short field_7_formattingRunLength)
{
this.field_7_formattingRunLength = field_7_formattingRunLength;
}
/**
* Get the reserved7 field for the TextObjectBase record.
*/
public int getReserved7()
{
return field_8_reserved7;
}
/**
* Set the reserved7 field for the TextObjectBase record.
*/
public void setReserved7(int field_8_reserved7)
{
this.field_8_reserved7 = field_8_reserved7;
}
/**
* Sets the reserved1 field value.
* reserved field
*/
public void setReserved1(boolean value)
{
field_1_options = reserved1.setShortBoolean(field_1_options, value);
}
/**
* reserved field
* @return the reserved1 field value.
*/
public boolean isReserved1()
{
return reserved1.isSet(field_1_options);
}
/**
* Sets the Horizontal text alignment field value.
*
*/
public void setHorizontalTextAlignment(short value)
{
field_1_options = HorizontalTextAlignment.setShortValue(field_1_options, value);
}
/**
*
* @return the Horizontal text alignment field value.
*/
public short getHorizontalTextAlignment()
{
return HorizontalTextAlignment.getShortValue(field_1_options);
}
/**
* Sets the Vertical text alignment field value.
*
*/
public void setVerticalTextAlignment(short value)
{
field_1_options = VerticalTextAlignment.setShortValue(field_1_options, value);
}
/**
*
* @return the Vertical text alignment field value.
*/
public short getVerticalTextAlignment()
{
return VerticalTextAlignment.getShortValue(field_1_options);
}
/**
* Sets the reserved2 field value.
*
*/
public void setReserved2(short value)
{
field_1_options = reserved2.setShortValue(field_1_options, value);
}
/**
*
* @return the reserved2 field value.
*/
public short getReserved2()
{
return reserved2.getShortValue(field_1_options);
}
/**
* Sets the text locked field value.
* Text has been locked
*/
public void setTextLocked(boolean value)
{
field_1_options = textLocked.setShortBoolean(field_1_options, value);
}
/**
* Text has been locked
* @return the text locked field value.
*/
public boolean isTextLocked()
{
return textLocked.isSet(field_1_options);
}
/**
* Sets the reserved3 field value.
*
*/
public void setReserved3(short value)
{
field_1_options = reserved3.setShortValue(field_1_options, value);
}
/**
*
* @return the reserved3 field value.
*/
public short getReserved3()
{
return reserved3.getShortValue(field_1_options);
}
}

View File

@ -19,253 +19,427 @@ package org.apache.poi.hssf.record;
import java.io.UnsupportedEncodingException;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
public class TextObjectRecord
extends TextObjectBaseRecord
{
HSSFRichTextString str;
/**
* The TXO record (0x01B6) is used to define the properties of a text box. It is
* followed by two or more continue records unless there is no actual text. The
* first continue records contain the text data and the last continue record
* contains the formatting runs.<p/>
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public final class TextObjectRecord extends Record {
public final static short sid = 0x01B6;
public TextObjectRecord()
{
}
private static final int FORMAT_RUN_ENCODED_SIZE = 8; // 2 shorts and 4 bytes reserved
public TextObjectRecord( RecordInputStream in )
{
super( in );
private static final BitField HorizontalTextAlignment = BitFieldFactory.getInstance(0x000E);
private static final BitField VerticalTextAlignment = BitFieldFactory.getInstance(0x0070);
private static final BitField textLocked = BitFieldFactory.getInstance(0x0200);
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) {
if (in.isContinueNext() && in.remaining() == 0) {
in.nextRecord();
processFontRuns(in);
} else throw new RecordFormatException("Expected Continue Record to hold font runs for TextObjectRecord");
}
if (str == null)
str = new HSSFRichTextString("");
}
public final static short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
public final static short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
public final static short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
public final static short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
public final static short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
public final static short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
public final static short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
public final static short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
public final static short TEXT_ORIENTATION_NONE = 0;
public final static short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
public final static short TEXT_ORIENTATION_ROT_RIGHT = 2;
public final static short TEXT_ORIENTATION_ROT_LEFT = 3;
private int field_1_options;
private int field_2_textOrientation;
private int field_3_reserved4;
private int field_4_reserved5;
private int field_5_reserved6;
private int field_8_reserved7;
private HSSFRichTextString _text;
/*
* Note - the next three fields are very similar to those on
* EmbededObjectRefSubRecord(ftPictFmla 0x0009)
*
* some observed values for the 4 bytes preceding the formula: C0 5E 86 03
* C0 11 AC 02 80 F1 8A 03 D4 F0 8A 03
*/
private int _unknownPreFormulaInt;
/** expect tRef, tRef3D, tArea, tArea3D or tName */
private Ptg _linkRefPtg;
/**
* Not clear if needed . Excel seems to be OK if this byte is not present.
* Value is often the same as the earlier firstColumn byte. */
private Byte _unknownPostFormulaByte;
public TextObjectRecord() {
}
public TextObjectRecord(RecordInputStream in) {
field_1_options = in.readUShort();
field_2_textOrientation = in.readUShort();
field_3_reserved4 = in.readUShort();
field_4_reserved5 = in.readUShort();
field_5_reserved6 = in.readUShort();
int field_6_textLength = in.readUShort();
int field_7_formattingDataLength = in.readUShort();
field_8_reserved7 = in.readInt();
if (in.remaining() > 0) {
// Text Objects can have simple reference formulas
// (This bit not mentioned in the MS document)
if (in.remaining() < 11) {
throw new RecordFormatException("Not enough remaining data for a link formula");
}
int formulaSize = in.readUShort();
_unknownPreFormulaInt = in.readInt();
Ptg[] ptgs = Ptg.readTokens(formulaSize, in);
if (ptgs.length != 1) {
throw new RecordFormatException("Read " + ptgs.length
+ " tokens but expected exactly 1");
}
_linkRefPtg = ptgs[0];
if (in.remaining() > 0) {
_unknownPostFormulaByte = new Byte(in.readByte());
} else {
_unknownPostFormulaByte = null;
}
} else {
_linkRefPtg = null;
}
if (in.remaining() > 0) {
throw new RecordFormatException("Unused " + in.remaining() + " bytes at end of record");
}
String text;
if (field_6_textLength > 0) {
text = readRawString(in, field_6_textLength);
} else {
text = "";
}
_text = new HSSFRichTextString(text);
if (field_7_formattingDataLength > 0) {
if (in.isContinueNext() && in.remaining() == 0) {
in.nextRecord();
processFontRuns(in, _text, field_7_formattingDataLength);
} else {
throw new RecordFormatException(
"Expected Continue Record to hold font runs for TextObjectRecord");
}
}
}
private static String readRawString(RecordInputStream in, int textLength) {
byte compressByte = in.readByte();
boolean isCompressed = (compressByte & 0x01) == 0;
if (isCompressed) {
return in.readCompressedUnicode(textLength);
}
return in.readUnicodeLEString(textLength);
}
private static void processFontRuns(RecordInputStream in, HSSFRichTextString str,
int formattingRunDataLength) {
if (formattingRunDataLength % FORMAT_RUN_ENCODED_SIZE != 0) {
throw new RecordFormatException("Bad format run data length " + formattingRunDataLength
+ ")");
}
if (in.remaining() != formattingRunDataLength) {
throw new RecordFormatException("Expected " + formattingRunDataLength
+ " bytes but got " + in.remaining());
}
int nRuns = formattingRunDataLength / FORMAT_RUN_ENCODED_SIZE;
for (int i = 0; i < nRuns; i++) {
short index = in.readShort();
short iFont = in.readShort();
in.readInt(); // skip reserved.
str.applyFont(index, str.length(), iFont);
}
}
public short getSid() {
return sid;
}
/**
* Only for the current record. does not include any subsequent Continue
* records
*/
private int getDataSize() {
int result = 2 + 2 + 2 + 2 + 2 + 2 + 2 + 4;
if (_linkRefPtg != null) {
result += 2 // formula size
+ 4 // unknownInt
+_linkRefPtg.getSize();
if (_unknownPostFormulaByte != null) {
result += 1;
}
}
return result;
}
private int serializeTXORecord(int offset, byte[] data) {
int dataSize = getDataSize();
LittleEndian.putUShort(data, 0 + offset, TextObjectRecord.sid);
LittleEndian.putUShort(data, 2 + offset, dataSize);
LittleEndian.putUShort(data, 4 + offset, field_1_options);
LittleEndian.putUShort(data, 6 + offset, field_2_textOrientation);
LittleEndian.putUShort(data, 8 + offset, field_3_reserved4);
LittleEndian.putUShort(data, 10 + offset, field_4_reserved5);
LittleEndian.putUShort(data, 12 + offset, field_5_reserved6);
LittleEndian.putUShort(data, 14 + offset, _text.length());
LittleEndian.putUShort(data, 16 + offset, getFormattingDataLength());
LittleEndian.putInt(data, 18 + offset, field_8_reserved7);
if (_linkRefPtg != null) {
int pos = offset+22;
int formulaSize = _linkRefPtg.getSize();
LittleEndian.putUShort(data, pos, formulaSize);
pos += LittleEndian.SHORT_SIZE;
LittleEndian.putInt(data, pos, _unknownPreFormulaInt);
pos += LittleEndian.INT_SIZE;
_linkRefPtg.writeBytes(data, pos);
pos += formulaSize;
if (_unknownPostFormulaByte != null) {
LittleEndian.putByte(data, pos, _unknownPostFormulaByte.byteValue());
pos += LittleEndian.BYTE_SIZE;
}
}
return 4 + dataSize;
}
private int serializeTrailingRecords(int offset, byte[] data) {
byte[] textBytes;
try {
textBytes = _text.getString().getBytes("UTF-16LE");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage(), e);
}
int remainingLength = textBytes.length;
int countTextBytesWritten = 0;
int pos = offset;
// (regardless what was read, we always serialize double-byte
// unicode characters (UTF-16LE).
Byte unicodeFlag = new Byte((byte)1);
while (remainingLength > 0) {
int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE - 2, remainingLength);
remainingLength -= chunkSize;
pos += ContinueRecord.write(data, pos, unicodeFlag, textBytes, countTextBytesWritten, chunkSize);
countTextBytesWritten += chunkSize;
}
byte[] formatData = createFormatData(_text);
pos += ContinueRecord.write(data, pos, null, formatData);
return pos - offset;
}
private int getTrailingRecordsSize() {
if (_text.length() < 1) {
return 0;
}
int encodedTextSize = 0;
int textBytesLength = _text.length() * LittleEndian.SHORT_SIZE;
while (textBytesLength > 0) {
int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE - 2, textBytesLength);
textBytesLength -= chunkSize;
encodedTextSize += 4; // +4 for ContinueRecord sid+size
encodedTextSize += 1+chunkSize; // +1 for compressed unicode flag,
}
int encodedFormatSize = (_text.numFormattingRuns() + 1) * FORMAT_RUN_ENCODED_SIZE
+ 4; // +4 for ContinueRecord sid+size
return encodedTextSize + encodedFormatSize;
}
public int getRecordSize()
{
int continue1Size = 0;
int continue2Size = 0;
if (str.length() != 0)
{
int length = str.length() * 2;
while(length > 0){
int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE-2, length);
length -= chunkSize;
public int serialize(int offset, byte[] data) {
continue1Size += chunkSize;
continue1Size += 1 + 4;
}
int expectedTotalSize = getRecordSize();
int totalSize = serializeTXORecord(offset, data);
if (_text.getString().length() > 0) {
totalSize += serializeTrailingRecords(offset+totalSize, data);
}
if (totalSize != expectedTotalSize)
throw new RecordFormatException(totalSize
+ " bytes written but getRecordSize() reports " + expectedTotalSize);
return totalSize;
}
continue2Size = (str.numFormattingRuns() + 1) * 8 + 4;
}
return super.getRecordSize() + continue1Size + continue2Size;
}
/**
* Note - this total size includes all potential {@link ContinueRecord}s written
*/
public int getRecordSize() {
int baseSize = 4 + getDataSize();
return baseSize + getTrailingRecordsSize();
}
private int getFormattingDataLength() {
if (_text.length() < 1) {
// important - no formatting data if text is empty
return 0;
}
return (_text.numFormattingRuns() + 1) * FORMAT_RUN_ENCODED_SIZE;
}
private static byte[] createFormatData(HSSFRichTextString str) {
int nRuns = str.numFormattingRuns();
byte[] result = new byte[(nRuns + 1) * FORMAT_RUN_ENCODED_SIZE];
int pos = 0;
for (int i = 0; i < nRuns; i++) {
LittleEndian.putUShort(result, pos, str.getIndexOfFormattingRun(i));
pos += 2;
int fontIndex = str.getFontOfFormattingRun(i);
LittleEndian.putUShort(result, pos, fontIndex == str.NO_FONT ? 0 : fontIndex);
pos += 2;
pos += 4; // skip reserved
}
LittleEndian.putUShort(result, pos, str.length());
pos += 2;
LittleEndian.putUShort(result, pos, 0);
pos += 2;
pos += 4; // skip reserved
public int serialize( int offset, byte[] data )
{
// Temporarily blank out str so that record size is calculated without the continue records.
HSSFRichTextString temp = str;
str = new HSSFRichTextString("");
int bytesWritten1 = super.serialize( offset, data );
str = temp;
return result;
}
int pos = offset + bytesWritten1;
if ( str.getString().equals( "" ) == false )
{
ContinueRecord c2 = createContinue2();
int bytesWritten2 = 0;
/**
* Sets the Horizontal text alignment field value.
*/
public void setHorizontalTextAlignment(int value) {
field_1_options = HorizontalTextAlignment.setValue(field_1_options, value);
}
try
{
byte[] c1Data = str.getString().getBytes( "UTF-16LE" );
int length = c1Data.length;
/**
* @return the Horizontal text alignment field value.
*/
public int getHorizontalTextAlignment() {
return HorizontalTextAlignment.getValue(field_1_options);
}
int charsWritten = 0;
int spos = pos;
while(length > 0){
int chunkSize = Math.min(RecordInputStream.MAX_RECORD_DATA_SIZE-2 , length);
length -= chunkSize;
/**
* Sets the Vertical text alignment field value.
*/
public void setVerticalTextAlignment(int value) {
field_1_options = VerticalTextAlignment.setValue(field_1_options, value);
}
//continue header
LittleEndian.putShort(data, spos, ContinueRecord.sid);
spos += LittleEndian.SHORT_SIZE;
LittleEndian.putShort(data, spos, (short)(chunkSize+1));
spos += LittleEndian.SHORT_SIZE;
/**
* @return the Vertical text alignment field value.
*/
public int getVerticalTextAlignment() {
return VerticalTextAlignment.getValue(field_1_options);
}
//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;
/**
* Sets the text locked field value.
*/
public void setTextLocked(boolean value) {
field_1_options = textLocked.setBoolean(field_1_options, value);
}
//copy characters data
System.arraycopy(c1Data, charsWritten, data, spos, chunkSize);
spos += chunkSize;
charsWritten += chunkSize;
}
/**
* @return the text locked field value.
*/
public boolean isTextLocked() {
return textLocked.isSet(field_1_options);
}
bytesWritten2 = (spos-pos);
}
catch ( UnsupportedEncodingException e )
{
throw new RuntimeException( e.getMessage(), e );
}
/**
* Get the text orientation field for the TextObjectBase record.
*
* @return One of TEXT_ORIENTATION_NONE TEXT_ORIENTATION_TOP_TO_BOTTOM
* TEXT_ORIENTATION_ROT_RIGHT TEXT_ORIENTATION_ROT_LEFT
*/
public int getTextOrientation() {
return field_2_textOrientation;
}
pos += bytesWritten2;
int bytesWritten3 = c2.serialize( pos, data );
pos += bytesWritten3;
/**
* Set the text orientation field for the TextObjectBase record.
*
* @param textOrientation
* One of TEXT_ORIENTATION_NONE TEXT_ORIENTATION_TOP_TO_BOTTOM
* TEXT_ORIENTATION_ROT_RIGHT TEXT_ORIENTATION_ROT_LEFT
*/
public void setTextOrientation(int textOrientation) {
this.field_2_textOrientation = textOrientation;
}
int size = bytesWritten1 + bytesWritten2 + bytesWritten3;
if ( size != getRecordSize() )
throw new RecordFormatException(size + " bytes written but getRecordSize() reports " + getRecordSize());
return size;
}
if ( bytesWritten1 != getRecordSize() )
throw new RecordFormatException(bytesWritten1 + " bytes written but getRecordSize() reports " + getRecordSize());
return bytesWritten1;
}
public HSSFRichTextString getStr() {
return _text;
}
private ContinueRecord createContinue2()
{
ContinueRecord c2 = new ContinueRecord();
byte[] c2Data = new byte[str.numFormattingRuns() * 8 + 8];
int pos = 0;
for ( int i = 0; i < str.numFormattingRuns(); i++ )
{
LittleEndian.putShort( c2Data, pos, (short) str.getIndexOfFormattingRun( i ) );
pos += 2;
LittleEndian.putShort( c2Data, pos, str.getFontOfFormattingRun( i ) == str.NO_FONT ? 0 : str.getFontOfFormattingRun( i ) );
pos += 2;
pos += 4; // skip reserved
}
LittleEndian.putShort( c2Data, pos, (short) str.length() );
pos += 2;
LittleEndian.putShort( c2Data, pos, (short) 0 );
pos += 2;
pos += 4; // skip reserved
public void setStr(HSSFRichTextString str) {
_text = str;
}
public Ptg getLinkRefPtg() {
return _linkRefPtg;
}
c2.setData( c2Data );
public String toString() {
StringBuffer sb = new StringBuffer();
return c2;
}
sb.append("[TXO]\n");
sb.append(" .options = ").append(HexDump.shortToHex(field_1_options)).append("\n");
sb.append(" .isHorizontal = ").append(getHorizontalTextAlignment()).append('\n');
sb.append(" .isVertical = ").append(getVerticalTextAlignment()).append('\n');
sb.append(" .textLocked = ").append(isTextLocked()).append('\n');
sb.append(" .textOrientation= ").append(HexDump.shortToHex(getTextOrientation())).append("\n");
sb.append(" .reserved4 = ").append(HexDump.shortToHex(field_3_reserved4)).append("\n");
sb.append(" .reserved5 = ").append(HexDump.shortToHex(field_4_reserved5)).append("\n");
sb.append(" .reserved6 = ").append(HexDump.shortToHex(field_5_reserved6)).append("\n");
sb.append(" .textLength = ").append(HexDump.shortToHex(_text.length())).append("\n");
sb.append(" .reserved7 = ").append(HexDump.intToHex(field_8_reserved7)).append("\n");
private void processFontRuns( RecordInputStream in )
{
while (in.remaining() > 0)
{
short index = in.readShort();
short iFont = in.readShort();
in.readInt(); // skip reserved.
sb.append(" .string = ").append(_text).append('\n');
str.applyFont( index, str.length(), iFont );
}
}
for (int i = 0; i < _text.numFormattingRuns(); i++) {
sb.append(" .textrun = ").append(_text.getFontOfFormattingRun(i)).append('\n');
private void processRawString( RecordInputStream in )
{
String s;
byte compressByte = in.readByte();
boolean isCompressed = compressByte == 0;
if ( isCompressed )
{
s = in.readCompressedUnicode(getTextLength());
}
else
{
s = in.readUnicodeLEString(getTextLength());
}
str = new HSSFRichTextString( s );
}
}
sb.append("[/TXO]\n");
return sb.toString();
}
public HSSFRichTextString getStr()
{
return str;
}
public Object clone() {
public void setStr( HSSFRichTextString str )
{
this.str = str;
}
TextObjectRecord rec = new TextObjectRecord();
rec._text = _text;
public String toString()
{
StringBuffer buffer = new StringBuffer();
rec.field_1_options = field_1_options;
rec.field_2_textOrientation = field_2_textOrientation;
rec.field_3_reserved4 = field_3_reserved4;
rec.field_4_reserved5 = field_4_reserved5;
rec.field_5_reserved6 = field_5_reserved6;
rec.field_8_reserved7 = field_8_reserved7;
buffer.append( "[TXO]\n" );
buffer.append( " .options = " )
.append( "0x" ).append( HexDump.toHex( getOptions() ) )
.append( " (" ).append( getOptions() ).append( " )" );
buffer.append( System.getProperty( "line.separator" ) );
buffer.append( " .reserved1 = " ).append( isReserved1() ).append( '\n' );
buffer.append( " .HorizontalTextAlignment = " ).append( getHorizontalTextAlignment() ).append( '\n' );
buffer.append( " .VerticalTextAlignment = " ).append( getVerticalTextAlignment() ).append( '\n' );
buffer.append( " .reserved2 = " ).append( getReserved2() ).append( '\n' );
buffer.append( " .textLocked = " ).append( isTextLocked() ).append( '\n' );
buffer.append( " .reserved3 = " ).append( getReserved3() ).append( '\n' );
buffer.append( " .textOrientation = " )
.append( "0x" ).append( HexDump.toHex( getTextOrientation() ) )
.append( " (" ).append( getTextOrientation() ).append( " )" );
buffer.append( System.getProperty( "line.separator" ) );
buffer.append( " .reserved4 = " )
.append( "0x" ).append( HexDump.toHex( getReserved4() ) )
.append( " (" ).append( getReserved4() ).append( " )" );
buffer.append( System.getProperty( "line.separator" ) );
buffer.append( " .reserved5 = " )
.append( "0x" ).append( HexDump.toHex( getReserved5() ) )
.append( " (" ).append( getReserved5() ).append( " )" );
buffer.append( System.getProperty( "line.separator" ) );
buffer.append( " .reserved6 = " )
.append( "0x" ).append( HexDump.toHex( getReserved6() ) )
.append( " (" ).append( getReserved6() ).append( " )" );
buffer.append( System.getProperty( "line.separator" ) );
buffer.append( " .textLength = " )
.append( "0x" ).append( HexDump.toHex( getTextLength() ) )
.append( " (" ).append( getTextLength() ).append( " )" );
buffer.append( System.getProperty( "line.separator" ) );
buffer.append( " .reserved7 = " )
.append( "0x" ).append( HexDump.toHex( getReserved7() ) )
.append( " (" ).append( getReserved7() ).append( " )" );
buffer.append( System.getProperty( "line.separator" ) );
buffer.append( " .string = " ).append(str).append('\n');
for (int i = 0; i < str.numFormattingRuns(); i++) {
buffer.append( " .textrun = " ).append(str.getFontOfFormattingRun(i)).append('\n');
}
buffer.append( "[/TXO]\n" );
return buffer.toString();
}
public Object clone() {
TextObjectRecord rec = new TextObjectRecord();
rec.str = str;
rec.setOptions(getOptions());
rec.setTextOrientation(getTextOrientation());
rec.setReserved4(getReserved4());
rec.setReserved5(getReserved5());
rec.setReserved6(getReserved6());
rec.setTextLength(getTextLength());
rec.setFormattingRunLength(getFormattingRunLength());
rec.setReserved7(getReserved7());
return rec;
}
rec._text = _text; // clone needed?
if (_linkRefPtg != null) {
rec._unknownPreFormulaInt = _unknownPreFormulaInt;
rec._linkRefPtg = _linkRefPtg.copy();
rec._unknownPostFormulaByte = rec._unknownPostFormulaByte;
}
return rec;
}
}

View File

@ -152,10 +152,7 @@ public class HSSFComment extends HSSFTextbox {
if (string.numFormattingRuns() == 0) string.applyFont((short)0);
if (txo != null) {
int frLength = ( string.numFormattingRuns() + 1 ) * 8;
txo.setFormattingRunLength( (short) frLength );
txo.setTextLength( (short) string.length() );
txo.setStr( string );
txo.setStr(string);
}
super.setString(string);
}

View File

@ -194,7 +194,7 @@ public class HSSFRichTextString
/**
* @return the number of characters in the font.
* @return the number of characters in the text.
*/
public int length()
{

View File

@ -18,6 +18,11 @@
package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.util.HexRead;
import junit.framework.TestCase;
/**
@ -25,63 +30,62 @@ import junit.framework.TestCase;
* class works correctly. Test data taken directly from a real
* Excel file.
*
* @author Glen Stampoultzis (glens at apache.org)
*/
public class TestTextObjectBaseRecord extends TestCase {
byte[] data = new byte[] {
0x44, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00,
};
public final class TestTextObjectBaseRecord extends TestCase {
/** data for one TXO rec and two continue recs */
private static final byte[] data = HexRead.readFromString(
"B6 01 " + // TextObjectRecord.sid
"12 00 " + // size 18
"44 02 02 00 00 00 00 00" +
"00 00 " +
"02 00 " + // strLen 2
"10 00 " + // 16 bytes for 2 format runs
"00 00" +
"00 00 " +
"3C 00 " + // ContinueRecord.sid
"05 00 " + // size 5
"01 " + // unicode uncompressed
"41 00 42 00 " + // 'AB'
"3C 00 " + // ContinueRecord.sid
"10 00 " + // size 16
"00 00 18 00 00 00 00 00 " +
"02 00 00 00 00 00 00 00 "
);
public void testLoad() {
TextObjectBaseRecord record = new TextObjectBaseRecord(new TestcaseRecordInputStream((short)0x1B6, (short)data.length, data));
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
in.nextRecord();
TextObjectRecord record = new TextObjectRecord(in);
// assertEquals( (short), record.getOptions());
assertEquals( false, record.isReserved1() );
assertEquals( TextObjectBaseRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment() );
assertEquals( TextObjectBaseRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment() );
assertEquals( 0, record.getReserved2() );
assertEquals( true, record.isTextLocked() );
assertEquals( 0, record.getReserved3() );
assertEquals( TextObjectBaseRecord.TEXT_ORIENTATION_ROT_RIGHT, record.getTextOrientation());
assertEquals( 0, record.getReserved4());
assertEquals( 0, record.getReserved5());
assertEquals( 0, record.getReserved6());
assertEquals( 2, record.getTextLength());
assertEquals( 2, record.getFormattingRunLength());
assertEquals( 0, record.getReserved7());
assertEquals(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED, record.getHorizontalTextAlignment());
assertEquals(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY, record.getVerticalTextAlignment());
assertEquals(true, record.isTextLocked());
assertEquals(TextObjectRecord.TEXT_ORIENTATION_ROT_RIGHT, record.getTextOrientation());
assertEquals( 22, record.getRecordSize() );
assertEquals(51, record.getRecordSize() );
}
public void testStore()
{
TextObjectBaseRecord record = new TextObjectBaseRecord();
TextObjectRecord record = new TextObjectRecord();
HSSFRichTextString str = new HSSFRichTextString("AB");
str.applyFont(0, 2, (short)0x0018);
str.applyFont(2, 2, (short)0x0320);
// record.setOptions( (short) 0x0000);
record.setReserved1( false );
record.setHorizontalTextAlignment( TextObjectBaseRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED );
record.setVerticalTextAlignment( TextObjectBaseRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY );
record.setReserved2( (short)0 );
record.setTextLocked( true );
record.setReserved3( (short)0 );
record.setTextOrientation( TextObjectBaseRecord.TEXT_ORIENTATION_ROT_RIGHT );
record.setReserved4( (short)0 );
record.setReserved5( (short)0 );
record.setReserved6( (short)0 );
record.setTextLength( (short)2 );
record.setFormattingRunLength( (short)2 );
record.setReserved7( 0 );
record.setHorizontalTextAlignment(TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED);
record.setVerticalTextAlignment(TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_JUSTIFY);
record.setTextLocked(true);
record.setTextOrientation(TextObjectRecord.TEXT_ORIENTATION_ROT_RIGHT);
record.setStr(str);
byte [] recordBytes = record.serialize();
assertEquals(recordBytes.length - 4, data.length);
assertEquals(recordBytes.length, data.length);
for (int i = 0; i < data.length; i++)
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
assertEquals("At offset " + i, data[i], recordBytes[i]);
}
}

View File

@ -22,7 +22,11 @@ import java.util.Arrays;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.util.HexRead;
import org.apache.poi.util.LittleEndian;
/**
* Tests that serialization and deserialization of the TextObjectRecord .
@ -32,17 +36,23 @@ import org.apache.poi.hssf.usermodel.HSSFRichTextString;
*/
public final 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 };
private static final byte[] simpleData = HexRead.readFromString(
"B6 01 12 00 " +
"12 02 00 00 00 00 00 00" +
"00 00 0D 00 08 00 00 00" +
"00 00 " +
"3C 00 1B 00 " +
"01 48 00 65 00 6C 00 6C 00 6F 00 " +
"2C 00 20 00 57 00 6F 00 72 00 6C " +
"00 64 00 21 00 " +
"3C 00 08 " +
"00 0D 00 00 00 00 00 00 00"
);
public void testRead() {
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(simpleData));
is.nextRecord();
TextObjectRecord record = new TextObjectRecord(is);
@ -50,36 +60,51 @@ public final class TestTextObjectRecord extends TestCase {
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()
{
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.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);
assertEquals(ser.length , simpleData.length);
//assertTrue(Arrays.equals(data, ser));
assertTrue(Arrays.equals(simpleData, ser));
//read again
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(simpleData));
is.nextRecord();
record = new TextObjectRecord(is);
}
/**
* Zero {@link ContinueRecord}s follow a {@link TextObjectRecord} if the text is empty
*/
public void testWriteEmpty() {
HSSFRichTextString str = new HSSFRichTextString("");
TextObjectRecord record = new TextObjectRecord();
record.setStr(str);
byte [] ser = record.serialize();
int formatDataLen = LittleEndian.getUShort(ser, 16);
assertEquals("formatDataLength", 0, formatDataLen);
assertEquals(22, ser.length); // just the TXO record
//read again
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(ser));
is.nextRecord();
record = new TextObjectRecord(is);
assertEquals(0, record.getStr().length());
}
/**
@ -95,10 +120,7 @@ public final class TestTextObjectRecord extends TestCase {
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 );
obj.setStr(str);
byte [] data = obj.serialize();
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(data));
@ -120,30 +142,12 @@ public final class TestTextObjectRecord extends TestCase {
HSSFRichTextString str = new HSSFRichTextString(text);
TextObjectRecord obj = new TextObjectRecord();
int frLength = ( str.numFormattingRuns() + 1 ) * 8;
obj.setFormattingRunLength( (short) frLength );
obj.setTextLength( (short) str.length() );
obj.setReserved1(true);
obj.setReserved2((short)2);
obj.setReserved3((short)3);
obj.setReserved4((short)4);
obj.setReserved5((short)5);
obj.setReserved6((short)6);
obj.setReserved7((short)7);
obj.setStr( str );
TextObjectRecord cloned = (TextObjectRecord)obj.clone();
assertEquals(obj.getReserved2(), cloned.getReserved2());
assertEquals(obj.getReserved3(), cloned.getReserved3());
assertEquals(obj.getReserved4(), cloned.getReserved4());
assertEquals(obj.getReserved5(), cloned.getReserved5());
assertEquals(obj.getReserved6(), cloned.getReserved6());
assertEquals(obj.getReserved7(), cloned.getReserved7());
assertEquals(obj.getRecordSize(), cloned.getRecordSize());
assertEquals(obj.getOptions(), cloned.getOptions());
assertEquals(obj.getHorizontalTextAlignment(), cloned.getHorizontalTextAlignment());
assertEquals(obj.getFormattingRunLength(), cloned.getFormattingRunLength());
assertEquals(obj.getStr().getString(), cloned.getStr().getString());
//finally check that the serialized data is the same
@ -151,4 +155,47 @@ public final class TestTextObjectRecord extends TestCase {
byte[] cln = cloned.serialize();
assertTrue(Arrays.equals(src, cln));
}
/** similar to {@link #simpleData} but with link formula at end of TXO rec*/
private static final byte[] linkData = HexRead.readFromString(
"B6 01 " + // TextObjectRecord.sid
"1E 00 " + // size 18
"44 02 02 00 00 00 00 00" +
"00 00 " +
"02 00 " + // strLen 2
"10 00 " + // 16 bytes for 2 format runs
"00 00 00 00 " +
"05 00 " + // formula size
"D4 F0 8A 03 " + // unknownInt
"24 01 00 13 C0 " + //tRef(T2)
"13 " + // ??
"3C 00 " + // ContinueRecord.sid
"05 00 " + // size 5
"01 " + // unicode uncompressed
"41 00 42 00 " + // 'AB'
"3C 00 " + // ContinueRecord.sid
"10 00 " + // size 16
"00 00 18 00 00 00 00 00 " +
"02 00 00 00 00 00 00 00 "
);
public void testLinkFormula() {
RecordInputStream is = new RecordInputStream(new ByteArrayInputStream(linkData));
is.nextRecord();
TextObjectRecord rec = new TextObjectRecord(is);
Ptg ptg = rec.getLinkRefPtg();
assertNotNull(ptg);
assertEquals(RefPtg.class, ptg.getClass());
RefPtg rptg = (RefPtg) ptg;
assertEquals("T2", rptg.toFormulaString());
byte [] data2 = rec.serialize();
assertEquals(linkData.length, data2.length);
assertTrue(Arrays.equals(linkData, data2));
}
}