コード例 #1
0
 public NumberEval(Ptg ptg)
 {
     if (ptg is IntPtg)
     {
         this._value = ((IntPtg)ptg).Value;
     }
     else if (ptg is NumberPtg)
     {
         this._value = ((NumberPtg)ptg).Value;
     }
 }
コード例 #2
0
ファイル: ParseNode.cs プロジェクト: Johnnyfly/source20131023
 public ParseNode(Ptg token, ParseNode[] children)
 {
     _token = token;
     _children = children;
     _isIf = IsIf(token);
     int tokenCount = 1;
     for (int i = 0; i < children.Length; i++)
     {
         tokenCount += children[i].GetTokenCount();
     }
     if (_isIf)
     {
         // there will be 2 or 3 extra tAttr tokens according to whether the false param is present
         tokenCount += children.Length;
     }
     _tokenCount = tokenCount;
 }
コード例 #3
0
        /**
         * Used to calculate value that should be encoded at the start of the encoded Ptg token array;
         * @return the size of the encoded Ptg tokens not including any trailing array data.
         */
        public static int GetEncodedSizeWithoutArrayData(Ptg[] ptgs)
        {
            int result = 0;

            for (int i = 0; i < ptgs.Length; i++)
            {
                Ptg ptg = ptgs[i];
                if (ptg is ArrayPtg)
                {
                    result += ArrayPtg.PLAIN_TOKEN_SIZE;
                }
                else
                {
                    result += ptg.Size;
                }
            }
            return(result);
        }
コード例 #4
0
 private static bool IsDeletedCellRef(Ptg ptg)
 {
     if (ptg == ErrPtg.REF_INVALID)
     {
         return(true);
     }
     if (ptg is DeletedArea3DPtg)
     {
         return(true);
     }
     if (ptg is DeletedRef3DPtg)
     {
         return(true);
     }
     if (ptg is AreaErrPtg)
     {
         return(true);
     }
     if (ptg is RefErrorPtg)
     {
         return(true);
     }
     return(false);
 }
コード例 #5
0
ファイル: Ptg.cs プロジェクト: Johnnyfly/source20131023
 public static bool DoesFormulaReferToDeletedCell(Ptg[] ptgs)
 {
     for (int i = 0; i < ptgs.Length; i++)
     {
         if (IsDeletedCellRef(ptgs[i]))
         {
             return true;
         }
     }
     return false;
 }
コード例 #6
0
ファイル: ParseNode.cs プロジェクト: Johnnyfly/source20131023
 private static bool IsIf(Ptg token)
 {
     if (token is FuncVarPtg)
     {
         FuncVarPtg func = (FuncVarPtg)token;
         if (FunctionMetadataRegistry.FUNCTION_NAME_IF.Equals(func.Name))
         {
             return true;
         }
     }
     return false;
 }
