fpspreadsheet: Initial version of reading BIFF8 shared rpn formulas. Reconstruction of string formula from shared rpn formula ok.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3488 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
parent
5a6d0dd650
commit
62ff8bc2d0
@ -81,8 +81,8 @@ type
|
||||
}
|
||||
TFEKind = (
|
||||
{ Basic operands }
|
||||
fekCell, fekCellRef, fekCellRange, fekNum, fekInteger, fekString, fekBool,
|
||||
fekErr, fekMissingArg,
|
||||
fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
|
||||
fekString, fekBool, fekErr, fekMissingArg,
|
||||
{ Basic operations }
|
||||
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
|
||||
fekConcat, // string concatenation
|
||||
@ -137,11 +137,13 @@ type
|
||||
or relative. It is a set consisting of TsRelFlag elements. }
|
||||
TsRelFlags = set of TsRelFlag;
|
||||
|
||||
{@@ Elements of an expanded formula. }
|
||||
{@@ Elements of an expanded formula.
|
||||
Note: If ElementKind is fekCellOffset, "Row" and "Col" have to be cast
|
||||
to signed integers! }
|
||||
TsFormulaElement = record
|
||||
ElementKind: TFEKind;
|
||||
Row, Row2: Word; // zero-based
|
||||
Col, Col2: Word; // zero-based
|
||||
Row, Row2: Cardinal; // zero-based
|
||||
Col, Col2: Cardinal; // zero-based
|
||||
Param1, Param2: Word; // Extra parameters
|
||||
DoubleValue: double;
|
||||
IntValue: Word;
|
||||
@ -381,6 +383,9 @@ type
|
||||
{@@ State flags while calculating formulas }
|
||||
TsCalcState = (csNotCalculated, csCalculating, csCalculated);
|
||||
|
||||
{@@ Pointer to a TCell record }
|
||||
PCell = ^TCell;
|
||||
|
||||
{@@ Cell structure for TsWorksheet
|
||||
The cell record contains information on the location of the cell (row and
|
||||
column index), on the value contained (number, date, text, ...), and on
|
||||
@ -403,6 +408,7 @@ type
|
||||
DateTimeValue: TDateTime;
|
||||
BoolValue: Boolean;
|
||||
ErrorValue: TsErrorValue;
|
||||
SharedFormulaBase: PCell; // Cell containing the shared formula
|
||||
{ Formatting fields }
|
||||
{ When adding/deleting formatting fields don't forget to update CopyFormat! }
|
||||
UsedFormattingFields: TsUsedFormattingFields;
|
||||
@ -420,9 +426,6 @@ type
|
||||
CalcState: TsCalcState;
|
||||
end;
|
||||
|
||||
{@@ Pointer to a TCell record }
|
||||
PCell = ^TCell;
|
||||
|
||||
const
|
||||
// Takes account of effect of cell margins on row height by adding this
|
||||
// value to the nominal row height. Note that this is an empirical value and may be wrong.
|
||||
@ -537,7 +540,6 @@ type
|
||||
function ReadAsDateTime(ACell: PCell; out AResult: TDateTime): Boolean; overload;
|
||||
function ReadFormulaAsString(ACell: PCell): String;
|
||||
function ReadNumericValue(ACell: PCell; out AValue: Double): Boolean;
|
||||
function ReadRPNFormulaAsString(ACell: PCell): String;
|
||||
|
||||
{ Reading of cell attributes }
|
||||
function GetNumberFormatAttributes(ACell: PCell; out ADecimals: Byte;
|
||||
@ -675,6 +677,8 @@ type
|
||||
|
||||
{ Formulas }
|
||||
procedure CalcFormulas;
|
||||
function HasFormula(ACell: PCell): Boolean;
|
||||
function ReadRPNFormulaAsString(ACell: PCell): String;
|
||||
|
||||
{ Data manipulation methods - For Cells }
|
||||
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
|
||||
@ -1141,6 +1145,7 @@ type
|
||||
ANext: PRPNItem): PRPNItem; overload;
|
||||
function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags;
|
||||
ANext: PRPNItem): PRPNItem; overload;
|
||||
function RPNCellOffset(ARowOffset, AColOffset: Integer; ANext: PRPNItem): PRPNItem;
|
||||
function RPNErr(AErrCode: Byte; ANext: PRPNItem): PRPNItem;
|
||||
function RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem;
|
||||
function RPNMissingArg(ANext: PRPNItem): PRPNItem;
|
||||
@ -1320,6 +1325,7 @@ var
|
||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCell
|
||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellRef
|
||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellRange
|
||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellOffset
|
||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellNum
|
||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellInteger
|
||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellString
|
||||
@ -1767,6 +1773,7 @@ var
|
||||
fe: TsFormulaElement;
|
||||
cell: PCell;
|
||||
r,c: Cardinal;
|
||||
formula: TsRPNFormula;
|
||||
begin
|
||||
if (Length(ACell^.RPNFormulaValue) = 0) or
|
||||
(ACell^.ContentType = cctError)
|
||||
@ -1777,8 +1784,14 @@ begin
|
||||
|
||||
args := TsArgumentStack.Create;
|
||||
try
|
||||
for i := 0 to Length(ACell^.RPNFormulaValue) - 1 do begin
|
||||
fe := ACell^.RPNFormulaValue[i]; // "fe" means "formula element"
|
||||
// Take care of shared formulas
|
||||
if ACell^.SharedFormulaBase = nil then
|
||||
formula := ACell^.RPNFormulaValue
|
||||
else
|
||||
formula := ACell^.SharedFormulaBase^.RPNFormulaValue;
|
||||
|
||||
for i := 0 to Length(formula) - 1 do begin
|
||||
fe := formula[i]; // "fe" means "formula element"
|
||||
case fe.ElementKind of
|
||||
fekCell, fekCellRef:
|
||||
begin
|
||||
@ -1803,6 +1816,16 @@ begin
|
||||
end;
|
||||
args.PushCellRange(fe.Row, fe.Col, fe.Row2, fe.Col2, self);
|
||||
end;
|
||||
fekCellOffset:
|
||||
begin
|
||||
cell := FindCell(aCell^.Row + SmallInt(fe.Row), ACell^.Col + SmallInt(fe.Col));
|
||||
if cell <> nil then
|
||||
case cell^.CalcState of
|
||||
csNotCalculated: CalcRPNFormula(cell);
|
||||
csCalculating : raise Exception.Create(lpCircularReference);
|
||||
end;
|
||||
args.PushCell(cell, self);
|
||||
end;
|
||||
fekNum:
|
||||
args.PushNumber(fe.DoubleValue, self);
|
||||
fekInteger:
|
||||
@ -1956,17 +1979,6 @@ begin
|
||||
lDestCell^.Col := AToCol;
|
||||
ChangedCell(AToRow, AToCol);
|
||||
ChangedFont(AToRow, AToCol);
|
||||
{
|
||||
lCurStr := AFromWorksheet.ReadAsUTF8Text(AFromRow, AFromCol);
|
||||
lCurUsedFormatting := AFromWorksheet.ReadUsedFormatting(AFromRow, AFromCol);
|
||||
lCurColor := AFromWorksheet.ReadBackgroundColor(AFromRow, AFromCol);
|
||||
WriteUTF8Text(AToRow, AToCol, lCurStr);
|
||||
WriteUsedFormatting(AToRow, AToCol, lCurUsedFormatting);
|
||||
if uffBackgroundColor in lCurUsedFormatting then
|
||||
begin
|
||||
WriteBackgroundColor(AToRow, AToCol, lCurColor);
|
||||
end;
|
||||
}
|
||||
end;
|
||||
|
||||
{@@
|
||||
@ -2059,7 +2071,6 @@ begin
|
||||
if (Result = nil) then
|
||||
begin
|
||||
New(Result);
|
||||
// Result := GetMem(SizeOf(TCell));
|
||||
FillChar(Result^, SizeOf(TCell), #0);
|
||||
|
||||
Result^.Row := ARow;
|
||||
@ -2140,16 +2151,19 @@ var
|
||||
nf: TsNumberFormat;
|
||||
begin
|
||||
Result := false;
|
||||
if ACell <> nil then begin
|
||||
if ACell <> nil then
|
||||
begin
|
||||
parser := TsNumFormatParser.Create(FWorkbook, ACell^.NumberFormatStr);
|
||||
try
|
||||
if parser.Status = psOK then begin
|
||||
nf := parser.NumFormat;
|
||||
if (nf = nfGeneral) or IsDateTimeFormat(nf) then begin
|
||||
if (nf = nfGeneral) or IsDateTimeFormat(nf) then
|
||||
begin
|
||||
ADecimals := 2;
|
||||
ACurrencySymbol := '?';
|
||||
end
|
||||
else begin
|
||||
else
|
||||
begin
|
||||
ADecimals := parser.Decimals;
|
||||
ACurrencySymbol := parser.CurrencySymbol;
|
||||
end;
|
||||
@ -2223,12 +2237,14 @@ var
|
||||
AVLNode: TAVLTreeNode;
|
||||
i: Integer;
|
||||
begin
|
||||
if AForceCalculation then begin
|
||||
if AForceCalculation then
|
||||
begin
|
||||
Result := $FFFFFFFF;
|
||||
// Traverse the tree from lowest to highest.
|
||||
// Since tree primary sort order is on row lowest col could exist anywhere.
|
||||
AVLNode := FCells.FindLowest;
|
||||
While Assigned(AVLNode) do begin
|
||||
while Assigned(AVLNode) do
|
||||
begin
|
||||
Result := Math.Min(Result, PCell(AVLNode.Data)^.Col);
|
||||
AVLNode := FCells.FindSuccessor(AVLNode);
|
||||
end;
|
||||
@ -2240,7 +2256,8 @@ begin
|
||||
// Store the result
|
||||
FFirstColIndex := Result;
|
||||
end
|
||||
else begin
|
||||
else
|
||||
begin
|
||||
Result := FFirstColIndex;
|
||||
if Result = $FFFFFFFF then
|
||||
Result := GetFirstColIndex(true);
|
||||
@ -2266,7 +2283,8 @@ var
|
||||
AVLNode: TAVLTreeNode;
|
||||
i: Integer;
|
||||
begin
|
||||
if AForceCalculation then begin
|
||||
if AForceCalculation then
|
||||
begin
|
||||
Result := 0;
|
||||
// Traverse the tree from lowest to highest.
|
||||
// Since tree primary sort order is on Row
|
||||
@ -2312,7 +2330,8 @@ begin
|
||||
n := GetLastColIndex;
|
||||
c := 0;
|
||||
Result := FindCell(ARow, c);
|
||||
while (result = nil) and (c < n) do begin
|
||||
while (result = nil) and (c < n) do
|
||||
begin
|
||||
inc(c);
|
||||
result := FindCell(ARow, c);
|
||||
end;
|
||||
@ -2331,7 +2350,8 @@ begin
|
||||
n := GetLastColIndex;
|
||||
c := n;
|
||||
Result := FindCell(ARow, c);
|
||||
while (Result = nil) and (c > 0) do begin
|
||||
while (Result = nil) and (c > 0) do
|
||||
begin
|
||||
dec(c);
|
||||
Result := FindCell(ARow, c);
|
||||
end;
|
||||
@ -2356,7 +2376,8 @@ var
|
||||
AVLNode: TAVLTreeNode;
|
||||
i: Integer;
|
||||
begin
|
||||
if AForceCalculation then begin
|
||||
if AForceCalculation then
|
||||
begin
|
||||
Result := $FFFFFFFF;
|
||||
AVLNode := FCells.FindLowest;
|
||||
if Assigned(AVLNode) then
|
||||
@ -2368,7 +2389,8 @@ begin
|
||||
// Store result
|
||||
FFirstRowIndex := Result;
|
||||
end
|
||||
else begin
|
||||
else
|
||||
begin
|
||||
Result := FFirstRowIndex;
|
||||
if Result = $FFFFFFFF then
|
||||
Result := GetFirstRowIndex(true);
|
||||
@ -2394,7 +2416,8 @@ var
|
||||
AVLNode: TAVLTreeNode;
|
||||
i: Integer;
|
||||
begin
|
||||
if AForceCalculation then begin
|
||||
if AForceCalculation then
|
||||
begin
|
||||
Result := 0;
|
||||
AVLNode := FCells.FindHighest;
|
||||
if Assigned(AVLNode) then
|
||||
@ -2420,6 +2443,18 @@ begin
|
||||
Result := GetLastRowIndex;
|
||||
end;
|
||||
|
||||
{@@
|
||||
Returns TRUE if the cell contains a formula (direct or shared, does not matter).
|
||||
}
|
||||
function TsWorksheet.HasFormula(ACell: PCell): Boolean;
|
||||
begin
|
||||
Result := Assigned(ACell) and (
|
||||
(ACell^.SharedFormulaBase <> nil) or
|
||||
(Length(ACell^.RPNFormulaValue) > 0) or
|
||||
(Length(ACell^.FormulaValue.FormulaStr) > 0)
|
||||
);
|
||||
end;
|
||||
|
||||
{@@
|
||||
Reads the contents of a cell and returns an user readable text
|
||||
representing the contents of the cell.
|
||||
@ -2470,7 +2505,8 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring;
|
||||
fmtp, fmtn, fmt0: String;
|
||||
begin
|
||||
Result := '';
|
||||
if not IsNaN(Value) then begin
|
||||
if not IsNaN(Value) then
|
||||
begin
|
||||
if ANumberFormatStr = '' then
|
||||
ANumberFormatStr := BuildDateTimeFormatString(ANumberFormat,
|
||||
Workbook.FormatSettings, ANumberFormatStr);
|
||||
@ -2613,10 +2649,11 @@ begin
|
||||
Result := '';
|
||||
if ACell = nil then
|
||||
exit;
|
||||
if Length(ACell^.RPNFormulaValue) > 0 then
|
||||
Result := ReadRPNFormulaAsString(ACell)
|
||||
else
|
||||
Result := ACell^.FormulaValue.FormulaStr;
|
||||
if HasFormula(ACell) then begin
|
||||
Result := ReadRPNFormulaAsString(ACell);
|
||||
if Result = '' then
|
||||
Result := ACell^.FormulaValue.FormulaStr;
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@
|
||||
@ -2668,6 +2705,7 @@ var
|
||||
s: String;
|
||||
ptr: Pointer;
|
||||
fek: TFEKind;
|
||||
formula: TsRPNFormula;
|
||||
begin
|
||||
Result := '';
|
||||
if ACell = nil then
|
||||
@ -2676,13 +2714,19 @@ begin
|
||||
fs := Workbook.FormatSettings;
|
||||
L := TStringList.Create;
|
||||
try
|
||||
// Take care of shared formulas
|
||||
if ACell^.SharedFormulaBase = nil then
|
||||
formula := ACell^.RPNFormulaValue
|
||||
else
|
||||
formula := ACell^.SharedFormulaBase^.RPNFormulaValue;
|
||||
|
||||
// We store the cell values and operation codes in a stringlist which serves
|
||||
// as kind of stack. Therefore, we do not destroy the original formula array.
|
||||
// We reverse the order of the items because in the next step stringlist
|
||||
// items will subsequently be deleted, and this is much easier when going
|
||||
// in reverse direction.
|
||||
for i := Length(ACell^.RPNFormulaValue)-1 downto 0 do begin
|
||||
elem := ACell^.RPNFormulaValue[i];
|
||||
for i := Length(formula)-1 downto 0 do begin
|
||||
elem := formula[i];
|
||||
ptr := Pointer(elem.ElementKind);
|
||||
case elem.ElementKind of
|
||||
fekNum:
|
||||
@ -2698,6 +2742,8 @@ begin
|
||||
L.AddObject(GetCellString(elem.Row, elem.Col, elem.RelFlags), ptr);
|
||||
fekCellRange:
|
||||
L.AddObject(GetCellRangeString(elem.Row, elem.Col, elem.Row2, elem.Col2, elem.RelFlags), ptr);
|
||||
fekCellOffset:
|
||||
L.AddObject(GetCellString(ACell^.Row + SmallInt(elem.Row), ACell^.Col + SmallInt(elem.Col)), ptr);
|
||||
// Operations:
|
||||
else
|
||||
L.AddObject(FEProps[elem.ElementKind].Symbol, ptr);
|
||||
@ -2752,7 +2798,7 @@ begin
|
||||
end;
|
||||
else
|
||||
if fek >= fekAdd then begin
|
||||
elem := ACell^.RPNFormulaValue[Length(ACell^.RPNFormulaValue) - 1 - i];
|
||||
elem := formula[Length(formula) - 1 - i];
|
||||
s := '';
|
||||
for j:= i+elem.ParamsNum downto i+1 do begin
|
||||
if j < L.Count then begin
|
||||
@ -6964,12 +7010,11 @@ end;
|
||||
Creates a pointer to a new RPN item. This represents an element in the array
|
||||
of token of an RPN formula.
|
||||
|
||||
@return Pointer the the RPN item
|
||||
@return Pointer to the RPN item
|
||||
}
|
||||
function NewRPNItem: PRPNItem;
|
||||
begin
|
||||
New(Result);
|
||||
// Result := GetMem(SizeOf(TRPNItem));
|
||||
FillChar(Result^.FE, SizeOf(Result^.FE), 0);
|
||||
Result^.FE.StringValue := '';
|
||||
end;
|
||||
@ -6979,13 +7024,8 @@ end;
|
||||
}
|
||||
procedure DisposeRPNItem(AItem: PRPNItem);
|
||||
begin
|
||||
if AItem <> nil then begin
|
||||
{
|
||||
AItem.FE.StringValue := '';;
|
||||
FreeMem(AItem, SizeOf(TRPNItem));
|
||||
}
|
||||
if AItem <> nil then
|
||||
Dispose(AItem);
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@
|
||||
@ -7125,6 +7165,25 @@ begin
|
||||
Result^.Next := ANext;
|
||||
end;
|
||||
|
||||
{@@
|
||||
Creates an entry in the RPN array for a relative cell reference as used in
|
||||
shared formulas. The given parameters indicate the relativ offset between
|
||||
the current cell coordinates and a reference rell.
|
||||
|
||||
@param ARowOffset Offset between current row and the row of a reference cell
|
||||
@param AColOffset Offset between current column and the column of a reference cell
|
||||
@param ANext Pointer to the next RPN item in the list
|
||||
}
|
||||
function RPNCellOffset(ARowOffset, AColOffset: Integer; ANext: PRPNItem): PRPNItem;
|
||||
begin
|
||||
Result := NewRPNItem;
|
||||
Result^.FE.ElementKind := fekCellOffset;
|
||||
Result^.FE.Row := ARowOffset;
|
||||
Result^.FE.Col := AColOffset;
|
||||
Result^.FE.RelFlags := [rfRelRow, rfRelCol];
|
||||
Result^.Next := ANext;
|
||||
end;
|
||||
|
||||
{@@
|
||||
Creates an entry in the RPN array with an error value.
|
||||
|
||||
|
@ -76,6 +76,7 @@ type
|
||||
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override;
|
||||
procedure ReadRowInfo(AStream: TStream); override;
|
||||
function ReadRPNFunc(AStream: TStream): Word; override;
|
||||
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal);
|
||||
function ReadRPNTokenArraySize(AStream: TStream): Word; override;
|
||||
procedure ReadStringRecord(AStream: TStream); override;
|
||||
procedure ReadWindow2(AStream: TStream); override;
|
||||
@ -601,7 +602,7 @@ begin
|
||||
|
||||
{ Formula token array }
|
||||
if FWorkbook.ReadFormulas then begin
|
||||
ok := ReadRPNTokenArray(AStream, cell^.RPNFormulaValue);
|
||||
ok := ReadRPNTokenArray(AStream, cell);
|
||||
if not ok then FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
|
||||
end;
|
||||
|
||||
@ -784,6 +785,19 @@ begin
|
||||
Result := b;
|
||||
end;
|
||||
|
||||
{ Reads the cell coordiantes of the top/left cell of a range using a shared formula.
|
||||
This cell contains the rpn token sequence of the formula.
|
||||
Is overridden because BIFF2 has 1 byte for column. }
|
||||
procedure TsSpreadBIFF2Reader.ReadRPNSharedFormulaBase(AStream: TStream;
|
||||
out ARow, ACol: Cardinal);
|
||||
begin
|
||||
// 2 bytes for row of first cell in shared formula
|
||||
ARow := WordLEToN(AStream.ReadWord);
|
||||
// 1 byte for column of first cell in shared formula
|
||||
ACol := AStream.ReadByte;
|
||||
end;
|
||||
|
||||
|
||||
{ Helper funtion for reading of the size of the token array of an RPN formula.
|
||||
Is overridden because BIFF2 uses 1 byte only. }
|
||||
function TsSpreadBIFF2Reader.ReadRPNTokenArraySize(AStream: TStream): Word;
|
||||
|
@ -87,6 +87,8 @@ type
|
||||
procedure ReadRichString(const AStream: TStream);
|
||||
procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal;
|
||||
out AFlags: TsRelFlags); override;
|
||||
procedure ReadRPNCellAddressOffset(AStream: TStream;
|
||||
out ARowOffset, AColOffset: Integer); override;
|
||||
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
||||
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); override;
|
||||
procedure ReadSST(const AStream: TStream);
|
||||
@ -1474,29 +1476,30 @@ begin
|
||||
|
||||
case RecordType of
|
||||
|
||||
INT_EXCEL_ID_BLANK : ReadBlank(AStream);
|
||||
INT_EXCEL_ID_MULBLANK: ReadMulBlank(AStream);
|
||||
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
|
||||
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
|
||||
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
||||
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
|
||||
INT_EXCEL_ID_BLANK : ReadBlank(AStream);
|
||||
INT_EXCEL_ID_MULBLANK : ReadMulBlank(AStream);
|
||||
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
|
||||
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
|
||||
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
||||
INT_EXCEL_ID_SHAREDFMLA: ReadSharedFormula(AStream);
|
||||
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
|
||||
//(RSTRING) This record stores a formatted text cell (Rich-Text).
|
||||
// In BIFF8 it is usually replaced by the LABELSST record. Excel still
|
||||
// uses this record, if it copies formatted text cells to the clipboard.
|
||||
INT_EXCEL_ID_RSTRING : ReadRichString(AStream);
|
||||
INT_EXCEL_ID_RSTRING : ReadRichString(AStream);
|
||||
// (RK) This record represents a cell that contains an RK value
|
||||
// (encoded integer or floating-point value). If a floating-point
|
||||
// value cannot be encoded to an RK value, a NUMBER record will be written.
|
||||
// This record replaces the record INTEGER written in BIFF2.
|
||||
INT_EXCEL_ID_RK : ReadRKValue(AStream);
|
||||
INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream);
|
||||
INT_EXCEL_ID_LABELSST: ReadLabelSST(AStream); //BIFF8 only
|
||||
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
|
||||
INT_EXCEL_ID_ROW : ReadRowInfo(AStream);
|
||||
INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream);
|
||||
INT_EXCEL_ID_PANE : ReadPane(AStream);
|
||||
INT_EXCEL_ID_BOF : ;
|
||||
INT_EXCEL_ID_EOF : SectionEOF := True;
|
||||
INT_EXCEL_ID_RK : ReadRKValue(AStream);
|
||||
INT_EXCEL_ID_MULRK : ReadMulRKValues(AStream);
|
||||
INT_EXCEL_ID_LABELSST : ReadLabelSST(AStream); //BIFF8 only
|
||||
INT_EXCEL_ID_COLINFO : ReadColInfo(AStream);
|
||||
INT_EXCEL_ID_ROW : ReadRowInfo(AStream);
|
||||
INT_EXCEL_ID_WINDOW2 : ReadWindow2(AStream);
|
||||
INT_EXCEL_ID_PANE : ReadPane(AStream);
|
||||
INT_EXCEL_ID_BOF : ;
|
||||
INT_EXCEL_ID_EOF : SectionEOF := True;
|
||||
else
|
||||
// nothing
|
||||
end;
|
||||
@ -1703,6 +1706,24 @@ begin
|
||||
if (c and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow);
|
||||
end;
|
||||
|
||||
{ Read the difference between cell row and column indexed of a cell and a reference
|
||||
cell.
|
||||
Overriding the implementation in xlscommon. }
|
||||
procedure TsSpreadBIFF8Reader.ReadRPNCellAddressOffset(AStream: TStream;
|
||||
out ARowOffset, AColOffset: Integer);
|
||||
var
|
||||
dr: SmallInt;
|
||||
dc: ShortInt;
|
||||
begin
|
||||
// 2 bytes for row offset
|
||||
dr := WordLEToN(AStream.ReadWord);
|
||||
ARowOffset := dr;
|
||||
|
||||
// 2 bytes for column offset
|
||||
dc := Lo(WordLEToN(AStream.ReadWord));
|
||||
AColOffset := dc;
|
||||
end;
|
||||
|
||||
{ Reads a cell range address used in an RPN formula element.
|
||||
Evaluates the corresponding bits to distinguish between absolute and
|
||||
relative addresses.
|
||||
|
@ -60,6 +60,7 @@ const
|
||||
INT_EXCEL_ID_MULBLANK = $00BE; // does not exist before BIFF5
|
||||
INT_EXCEL_ID_XF = $00E0; // BIFF2:$0043, BIFF3:$0243, BIFF4:$0443
|
||||
INT_EXCEL_ID_RSTRING = $00D6; // does not exist before BIFF5
|
||||
INT_EXCEL_ID_SHAREDFMLA = $04BC; // does not exist before BIFF5
|
||||
INT_EXCEL_ID_BOF = $0809; // BIFF2:$0009, BIFF3:$0209; BIFF4:$0409
|
||||
|
||||
{ FONT record constants }
|
||||
@ -105,6 +106,9 @@ const
|
||||
INT_EXCEL_TOKEN_TAREA_R = $25;
|
||||
INT_EXCEL_TOKEN_TAREA_V = $45;
|
||||
INT_EXCEL_TOKEN_TAREA_A = $65;
|
||||
INT_EXCEL_TOKEN_TREFN_R = $2C;
|
||||
INT_EXCEL_TOKEN_TREFN_V = $4C;
|
||||
INT_EXCEL_TOKEN_TREFN_A = $6C;
|
||||
|
||||
{ Function Tokens }
|
||||
// _R: reference; _V: value; _A: array
|
||||
@ -114,9 +118,12 @@ const
|
||||
INT_EXCEL_TOKEN_FUNC_A = $61;
|
||||
|
||||
//VAR: variable number of arguments:
|
||||
INT_EXCEL_TOKEN_FUNCVAR_R = $22;
|
||||
INT_EXCEL_TOKEN_FUNCVAR_V = $42;
|
||||
INT_EXCEL_TOKEN_FUNCVAR_A = $62;
|
||||
INT_EXCEL_TOKEN_FUNCVAR_R = $22;
|
||||
INT_EXCEL_TOKEN_FUNCVAR_V = $42;
|
||||
INT_EXCEL_TOKEN_FUNCVAR_A = $62;
|
||||
|
||||
{ Special tokens }
|
||||
INT_EXCEL_TOKEN_TEXP = $01; // cell belongs to shared formula
|
||||
|
||||
{ Built-in/worksheet functions }
|
||||
INT_EXCEL_SHEET_FUNC_COUNT = 0;
|
||||
@ -431,11 +438,15 @@ type
|
||||
// Read the array of RPN tokens of a formula
|
||||
procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal;
|
||||
out AFlags: TsRelFlags); virtual;
|
||||
procedure ReadRPNCellAddressOffset(AStream: TStream;
|
||||
out ARowOffset, AColOffset: Integer); virtual;
|
||||
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
||||
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); virtual;
|
||||
function ReadRPNFunc(AStream: TStream): Word; virtual;
|
||||
function ReadRPNTokenArray(AStream: TStream; var AFormula: TsRPNFormula): Boolean;
|
||||
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); virtual;
|
||||
function ReadRPNTokenArray(AStream: TStream; ACell: PCell): Boolean;
|
||||
function ReadRPNTokenArraySize(AStream: TStream): word; virtual;
|
||||
procedure ReadSharedFormula(AStream: TStream);
|
||||
|
||||
// Helper function for reading a string with 8-bit length
|
||||
function ReadString_8bitLen(AStream: TStream): String; virtual;
|
||||
@ -542,6 +553,7 @@ const
|
||||
INT_EXCEL_TOKEN_TREFV, {fekCell}
|
||||
INT_EXCEL_TOKEN_TREFR, {fekCellRef}
|
||||
INT_EXCEL_TOKEN_TAREA_R, {fekCellRange}
|
||||
INT_EXCEL_TOKEN_TREFN_V, {fekCellOffset}
|
||||
INT_EXCEL_TOKEN_TNUM, {fekNum}
|
||||
INT_EXCEL_TOKEN_TINT, {fekInteger}
|
||||
INT_EXCEL_TOKEN_TSTR, {fekString}
|
||||
@ -1166,8 +1178,6 @@ var
|
||||
cell: PCell;
|
||||
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
{ BIFF Record data }
|
||||
{ Index to XF Record }
|
||||
ReadRowColXF(AStream, ARow, ACol, XF);
|
||||
|
||||
@ -1230,8 +1240,9 @@ begin
|
||||
|
||||
{ Formula token array }
|
||||
if FWorkbook.ReadFormulas then begin
|
||||
ok := ReadRPNTokenArray(AStream, cell^.RPNFormulaValue);
|
||||
if not ok then FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
|
||||
ok := ReadRPNTokenArray(AStream, cell);
|
||||
if not ok then
|
||||
FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
|
||||
end;
|
||||
|
||||
{Add attributes}
|
||||
@ -1533,6 +1544,24 @@ begin
|
||||
if (r and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow);
|
||||
end;
|
||||
|
||||
{ Read the difference between cell row and column indexed of a cell and a reference
|
||||
cell.
|
||||
Implemented here for BIFF5. BIFF8 must be overridden. Not used by BIFF2. }
|
||||
procedure TsSpreadBIFFReader.ReadRPNCellAddressOffset(AStream: TStream;
|
||||
out ARowOffset, AColOffset: Integer);
|
||||
var
|
||||
r: SmallInt;
|
||||
c: ShortInt;
|
||||
begin
|
||||
// 2 bytes for row
|
||||
r := WordLEToN(AStream.ReadWord) and $3FFF;
|
||||
ARowOffset := r;
|
||||
|
||||
// 1 byte for column
|
||||
c := AStream.ReadByte;
|
||||
AColOffset := c;
|
||||
end;
|
||||
|
||||
{ Reads the cell address used in an RPN formula element. Evaluates the corresponding
|
||||
bits to distinguish between absolute and relative addresses.
|
||||
Implemented here for BIFF2-BIFF5. BIFF8 must be overridden. }
|
||||
@ -1565,8 +1594,22 @@ begin
|
||||
Result := WordLEToN(AStream.ReadWord);
|
||||
end;
|
||||
|
||||
{ Reads the cell coordiantes of the top/left cell of a range using a shared formula.
|
||||
This cell contains the rpn token sequence of the formula.
|
||||
Valid for BIFF3-BIFF8. BIFF2 needs to be overridden (has 1 byte for column). }
|
||||
procedure TsSpreadBIFFReader.ReadRPNSharedFormulaBase(AStream: TStream;
|
||||
out ARow, ACol: Cardinal);
|
||||
begin
|
||||
// 2 bytes for row of first cell in shared formula
|
||||
ARow := WordLEToN(AStream.ReadWord);
|
||||
// 2 bytes for column of first cell in shared formula
|
||||
ACol := WordLEToN(AStream.ReadWord);
|
||||
end;
|
||||
|
||||
{ Reads the array of rpn tokens from the current stream position, creates an
|
||||
rpn formula and stores it in the cell. }
|
||||
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
|
||||
var AFormula: TsRPNFormula): Boolean;
|
||||
ACell: PCell): Boolean;
|
||||
var
|
||||
n: Word;
|
||||
p0: Int64;
|
||||
@ -1576,6 +1619,7 @@ var
|
||||
dblVal: Double = 0.0; // IEEE 8 byte floating point number
|
||||
flags: TsRelFlags;
|
||||
r, c, r2, c2: Cardinal;
|
||||
dr, dc: Integer;
|
||||
fek: TFEKind;
|
||||
func: Word;
|
||||
b: Byte;
|
||||
@ -1603,6 +1647,15 @@ begin
|
||||
ReadRPNCellRangeAddress(AStream, r, c, r2, c2, flags);
|
||||
rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem);
|
||||
end;
|
||||
INT_EXCEL_TOKEN_TREFN_R, INT_EXCEL_TOKEN_TREFN_V:
|
||||
begin
|
||||
ReadRPNCellAddressOffset(AStream, dr, dc);
|
||||
rpnItem := RPNCellOffset(dr, dc, rpnItem);
|
||||
end;
|
||||
INT_EXCEL_TOKEN_TREFN_A:
|
||||
begin
|
||||
raise Exception.Create('Cell range offset not yet implemented. Please report an issue');
|
||||
end;
|
||||
INT_EXCEL_TOKEN_TMISSARG:
|
||||
rpnItem := RPNMissingArg(rpnItem);
|
||||
INT_EXCEL_TOKEN_TSTR:
|
||||
@ -1655,7 +1708,15 @@ begin
|
||||
end;
|
||||
if not found then
|
||||
supported := false;
|
||||
end
|
||||
end;
|
||||
|
||||
INT_EXCEL_TOKEN_TEXP:
|
||||
// Indicates that cell belongs to a shared or array formula. We determine
|
||||
// the base cell of the shared formula and store it in the current cell.
|
||||
begin
|
||||
ReadRPNSharedFormulaBase(AStream, r, c);
|
||||
ACell^.SharedFormulaBase := FWorksheet.FindCell(r, c);
|
||||
end;
|
||||
|
||||
else
|
||||
found := false;
|
||||
@ -1671,11 +1732,11 @@ begin
|
||||
end;
|
||||
if not supported then begin
|
||||
DestroyRPNFormula(rpnItem);
|
||||
SetLength(AFormula, 0);
|
||||
SetLength(ACell^.RPNFormulaValue, 0);
|
||||
Result := false;
|
||||
end
|
||||
else begin
|
||||
AFormula := CreateRPNFormula(rpnItem, true); // true --> we have to flip the order of items!
|
||||
ACell^.RPNFormulaValue := CreateRPNFormula(rpnItem, true); // true --> we have to flip the order of items!
|
||||
Result := true;
|
||||
end;
|
||||
end;
|
||||
@ -1688,6 +1749,40 @@ begin
|
||||
Result := WordLEToN(AStream.ReadWord);
|
||||
end;
|
||||
|
||||
{ Reads a SHAREDFMLA record, i.e. reads cell range coordinates and a rpn
|
||||
formula. The formula is applied to all cells in the range. The formula stored
|
||||
only in the top/left cell of the range. }
|
||||
procedure TsSpreadBIFFReader.ReadSharedFormula(AStream: TStream);
|
||||
var
|
||||
r1, r2, c1, c2: Cardinal;
|
||||
flags: TsRelFlags;
|
||||
b: Byte;
|
||||
ok: Boolean;
|
||||
cell: PCell;
|
||||
begin
|
||||
// Cell range in which the formula is valid
|
||||
r1 := WordLEToN(AStream.ReadWord);
|
||||
r2 := WordLEToN(AStream.ReadWord);
|
||||
c1 := AStream.ReadByte; // 8 bit, even for BIFF8
|
||||
c2 := AStream.ReadByte;
|
||||
|
||||
{ Create cell }
|
||||
if FIsVirtualMode then begin // "Virtual" cell
|
||||
InitCell(r1, c1, FVirtualCell);
|
||||
cell := @FVirtualCell;
|
||||
end else
|
||||
cell := FWorksheet.GetCell(r1, c1); // "Real" cell
|
||||
|
||||
// Unused
|
||||
AStream.ReadByte;
|
||||
|
||||
// Number of existing FORMULA records for this shared formula
|
||||
AStream.ReadByte;
|
||||
|
||||
// RPN formula tokens
|
||||
ok := ReadRPNTokenArray(AStream, cell);
|
||||
end;
|
||||
|
||||
{ Helper function for reading a string with 8-bit length. Here, we implement the
|
||||
version for ansistrings since it is valid for all BIFF versions except for
|
||||
BIFF8 where it has to be overridden. }
|
||||
|
Loading…
Reference in New Issue
Block a user