/* ====================================================================
|
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.
|
==================================================================== */
|
|
namespace HH.WMS.Utils.NPOI.HSSF.Record
|
{
|
using System;
|
using System.IO;
|
using System.Text;
|
using HH.WMS.Utils.NPOI.HSSF.Record;
|
using HH.WMS.Utils.NPOI.Util;
|
using HH.WMS.Utils.NPOI.HSSF.UserModel;
|
|
|
using HH.WMS.Utils.NPOI.SS.Formula;
|
using HH.WMS.Utils.NPOI.SS.UserModel;
|
using HH.WMS.Utils.NPOI.HSSF.Record.Cont;
|
using HH.WMS.Utils.NPOI.SS.Formula.PTG;
|
using System.Globalization;
|
|
public class TextObjectRecord : ContinuableRecord
|
{
|
HH.WMS.Utils.NPOI.SS.UserModel.IRichTextString _text;
|
|
public const short sid = 0x1B6;
|
|
private static int FORMAT_RUN_ENCODED_SIZE = 8; // 2 shorts and 4 bytes reserved
|
|
|
private BitField _HorizontalTextAlignment = BitFieldFactory.GetInstance(0x000E);
|
private BitField _VerticalTextAlignment = BitFieldFactory.GetInstance(0x0070);
|
private BitField textLocked = BitFieldFactory.GetInstance(0x200);
|
|
public const short TEXT_ORIENTATION_NONE = 0;
|
public const short TEXT_ORIENTATION_TOP_TO_BOTTOM = 1;
|
public const short TEXT_ORIENTATION_ROT_RIGHT = 2;
|
public const short TEXT_ORIENTATION_ROT_LEFT = 3;
|
|
|
public const short HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED = 1;
|
public const short HORIZONTAL_TEXT_ALIGNMENT_CENTERED = 2;
|
public const short HORIZONTAL_TEXT_ALIGNMENT_RIGHT_ALIGNED = 3;
|
public const short HORIZONTAL_TEXT_ALIGNMENT_JUSTIFIED = 4;
|
public const short VERTICAL_TEXT_ALIGNMENT_TOP = 1;
|
public const short VERTICAL_TEXT_ALIGNMENT_CENTER = 2;
|
public const short VERTICAL_TEXT_ALIGNMENT_BOTTOM = 3;
|
public const short VERTICAL_TEXT_ALIGNMENT_JUSTIFY = 4;
|
|
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;
|
/*
|
* 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 OperandPtg _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 in1)
|
{
|
|
field_1_options = in1.ReadUShort();
|
field_2_textOrientation = in1.ReadUShort();
|
field_3_reserved4 = in1.ReadUShort();
|
field_4_reserved5 = in1.ReadUShort();
|
field_5_reserved6 = in1.ReadUShort();
|
int field_6_textLength = in1.ReadUShort();
|
int field_7_formattingDataLength = in1.ReadUShort();
|
field_8_reserved7 = in1.ReadInt();
|
|
if (in1.Remaining > 0)
|
{
|
// Text Objects can have simple reference formulas
|
// (This bit not mentioned in the MS document)
|
if (in1.Remaining < 11)
|
{
|
throw new RecordFormatException("Not enough remaining data for a link formula");
|
}
|
int formulaSize = in1.ReadUShort();
|
_unknownPreFormulaInt = in1.ReadInt();
|
Ptg[] ptgs = Ptg.ReadTokens(formulaSize, in1);
|
if (ptgs.Length != 1)
|
{
|
throw new RecordFormatException("Read " + ptgs.Length
|
+ " tokens but expected exactly 1");
|
}
|
_linkRefPtg = (OperandPtg)ptgs[0];
|
if (in1.Remaining > 0)
|
{
|
_unknownPostFormulaByte = (byte)in1.ReadByte();
|
}
|
else
|
{
|
_unknownPostFormulaByte = null;
|
}
|
}
|
else
|
{
|
_linkRefPtg = null;
|
}
|
if (in1.Remaining > 0)
|
{
|
throw new RecordFormatException("Unused " + in1.Remaining + " bytes at end of record");
|
}
|
|
String text;
|
if (field_6_textLength > 0)
|
{
|
text = ReadRawString(in1, field_6_textLength);
|
}
|
else
|
{
|
text = "";
|
}
|
_text = new HSSFRichTextString(text);
|
|
if (field_7_formattingDataLength > 0)
|
{
|
ProcessFontRuns(in1, _text, field_7_formattingDataLength);
|
}
|
}
|
private static void ProcessFontRuns(RecordInputStream in1, IRichTextString str,
|
int formattingRunDataLength)
|
{
|
if (formattingRunDataLength % FORMAT_RUN_ENCODED_SIZE != 0)
|
{
|
throw new RecordFormatException("Bad format run data length " + formattingRunDataLength
|
+ ")");
|
}
|
int nRuns = formattingRunDataLength / FORMAT_RUN_ENCODED_SIZE;
|
for (int i = 0; i < nRuns; i++)
|
{
|
short index = in1.ReadShort();
|
short iFont = in1.ReadShort();
|
in1.ReadInt(); // skip reserved.
|
str.ApplyFont(index, str.Length, iFont);
|
}
|
}
|
|
private int TrailingRecordsSize
|
{
|
get
|
{
|
if (_text.Length < 1)
|
{
|
return 0;
|
}
|
int encodedTextSize = 0;
|
int textBytesLength = _text.Length * LittleEndianConsts.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;
|
}
|
}
|
private static byte[] CreateFormatData(IRichTextString 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 = ((HSSFRichTextString)str).GetFontOfFormattingRun(i);
|
LittleEndian.PutUShort(result, pos, fontIndex == HSSFRichTextString.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
|
|
return result;
|
}
|
|
private void SerializeTrailingRecords(ContinuableRecordOutput out1)
|
{
|
out1.WriteContinue();
|
out1.WriteStringData(_text.String);
|
out1.WriteContinue();
|
WriteFormatData(out1,_text);
|
}
|
|
private void WriteFormatData(ContinuableRecordOutput out1, IRichTextString str)
|
{
|
int nRuns = str.NumFormattingRuns;
|
for (int i = 0; i < nRuns; i++)
|
{
|
out1.WriteShort(str.GetIndexOfFormattingRun(i));
|
int fontIndex = ((HSSFRichTextString)str).GetFontOfFormattingRun(i);
|
out1.WriteShort(fontIndex == HSSFRichTextString.NO_FONT ? 0 : fontIndex);
|
out1.WriteInt(0); // skip reserved
|
}
|
out1.WriteShort(str.Length);
|
out1.WriteShort(0);
|
out1.WriteInt(0); // skip reserved
|
}
|
|
|
private int FormattingDataLength
|
{
|
get
|
{
|
if (_text.Length < 1)
|
{
|
// important - no formatting data if text is empty
|
return 0;
|
}
|
return (_text.NumFormattingRuns + 1) * FORMAT_RUN_ENCODED_SIZE;
|
}
|
}
|
|
private void SerializeTXORecord(ContinuableRecordOutput out1)
|
{
|
out1.WriteShort(field_1_options);
|
out1.WriteShort(field_2_textOrientation);
|
out1.WriteShort(field_3_reserved4);
|
out1.WriteShort(field_4_reserved5);
|
out1.WriteShort(field_5_reserved6);
|
out1.WriteShort(_text.Length);
|
out1.WriteShort(FormattingDataLength);
|
out1.WriteInt(field_8_reserved7);
|
|
if (_linkRefPtg != null)
|
{
|
int formulaSize = _linkRefPtg.Size;
|
out1.WriteShort(formulaSize);
|
out1.WriteInt(_unknownPreFormulaInt);
|
_linkRefPtg.Write(out1);
|
|
if (_unknownPostFormulaByte != null)
|
{
|
out1.WriteByte(Convert.ToByte(_unknownPostFormulaByte, CultureInfo.InvariantCulture));
|
}
|
}
|
}
|
|
|
protected override void Serialize(ContinuableRecordOutput out1)
|
{
|
SerializeTXORecord(out1);
|
|
if (_text.String.Length > 0)
|
{
|
SerializeTrailingRecords(out1);
|
}
|
}
|
|
private void ProcessFontRuns(RecordInputStream in1)
|
{
|
while (in1.Remaining > 0)
|
{
|
short index = in1.ReadShort();
|
short iFont = in1.ReadShort();
|
in1.ReadInt(); // skip reserved.
|
|
_text.ApplyFont(index, _text.Length, iFont);
|
}
|
}
|
|
|
private static String ReadRawString(RecordInputStream in1, int textLength)
|
{
|
byte compressByte = (byte)in1.ReadByte();
|
bool isCompressed = (compressByte & 0x01) == 0;
|
if (isCompressed)
|
{
|
return in1.ReadCompressedUnicode(textLength);
|
}
|
return in1.ReadUnicodeLEString(textLength);
|
}
|
|
public IRichTextString Str
|
{
|
get { return _text; }
|
set { this._text = value; }
|
}
|
public override short Sid
|
{
|
get
|
{
|
return sid;
|
}
|
}
|
|
/**
|
* 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 TextOrientation
|
{
|
get { return field_2_textOrientation; }
|
set { this.field_2_textOrientation = value; }
|
}
|
|
|
/**
|
* @return the Horizontal text alignment field value.
|
*/
|
public int HorizontalTextAlignment
|
{
|
get
|
{
|
return _HorizontalTextAlignment.GetValue(field_1_options);
|
}
|
set { field_1_options = _HorizontalTextAlignment.SetValue(field_1_options, value); }
|
}
|
/**
|
* @return the Vertical text alignment field value.
|
*/
|
public int VerticalTextAlignment
|
{
|
get
|
{
|
return _VerticalTextAlignment.GetValue(field_1_options);
|
}
|
set { field_1_options = _VerticalTextAlignment.SetValue(field_1_options, value); }
|
}
|
|
/**
|
* Text has been locked
|
* @return the text locked field value.
|
*/
|
public bool IsTextLocked
|
{
|
get { return textLocked.IsSet(field_1_options); }
|
set { field_1_options = textLocked.SetBoolean(field_1_options, value); }
|
}
|
public Ptg LinkRefPtg
|
{
|
get
|
{
|
return _linkRefPtg;
|
}
|
}
|
public override String ToString()
|
{
|
StringBuilder sb = new StringBuilder();
|
|
sb.Append("[TXO]\n");
|
sb.Append(" .options = ").Append(HexDump.ShortToHex(field_1_options)).Append("\n");
|
sb.Append(" .IsHorizontal = ").Append(HorizontalTextAlignment).Append('\n');
|
sb.Append(" .IsVertical = ").Append(VerticalTextAlignment).Append('\n');
|
sb.Append(" .textLocked = ").Append(IsTextLocked).Append('\n');
|
sb.Append(" .textOrientation= ").Append(HexDump.ShortToHex(TextOrientation)).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");
|
|
sb.Append(" .string = ").Append(_text).Append('\n');
|
|
for (int i = 0; i < _text.NumFormattingRuns; i++)
|
{
|
sb.Append(" .textrun = ").Append(((HSSFRichTextString)_text).GetFontOfFormattingRun(i)).Append('\n');
|
|
}
|
sb.Append("[/TXO]\n");
|
return sb.ToString();
|
}
|
|
public override Object Clone()
|
{
|
|
TextObjectRecord rec = new TextObjectRecord();
|
rec._text = _text;
|
|
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;
|
|
rec._text = _text; // clone needed?
|
|
if (_linkRefPtg != null)
|
{
|
rec._unknownPreFormulaInt = _unknownPreFormulaInt;
|
rec._linkRefPtg = _linkRefPtg.Copy();
|
rec._unknownPostFormulaByte = _unknownPostFormulaByte;
|
}
|
return rec;
|
}
|
|
}
|
}
|