コード例 #7
0
        /**
         * Static method To convert an array of {@link Ptg}s in RPN order
         * To a human readable string format in infix mode.
         * @param book  used for defined names and 3D references
         * @param ptgs  must not be <c>null</c>
         * @return a human readable String
         */
        public static String ToFormulaString(IFormulaRenderingWorkbook book, Ptg[] ptgs)
        {
            if (ptgs == null || ptgs.Length == 0)
            {
                throw new ArgumentException("ptgs must not be null");
            }
            Stack stack = new Stack();

            for (int i = 0; i < ptgs.Length; i++)
            {
                Ptg ptg = ptgs[i];
                // TODO - what about MemNoMemPtg?
                if (ptg is MemAreaPtg || ptg is MemFuncPtg || ptg is MemErrPtg)
                {
                    // marks the start of a list of area expressions which will be naturally combined
                    // by their trailing operators (e.g. UnionPtg)
                    // TODO - Put comment and throw exception in ToFormulaString() of these classes
                    continue;
                }
                if (ptg is ParenthesisPtg)
                {
                    String contents = (String)stack.Pop();
                    stack.Push("(" + contents + ")");
                    continue;
                }
                if (ptg is AttrPtg)
                {
                    AttrPtg attrPtg = ((AttrPtg)ptg);
                    if (attrPtg.IsOptimizedIf || attrPtg.IsOptimizedChoose || attrPtg.IsSkip)
                    {
                        continue;
                    }
                    if (attrPtg.IsSpace)
                    {
                        // POI currently doesn't render spaces in formulas
                        continue;
                        // but if it ever did, care must be taken:
                        // tAttrSpace comes *before* the operand it applies To, which may be consistent
                        // with how the formula text appears but is against the RPN ordering assumed here
                    }
                    if (attrPtg.IsSemiVolatile)
                    {
                        // similar To tAttrSpace - RPN is violated
                        continue;
                    }
                    if (attrPtg.IsSum)
                    {
                        String[] operands = GetOperands(stack, attrPtg.NumberOfOperands);
                        stack.Push(attrPtg.ToFormulaString(operands));
                        continue;
                    }
                    throw new Exception("Unexpected tAttr: " + attrPtg.ToString());
                }

                if (ptg is WorkbookDependentFormula)
                {
                    WorkbookDependentFormula optg = (WorkbookDependentFormula)ptg;
                    stack.Push(optg.ToFormulaString(book));
                    continue;
                }
                if (!(ptg is OperationPtg))
                {
                    stack.Push(ptg.ToFormulaString());
                    continue;
                }

                OperationPtg o = (OperationPtg)ptg;
                String[] operands1 = GetOperands(stack, o.NumberOfOperands);
                stack.Push(o.ToFormulaString(operands1));
            }
            if (stack.Count == 0)
            {
                // inspection of the code above reveals that every stack.pop() is followed by a
                // stack.push(). So this is either an internal error or impossible.
                throw new InvalidOperationException("Stack underflow");
            }
            String result = (String)stack.Pop();
            if (stack.Count != 0)
            {
                // Might be caused by some Tokens like AttrPtg and Mem*Ptg, which really shouldn't
                // Put anything on the stack
                throw new InvalidOperationException("too much stuff left on the stack");
            }
            return result;
        }
コード例 #8
0
 private Ptg AdjustPtgDueToShiftMove(Ptg ptg)
 {
     Ptg updatedPtg = null;
     if (ptg is Ref3DPtg)
     {
         Ref3DPtg ref1 = (Ref3DPtg)ptg;
         if (ref1.ExternSheetIndex == _srcSheetIndex)
         {
             ref1.ExternSheetIndex = (_dstSheetIndex);
             updatedPtg = ref1;
         }
         else if (ref1.ExternSheetIndex == _dstSheetIndex)
         {
             ref1.ExternSheetIndex = (_srcSheetIndex);
             updatedPtg = ref1;
         }
     }
     return updatedPtg;
 }
コード例 #9
0
    /**
     * @param in the stream to read data from
     * @param cbFContinued the seconf short in the record header
     * @param cmoOt the Containing Obj's {@link CommonObjectDataSubRecord#field_1_objectType}
     */
        public LbsDataSubRecord(ILittleEndianInput in1, int cbFContinued, int cmoOt)
        {
            _cbFContinued = cbFContinued;

            int encodedTokenLen = in1.ReadUShort();
            if (encodedTokenLen > 0)
            {
                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");
                }
                _linkPtg = ptgs[0];
                switch (encodedTokenLen - formulaSize - 6)
                {
                    case 1:
                        _unknownPostFormulaByte = (byte)in1.ReadByte();
                        break;
                    case 0:
                        _unknownPostFormulaByte = null;
                        break;
                    default:
                        throw new RecordFormatException("Unexpected leftover bytes");
                }
            }

            _cLines = in1.ReadUShort();
            _iSel = in1.ReadUShort();
            _flags = in1.ReadUShort();
            _idEdit = in1.ReadUShort();

            // From [MS-XLS].pdf 2.5.147 FtLbsData:
            // This field MUST exist if and only if the Containing Obj?s cmo.ot is equal to 0x14.
            if (cmoOt == 0x14)
            {
                _dropData = new LbsDropData(in1);
            }

            // From [MS-XLS].pdf 2.5.147 FtLbsData:
            // This array MUST exist if and only if the fValidPlex flag (0x2) is set
            if ((_flags & 0x2) != 0)
            {
                _rgLines = new String[_cLines];
                for (int i = 0; i < _cLines; i++)
                {
                    _rgLines[i] = StringUtil.ReadUnicodeString(in1);
                }
            }

            // bits 5-6 in the _flags specify the type
            // of selection behavior this list control is expected to support

            // From [MS-XLS].pdf 2.5.147 FtLbsData:
            // This array MUST exist if and only if the wListType field is not equal to 0.
            if (((_flags >> 4) & 0x2) != 0)
            {
                _bsels = new bool[_cLines];
                for (int i = 0; i < _cLines; i++)
                {
                    _bsels[i] = in1.ReadByte() == 1;
                }
            }

        }
コード例 #10
0
ファイル: DVRecord.cs プロジェクト: Johnnyfly/source20131023
        public DVRecord(int validationType, int operator1, int errorStyle, bool emptyCellAllowed,
            bool suppressDropDownArrow, bool isExplicitList,
            bool showPromptBox, String promptTitle, String promptText,
            bool showErrorBox, String errorTitle, String errorText,
            Ptg[] formula1, Ptg[] formula2,
            CellRangeAddressList regions)
        {

            int flags = 0;
            flags = opt_data_type.SetValue(flags, validationType);
            flags = opt_condition_operator.SetValue(flags, operator1);
            flags = opt_error_style.SetValue(flags, errorStyle);
            flags = opt_empty_cell_allowed.SetBoolean(flags, emptyCellAllowed);
            flags = opt_suppress_dropdown_arrow.SetBoolean(flags, suppressDropDownArrow);
            flags = opt_string_list_formula.SetBoolean(flags, isExplicitList);
            flags = opt_show_prompt_on_cell_selected.SetBoolean(flags, showPromptBox);
            flags = opt_show_error_on_invalid_value.SetBoolean(flags, showErrorBox);
            _option_flags = flags;
            _promptTitle = ResolveTitleText(promptTitle);
            _promptText = ResolveTitleText(promptText);
            _errorTitle = ResolveTitleText(errorTitle);
            _errorText = ResolveTitleText(errorText);
            _formula1 = Zephyr.Utils.NPOI.SS.Formula.Formula.Create(formula1);
            _formula2 = Zephyr.Utils.NPOI.SS.Formula.Formula.Create(formula2);
            _regions = regions;
        }
コード例 #11
0
 private Ptg AdjustPtg(Ptg ptg, int currentExternSheetIx)
 {
     //return AdjustPtgDueToRowMove(ptg, currentExternSheetIx);
     switch (_mode)
     {
         case ShiftMode.Row:
             return AdjustPtgDueToRowMove(ptg, currentExternSheetIx);
         case ShiftMode.Sheet:
             return AdjustPtgDueToShiftMove(ptg);
         default:
             throw new InvalidOperationException("Unsupported shift mode: " + _mode);
     }
 }
コード例 #12
0
        public void SetArrayFormula(CellRangeAddress r, Ptg[] ptgs)
        {

            ArrayRecord arr = new ArrayRecord(Zephyr.Utils.NPOI.SS.Formula.Formula.Create(ptgs), new CellRangeAddress8Bit(r.FirstRow, r.LastRow, r.FirstColumn, r.LastColumn));
            _sharedValueManager.AddArrayRecord(arr);
        }
コード例 #13
0
ファイル: Ptg.cs プロジェクト: Johnnyfly/source20131023
 private static bool IsDeletedCellRef(Ptg ptg)
 {
     if (ptg == ErrPtg.REF_INVALID)
     {
         return true;
     }
     if (ptg is DeletedArea3DPtg)
     {
         return true;
     }
     if (ptg is DeletedRef3DPtg)
     {
         return true;
     }
     if (ptg is AreaErrPtg)
     {
         return true;
     }
     if (ptg is RefErrorPtg)
     {
         return true;
     }
     return false;
 }
コード例 #14
0
        /**
 * Also checks for a related shared formula and unlinks it if found
 */
        public void SetParsedExpression(Ptg[] ptgs)
        {
            NotifyFormulaChanging();
            _formulaRecord.ParsedExpression=(ptgs);
        }
コード例 #15
0
ファイル: ParseNode.cs プロジェクト: Johnnyfly/source20131023
 public ParseNode(Ptg token, ParseNode child0, ParseNode child1): this(token, new ParseNode[] { child0, child1, })
 {
    
 }
コード例 #16
0
ファイル: ParseNode.cs プロジェクト: Johnnyfly/source20131023
 public ParseNode(Ptg token):this(token, EMPTY_ARRAY) 
 {
     
 }
コード例 #17
0
ファイル: ParseNode.cs プロジェクト: Johnnyfly/source20131023
 public void Add(Ptg token)
 {
     if (token == null)
     {
         throw new ArgumentException("token must not be null");
     }
     _ptgs[_offset] = token;
     _offset++;
 }
コード例 #18
0
ファイル: ParseNode.cs プロジェクト: Johnnyfly/source20131023
 public void SetPlaceholder(int index, Ptg token)
 {
     if (_ptgs[index] != null)
     {
         throw new InvalidOperationException("Invalid placeholder index (" + index + ")");
     }
     _ptgs[index] = token;
 }
コード例 #19
0
ファイル: Formula.cs プロジェクト: Johnnyfly/source20131023
 /**
  * Creates a {@link Formula} object from a supplied {@link Ptg} array. 
  * Handles <code>null</code>s OK.
  * @param ptgs may be <code>null</code>
  * @return Never <code>null</code> (Possibly empty if the supplied <c>ptgs</c> is <code>null</code>)
  */
 public static Formula Create(Ptg[] ptgs)
 {
     if (ptgs == null || ptgs.Length < 1)
     {
         return EMPTY;
     }
     int totalSize = Ptg.GetEncodedSize(ptgs);
     byte[] encodedData = new byte[totalSize];
     Ptg.SerializePtgs(ptgs, encodedData, 0);
     int encodedTokenLen = Ptg.GetEncodedSizeWithoutArrayData(ptgs);
     return new Formula(encodedData, encodedTokenLen);
 }
コード例 #20
0
        /**
         * Generates the variable Function ptg for the formula.
         * 
         * For IF Formulas, Additional PTGs are Added To the Tokens
	 * @param name a {@link NamePtg} or {@link NameXPtg} or <code>null</code>
         * @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this Function
         */
        private ParseNode GetFunction(String name, Ptg namePtg, ParseNode[] args)
        {

            FunctionMetadata fm = FunctionMetadataRegistry.GetFunctionByName(name.ToUpper());
            int numArgs = args.Length;
            if (fm == null)
            {
                if (namePtg == null)
                {
                    throw new InvalidOperationException("NamePtg must be supplied for external Functions");
                }
                // must be external Function
                ParseNode[] allArgs = new ParseNode[numArgs + 1];
                allArgs[0] = new ParseNode(namePtg);
                System.Array.Copy(args, 0, allArgs, 1, numArgs);
                return new ParseNode(FuncVarPtg.Create(name, (byte)(numArgs + 1)), allArgs);
            }

            if (namePtg != null)
            {
                throw new InvalidOperationException("NamePtg no applicable To internal Functions");
            }
            bool IsVarArgs = !fm.HasFixedArgsLength;
            int funcIx = fm.Index;
		if (funcIx == FunctionMetadataRegistry.FUNCTION_INDEX_SUM && args.Length == 1) {
			// Excel encodes the sum of a single argument as tAttrSum
			// POI does the same for consistency, but this is not critical
			return new ParseNode(AttrPtg.GetSumSingle(), args);
			// The code below would encode tFuncVar(SUM) which seems to do no harm
		}
            ValidateNumArgs(args.Length, fm);

            AbstractFunctionPtg retval;
            if (IsVarArgs)
            {
                retval = FuncVarPtg.Create(name, (byte)numArgs);
            }
            else
            {
                retval = FuncPtg.Create(funcIx);
            }
            return new ParseNode(retval, args);
        }
コード例 #21
0
 /**
  * @param ptgs - if necessary, will get modified by this method
  * @param currentExternSheetIx - the extern sheet index of the sheet that contains the formula being adjusted
  * @return <c>true</c> if a change was made to the formula tokens
  */
 public bool AdjustFormula(Ptg[] ptgs, int currentExternSheetIx)
 {
     bool refsWereChanged = false;
     for (int i = 0; i < ptgs.Length; i++)
     {
         Ptg newPtg = AdjustPtg(ptgs[i], currentExternSheetIx);
         if (newPtg != null)
         {
             refsWereChanged = true;
             ptgs[i] = newPtg;
         }
     }
     return refsWereChanged;
 }
コード例 #22
0
 private static Double ConvertArrayNumber(Ptg ptg, bool isPositive)
 {
     double value;
     if (ptg is IntPtg)
     {
         value = ((IntPtg)ptg).Value;
     }
     else if (ptg is NumberPtg)
     {
         value = ((NumberPtg)ptg).Value;
     }
     else
     {
         throw new Exception("Unexpected ptg (" + ptg.GetType().Name + ")");
     }
     if (!isPositive)
     {
         value = -value;
     }
     return value;
 }
コード例 #23
0
 /**
  * @return <c>true</c> if this Ptg needed to be changed
  */
 private Ptg AdjustPtgDueToRowMove(Ptg ptg, int currentExternSheetIx)
 {
     if (ptg is RefPtg)
     {
         if (currentExternSheetIx != _externSheetIndex)
         {
             // local refs on other sheets are unaffected
             return null;
         }
         RefPtg rptg = (RefPtg)ptg;
         return RowMoveRefPtg(rptg);
     }
     if (ptg is Ref3DPtg)
     {
         Ref3DPtg rptg = (Ref3DPtg)ptg;
         if (_externSheetIndex != rptg.ExternSheetIndex)
         {
             // only move 3D refs that refer to the sheet with cells being moved
             // (currentExternSheetIx is irrelevant)
             return null;
         }
         return RowMoveRefPtg(rptg);
     }
     if (ptg is Area2DPtgBase)
     {
         if (currentExternSheetIx != _externSheetIndex)
         {
             // local refs on other sheets are unaffected
             return ptg;
         }
         return RowMoveAreaPtg((Area2DPtgBase)ptg);
     }
     if (ptg is Area3DPtg)
     {
         Area3DPtg aptg = (Area3DPtg)ptg;
         if (_externSheetIndex != aptg.ExternSheetIndex)
         {
             // only move 3D refs that refer to the sheet with cells being moved
             // (currentExternSheetIx is irrelevant)
             return null;
         }
         return RowMoveAreaPtg(aptg);
     }
     return null;
 }
コード例 #24
0
        /**
         * 
         * "A1", "B3" -> "A1:B3"   
         * "sheet1!A1", "B3" -> "sheet1!A1:B3"
         * 
         * @return <c>null</c> if the range expression cannot / shouldn't be reduced.
         */
        private static Ptg ReduceRangeExpression(Ptg ptgA, Ptg ptgB)
        {
            if (!(ptgB is RefPtg))
            {
                // only when second ref is simple 2-D ref can the range 
                // expression be converted To an area ref
                return null;
            }
            RefPtg refB = (RefPtg)ptgB;

            if (ptgA is RefPtg)
            {
                RefPtg refA = (RefPtg)ptgA;
                return new AreaPtg(refA.Row, refB.Row, refA.Column, refB.Column,
                        refA.IsRowRelative, refB.IsRowRelative, refA.IsColRelative, refB.IsColRelative);
            }
            if (ptgA is Ref3DPtg)
            {
                Ref3DPtg refA = (Ref3DPtg)ptgA;
                return new Area3DPtg(refA.Row, refB.Row, refA.Column, refB.Column,
                        refA.IsRowRelative, refB.IsRowRelative, refA.IsColRelative, refB.IsColRelative,
                        refA.ExternSheetIndex);
            }
            // Note - other operand types (like AreaPtg) which probably can't evaluate 
            // do not cause validation errors at Parse time
            return null;
        }
コード例 #25
0
        private static Ptg CreateDeletedRef(Ptg ptg)
        {
            if (ptg is RefPtg)
            {
                return new RefErrorPtg();
            }
            if (ptg is Ref3DPtg)
            {
                Ref3DPtg rptg = (Ref3DPtg)ptg;
                return new DeletedRef3DPtg(rptg.ExternSheetIndex);
            }
            if (ptg is AreaPtg)
            {
                return new AreaErrPtg();
            }
            if (ptg is Area3DPtg)
            {
                Area3DPtg area3DPtg = (Area3DPtg)ptg;
                return new DeletedArea3DPtg(area3DPtg.ExternSheetIndex);
            }

            throw new ArgumentException("Unexpected ref ptg class (" + ptg.GetType().Name + ")");
        }
コード例 #26
0
ファイル: Ptg.cs プロジェクト: Johnnyfly/source20131023
        /**
	 * This method will return the same result as {@link #getEncodedSizeWithoutArrayData(Ptg[])}
	 * if there are no array tokens present.
	 * @return the full size taken to encode the specified <c>Ptg</c>s
	 */
        public static int GetEncodedSize(Ptg[] ptgs)
        {
            int result = 0;
            for (int i = 0; i < ptgs.Length; i++)
            {
                result += ptgs[i].Size;
            }
            return result;
        }
コード例 #27
0
 public FormulaPair(Ptg[] formula1, Ptg[] formula2)
 {
     _formula1 = formula1;
     _formula2 = formula2;
 }
コード例 #28
0
ファイル: Ptg.cs プロジェクト: Johnnyfly/source20131023
        /**
 * Used to calculate value that should be encoded at the start of the encoded Ptg token array;
 * @return the size of the encoded Ptg tokens not including any trailing array data.
 */
        public static int GetEncodedSizeWithoutArrayData(Ptg[] ptgs)
        {
            int result = 0;
            for (int i = 0; i < ptgs.Length; i++)
            {
                Ptg ptg = ptgs[i];
                if (ptg is ArrayPtg)
                {
                    result += ArrayPtg.PLAIN_TOKEN_SIZE;
                }
                else
                {
                    result += ptg.Size;
                }
            }
            return result;
        }
コード例 #29
0
 public void SetParsedExpression(Ptg[] ptgs)
 {
     field_5_name_definition = Formula.Create(ptgs);
 }
コード例 #30
0
ファイル: Ptg.cs プロジェクト: Johnnyfly/source20131023
        /**
         * Writes the ptgs to the data buffer, starting at the specified offset.  
         *
         * <br/>
         * The 2 byte encode Length field is <b>not</b> written by this method.
         * @return number of bytes written
         */
        public static int SerializePtgs(Ptg[] ptgs, byte[] array, int offset)
        {
            int size = ptgs.Length;

            LittleEndianByteArrayOutputStream out1 = new LittleEndianByteArrayOutputStream(array, offset);

            ArrayList arrayPtgs = null;

            for (int k = 0; k < size; k++)
            {
                Ptg ptg = ptgs[k];

                ptg.Write(out1);
                if (ptg is ArrayPtg)
                {
                    if (arrayPtgs == null)
                    {
                        arrayPtgs = new ArrayList(5);
                    }
                    arrayPtgs.Add(ptg);

                }
            }
            if (arrayPtgs != null)
            {
                for (int i = 0; i < arrayPtgs.Count; i++)
                {
                    ArrayPtg p = (ArrayPtg)arrayPtgs[i];
                    p.WriteTokenValueBytes(out1);
                }
            }
            return out1.WriteIndex - offset; ;
        }
コード例 #31
0
 public void SetParsedExpression(Ptg[] ptgs)
 {
     field_8_parsed_expr = Zephyr.Utils.NPOI.SS.Formula.Formula.Create(ptgs);
 }
コード例 #32
0
        /**
         * Constructs an EmbeddedObjectRef record and Sets its fields appropriately.
         *
         * @param in the record input stream.
         */
        public EmbeddedObjectRefSubRecord(ILittleEndianInput in1, int size)
        {
            // Much guess-work going on here due to lack of any documentation.
            // See similar source code in OOO:
            // http://lxr.go-oo.org/source/sc/sc/source/filter/excel/xiescher.cxx
            // 1223 void XclImpOleObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nRecSize )

            int streamIdOffset = in1.ReadShort(); // OOO calls this 'nFmlaLen'
            int remaining = size - LittleEndianConsts.SHORT_SIZE;

            int dataLenAfterFormula = remaining - streamIdOffset;
            int formulaSize = in1.ReadUShort();

            remaining -= LittleEndianConsts.SHORT_SIZE;
            field_1_unknown_int = in1.ReadInt();
            remaining -= LittleEndianConsts.INT_SIZE;
            byte[] formulaRawBytes = ReadRawData(in1, formulaSize);
            remaining -= formulaSize;
            field_2_refPtg = ReadRefPtg(formulaRawBytes);
            if (field_2_refPtg == null)
            {
                // common case
                // field_2_n16 seems to be 5 here
                // The formula almost looks like tTbl but the row/column values seem like garbage.
                field_2_unknownFormulaData = formulaRawBytes;
            }
            else
            {
                field_2_unknownFormulaData = null;
            }


            int stringByteCount;
            if (remaining >= dataLenAfterFormula + 3)
            {
                int tag = in1.ReadByte();
                stringByteCount = LittleEndianConsts.BYTE_SIZE;
                if (tag != 0x03)
                {
                    throw new RecordFormatException("Expected byte 0x03 here");
                }
                int nChars = in1.ReadUShort();
                stringByteCount += LittleEndianConsts.SHORT_SIZE;
                if (nChars > 0)
                {
                    // OOO: the 4th way Xcl stores a unicode string: not even a Grbit byte present if Length 0
                    field_3_unicode_flag = (in1.ReadByte() & 0x01) != 0;
                    stringByteCount += LittleEndianConsts.BYTE_SIZE;
                    if (field_3_unicode_flag)
                    {
                        field_4_ole_classname = StringUtil.ReadUnicodeLE(in1,nChars);
                        stringByteCount += nChars * 2;
                    }
                    else
                    {
                        field_4_ole_classname = StringUtil.ReadCompressedUnicode(in1,nChars);
                        stringByteCount += nChars;
                    }
                }
                else
                {
                    field_4_ole_classname = "";
                }
            }
            else
            {
                field_4_ole_classname = null;
                stringByteCount = 0;
            }
            remaining -= stringByteCount;
            // Pad to next 2-byte boundary
            if (((stringByteCount + formulaSize) % 2) != 0)
            {
                int b = in1.ReadByte();
                remaining -= LittleEndianConsts.BYTE_SIZE;
                if (field_2_refPtg != null && field_4_ole_classname == null)
                {
                    field_4_unknownByte = (byte)b;
                }
            }
            int nUnexpectedPadding = remaining - dataLenAfterFormula;

            if (nUnexpectedPadding > 0)
            {
                Console.WriteLine("Discarding " + nUnexpectedPadding + " unexpected padding bytes ");
                ReadRawData(in1, nUnexpectedPadding);
                remaining -= nUnexpectedPadding;
            }

            // Fetch the stream ID
            if (dataLenAfterFormula >= 4)
            {
                field_5_stream_id = in1.ReadInt();
                remaining -= LittleEndianConsts.INT_SIZE;
            }
            else
            {
                field_5_stream_id = null;
            }

            field_6_unknown = ReadRawData(in1, remaining);
        }