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 = (
|
TFEKind = (
|
||||||
{ Basic operands }
|
{ Basic operands }
|
||||||
fekCell, fekCellRef, fekCellRange, fekNum, fekInteger, fekString, fekBool,
|
fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
|
||||||
fekErr, fekMissingArg,
|
fekString, fekBool, fekErr, fekMissingArg,
|
||||||
{ Basic operations }
|
{ Basic operations }
|
||||||
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
|
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
|
||||||
fekConcat, // string concatenation
|
fekConcat, // string concatenation
|
||||||
@ -137,11 +137,13 @@ type
|
|||||||
or relative. It is a set consisting of TsRelFlag elements. }
|
or relative. It is a set consisting of TsRelFlag elements. }
|
||||||
TsRelFlags = set of TsRelFlag;
|
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
|
TsFormulaElement = record
|
||||||
ElementKind: TFEKind;
|
ElementKind: TFEKind;
|
||||||
Row, Row2: Word; // zero-based
|
Row, Row2: Cardinal; // zero-based
|
||||||
Col, Col2: Word; // zero-based
|
Col, Col2: Cardinal; // zero-based
|
||||||
Param1, Param2: Word; // Extra parameters
|
Param1, Param2: Word; // Extra parameters
|
||||||
DoubleValue: double;
|
DoubleValue: double;
|
||||||
IntValue: Word;
|
IntValue: Word;
|
||||||
@ -381,6 +383,9 @@ type
|
|||||||
{@@ State flags while calculating formulas }
|
{@@ State flags while calculating formulas }
|
||||||
TsCalcState = (csNotCalculated, csCalculating, csCalculated);
|
TsCalcState = (csNotCalculated, csCalculating, csCalculated);
|
||||||
|
|
||||||
|
{@@ Pointer to a TCell record }
|
||||||
|
PCell = ^TCell;
|
||||||
|
|
||||||
{@@ Cell structure for TsWorksheet
|
{@@ Cell structure for TsWorksheet
|
||||||
The cell record contains information on the location of the cell (row and
|
The cell record contains information on the location of the cell (row and
|
||||||
column index), on the value contained (number, date, text, ...), and on
|
column index), on the value contained (number, date, text, ...), and on
|
||||||
@ -403,6 +408,7 @@ type
|
|||||||
DateTimeValue: TDateTime;
|
DateTimeValue: TDateTime;
|
||||||
BoolValue: Boolean;
|
BoolValue: Boolean;
|
||||||
ErrorValue: TsErrorValue;
|
ErrorValue: TsErrorValue;
|
||||||
|
SharedFormulaBase: PCell; // Cell containing the shared formula
|
||||||
{ Formatting fields }
|
{ Formatting fields }
|
||||||
{ When adding/deleting formatting fields don't forget to update CopyFormat! }
|
{ When adding/deleting formatting fields don't forget to update CopyFormat! }
|
||||||
UsedFormattingFields: TsUsedFormattingFields;
|
UsedFormattingFields: TsUsedFormattingFields;
|
||||||
@ -420,9 +426,6 @@ type
|
|||||||
CalcState: TsCalcState;
|
CalcState: TsCalcState;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ Pointer to a TCell record }
|
|
||||||
PCell = ^TCell;
|
|
||||||
|
|
||||||
const
|
const
|
||||||
// Takes account of effect of cell margins on row height by adding this
|
// 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.
|
// 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 ReadAsDateTime(ACell: PCell; out AResult: TDateTime): Boolean; overload;
|
||||||
function ReadFormulaAsString(ACell: PCell): String;
|
function ReadFormulaAsString(ACell: PCell): String;
|
||||||
function ReadNumericValue(ACell: PCell; out AValue: Double): Boolean;
|
function ReadNumericValue(ACell: PCell; out AValue: Double): Boolean;
|
||||||
function ReadRPNFormulaAsString(ACell: PCell): String;
|
|
||||||
|
|
||||||
{ Reading of cell attributes }
|
{ Reading of cell attributes }
|
||||||
function GetNumberFormatAttributes(ACell: PCell; out ADecimals: Byte;
|
function GetNumberFormatAttributes(ACell: PCell; out ADecimals: Byte;
|
||||||
@ -675,6 +677,8 @@ type
|
|||||||
|
|
||||||
{ Formulas }
|
{ Formulas }
|
||||||
procedure CalcFormulas;
|
procedure CalcFormulas;
|
||||||
|
function HasFormula(ACell: PCell): Boolean;
|
||||||
|
function ReadRPNFormulaAsString(ACell: PCell): String;
|
||||||
|
|
||||||
{ Data manipulation methods - For Cells }
|
{ Data manipulation methods - For Cells }
|
||||||
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
|
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
|
||||||
@ -1141,6 +1145,7 @@ type
|
|||||||
ANext: PRPNItem): PRPNItem; overload;
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags;
|
function RPNCellRange(ARow, ACol, ARow2, ACol2: Integer; AFlags: TsRelFlags;
|
||||||
ANext: PRPNItem): PRPNItem; overload;
|
ANext: PRPNItem): PRPNItem; overload;
|
||||||
|
function RPNCellOffset(ARowOffset, AColOffset: Integer; ANext: PRPNItem): PRPNItem;
|
||||||
function RPNErr(AErrCode: Byte; ANext: PRPNItem): PRPNItem;
|
function RPNErr(AErrCode: Byte; ANext: PRPNItem): PRPNItem;
|
||||||
function RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem;
|
function RPNInteger(AValue: Word; ANext: PRPNItem): PRPNItem;
|
||||||
function RPNMissingArg(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), // fekCell
|
||||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellRef
|
(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), // 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), // fekCellNum
|
||||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellInteger
|
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellInteger
|
||||||
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellString
|
(Symbol:''; MinParams:Byte(-1); MaxParams:Byte(-1); Func:nil), // fekCellString
|
||||||
@ -1767,6 +1773,7 @@ var
|
|||||||
fe: TsFormulaElement;
|
fe: TsFormulaElement;
|
||||||
cell: PCell;
|
cell: PCell;
|
||||||
r,c: Cardinal;
|
r,c: Cardinal;
|
||||||
|
formula: TsRPNFormula;
|
||||||
begin
|
begin
|
||||||
if (Length(ACell^.RPNFormulaValue) = 0) or
|
if (Length(ACell^.RPNFormulaValue) = 0) or
|
||||||
(ACell^.ContentType = cctError)
|
(ACell^.ContentType = cctError)
|
||||||
@ -1777,8 +1784,14 @@ begin
|
|||||||
|
|
||||||
args := TsArgumentStack.Create;
|
args := TsArgumentStack.Create;
|
||||||
try
|
try
|
||||||
for i := 0 to Length(ACell^.RPNFormulaValue) - 1 do begin
|
// Take care of shared formulas
|
||||||
fe := ACell^.RPNFormulaValue[i]; // "fe" means "formula element"
|
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
|
case fe.ElementKind of
|
||||||
fekCell, fekCellRef:
|
fekCell, fekCellRef:
|
||||||
begin
|
begin
|
||||||
@ -1803,6 +1816,16 @@ begin
|
|||||||
end;
|
end;
|
||||||
args.PushCellRange(fe.Row, fe.Col, fe.Row2, fe.Col2, self);
|
args.PushCellRange(fe.Row, fe.Col, fe.Row2, fe.Col2, self);
|
||||||
end;
|
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:
|
fekNum:
|
||||||
args.PushNumber(fe.DoubleValue, self);
|
args.PushNumber(fe.DoubleValue, self);
|
||||||
fekInteger:
|
fekInteger:
|
||||||
@ -1956,17 +1979,6 @@ begin
|
|||||||
lDestCell^.Col := AToCol;
|
lDestCell^.Col := AToCol;
|
||||||
ChangedCell(AToRow, AToCol);
|
ChangedCell(AToRow, AToCol);
|
||||||
ChangedFont(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;
|
end;
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
@ -2059,7 +2071,6 @@ begin
|
|||||||
if (Result = nil) then
|
if (Result = nil) then
|
||||||
begin
|
begin
|
||||||
New(Result);
|
New(Result);
|
||||||
// Result := GetMem(SizeOf(TCell));
|
|
||||||
FillChar(Result^, SizeOf(TCell), #0);
|
FillChar(Result^, SizeOf(TCell), #0);
|
||||||
|
|
||||||
Result^.Row := ARow;
|
Result^.Row := ARow;
|
||||||
@ -2140,16 +2151,19 @@ var
|
|||||||
nf: TsNumberFormat;
|
nf: TsNumberFormat;
|
||||||
begin
|
begin
|
||||||
Result := false;
|
Result := false;
|
||||||
if ACell <> nil then begin
|
if ACell <> nil then
|
||||||
|
begin
|
||||||
parser := TsNumFormatParser.Create(FWorkbook, ACell^.NumberFormatStr);
|
parser := TsNumFormatParser.Create(FWorkbook, ACell^.NumberFormatStr);
|
||||||
try
|
try
|
||||||
if parser.Status = psOK then begin
|
if parser.Status = psOK then begin
|
||||||
nf := parser.NumFormat;
|
nf := parser.NumFormat;
|
||||||
if (nf = nfGeneral) or IsDateTimeFormat(nf) then begin
|
if (nf = nfGeneral) or IsDateTimeFormat(nf) then
|
||||||
|
begin
|
||||||
ADecimals := 2;
|
ADecimals := 2;
|
||||||
ACurrencySymbol := '?';
|
ACurrencySymbol := '?';
|
||||||
end
|
end
|
||||||
else begin
|
else
|
||||||
|
begin
|
||||||
ADecimals := parser.Decimals;
|
ADecimals := parser.Decimals;
|
||||||
ACurrencySymbol := parser.CurrencySymbol;
|
ACurrencySymbol := parser.CurrencySymbol;
|
||||||
end;
|
end;
|
||||||
@ -2223,12 +2237,14 @@ var
|
|||||||
AVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
if AForceCalculation then begin
|
if AForceCalculation then
|
||||||
|
begin
|
||||||
Result := $FFFFFFFF;
|
Result := $FFFFFFFF;
|
||||||
// Traverse the tree from lowest to highest.
|
// Traverse the tree from lowest to highest.
|
||||||
// Since tree primary sort order is on row lowest col could exist anywhere.
|
// Since tree primary sort order is on row lowest col could exist anywhere.
|
||||||
AVLNode := FCells.FindLowest;
|
AVLNode := FCells.FindLowest;
|
||||||
While Assigned(AVLNode) do begin
|
while Assigned(AVLNode) do
|
||||||
|
begin
|
||||||
Result := Math.Min(Result, PCell(AVLNode.Data)^.Col);
|
Result := Math.Min(Result, PCell(AVLNode.Data)^.Col);
|
||||||
AVLNode := FCells.FindSuccessor(AVLNode);
|
AVLNode := FCells.FindSuccessor(AVLNode);
|
||||||
end;
|
end;
|
||||||
@ -2240,7 +2256,8 @@ begin
|
|||||||
// Store the result
|
// Store the result
|
||||||
FFirstColIndex := Result;
|
FFirstColIndex := Result;
|
||||||
end
|
end
|
||||||
else begin
|
else
|
||||||
|
begin
|
||||||
Result := FFirstColIndex;
|
Result := FFirstColIndex;
|
||||||
if Result = $FFFFFFFF then
|
if Result = $FFFFFFFF then
|
||||||
Result := GetFirstColIndex(true);
|
Result := GetFirstColIndex(true);
|
||||||
@ -2266,7 +2283,8 @@ var
|
|||||||
AVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
if AForceCalculation then begin
|
if AForceCalculation then
|
||||||
|
begin
|
||||||
Result := 0;
|
Result := 0;
|
||||||
// Traverse the tree from lowest to highest.
|
// Traverse the tree from lowest to highest.
|
||||||
// Since tree primary sort order is on Row
|
// Since tree primary sort order is on Row
|
||||||
@ -2312,7 +2330,8 @@ begin
|
|||||||
n := GetLastColIndex;
|
n := GetLastColIndex;
|
||||||
c := 0;
|
c := 0;
|
||||||
Result := FindCell(ARow, c);
|
Result := FindCell(ARow, c);
|
||||||
while (result = nil) and (c < n) do begin
|
while (result = nil) and (c < n) do
|
||||||
|
begin
|
||||||
inc(c);
|
inc(c);
|
||||||
result := FindCell(ARow, c);
|
result := FindCell(ARow, c);
|
||||||
end;
|
end;
|
||||||
@ -2331,7 +2350,8 @@ begin
|
|||||||
n := GetLastColIndex;
|
n := GetLastColIndex;
|
||||||
c := n;
|
c := n;
|
||||||
Result := FindCell(ARow, c);
|
Result := FindCell(ARow, c);
|
||||||
while (Result = nil) and (c > 0) do begin
|
while (Result = nil) and (c > 0) do
|
||||||
|
begin
|
||||||
dec(c);
|
dec(c);
|
||||||
Result := FindCell(ARow, c);
|
Result := FindCell(ARow, c);
|
||||||
end;
|
end;
|
||||||
@ -2356,7 +2376,8 @@ var
|
|||||||
AVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
if AForceCalculation then begin
|
if AForceCalculation then
|
||||||
|
begin
|
||||||
Result := $FFFFFFFF;
|
Result := $FFFFFFFF;
|
||||||
AVLNode := FCells.FindLowest;
|
AVLNode := FCells.FindLowest;
|
||||||
if Assigned(AVLNode) then
|
if Assigned(AVLNode) then
|
||||||
@ -2368,7 +2389,8 @@ begin
|
|||||||
// Store result
|
// Store result
|
||||||
FFirstRowIndex := Result;
|
FFirstRowIndex := Result;
|
||||||
end
|
end
|
||||||
else begin
|
else
|
||||||
|
begin
|
||||||
Result := FFirstRowIndex;
|
Result := FFirstRowIndex;
|
||||||
if Result = $FFFFFFFF then
|
if Result = $FFFFFFFF then
|
||||||
Result := GetFirstRowIndex(true);
|
Result := GetFirstRowIndex(true);
|
||||||
@ -2394,7 +2416,8 @@ var
|
|||||||
AVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
if AForceCalculation then begin
|
if AForceCalculation then
|
||||||
|
begin
|
||||||
Result := 0;
|
Result := 0;
|
||||||
AVLNode := FCells.FindHighest;
|
AVLNode := FCells.FindHighest;
|
||||||
if Assigned(AVLNode) then
|
if Assigned(AVLNode) then
|
||||||
@ -2420,6 +2443,18 @@ begin
|
|||||||
Result := GetLastRowIndex;
|
Result := GetLastRowIndex;
|
||||||
end;
|
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
|
Reads the contents of a cell and returns an user readable text
|
||||||
representing the contents of the cell.
|
representing the contents of the cell.
|
||||||
@ -2470,7 +2505,8 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring;
|
|||||||
fmtp, fmtn, fmt0: String;
|
fmtp, fmtn, fmt0: String;
|
||||||
begin
|
begin
|
||||||
Result := '';
|
Result := '';
|
||||||
if not IsNaN(Value) then begin
|
if not IsNaN(Value) then
|
||||||
|
begin
|
||||||
if ANumberFormatStr = '' then
|
if ANumberFormatStr = '' then
|
||||||
ANumberFormatStr := BuildDateTimeFormatString(ANumberFormat,
|
ANumberFormatStr := BuildDateTimeFormatString(ANumberFormat,
|
||||||
Workbook.FormatSettings, ANumberFormatStr);
|
Workbook.FormatSettings, ANumberFormatStr);
|
||||||
@ -2613,11 +2649,12 @@ begin
|
|||||||
Result := '';
|
Result := '';
|
||||||
if ACell = nil then
|
if ACell = nil then
|
||||||
exit;
|
exit;
|
||||||
if Length(ACell^.RPNFormulaValue) > 0 then
|
if HasFormula(ACell) then begin
|
||||||
Result := ReadRPNFormulaAsString(ACell)
|
Result := ReadRPNFormulaAsString(ACell);
|
||||||
else
|
if Result = '' then
|
||||||
Result := ACell^.FormulaValue.FormulaStr;
|
Result := ACell^.FormulaValue.FormulaStr;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
Returns to numeric equivalent of the cell contents. This is the NumberValue
|
Returns to numeric equivalent of the cell contents. This is the NumberValue
|
||||||
@ -2668,6 +2705,7 @@ var
|
|||||||
s: String;
|
s: String;
|
||||||
ptr: Pointer;
|
ptr: Pointer;
|
||||||
fek: TFEKind;
|
fek: TFEKind;
|
||||||
|
formula: TsRPNFormula;
|
||||||
begin
|
begin
|
||||||
Result := '';
|
Result := '';
|
||||||
if ACell = nil then
|
if ACell = nil then
|
||||||
@ -2676,13 +2714,19 @@ begin
|
|||||||
fs := Workbook.FormatSettings;
|
fs := Workbook.FormatSettings;
|
||||||
L := TStringList.Create;
|
L := TStringList.Create;
|
||||||
try
|
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
|
// 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.
|
// 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
|
// 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
|
// items will subsequently be deleted, and this is much easier when going
|
||||||
// in reverse direction.
|
// in reverse direction.
|
||||||
for i := Length(ACell^.RPNFormulaValue)-1 downto 0 do begin
|
for i := Length(formula)-1 downto 0 do begin
|
||||||
elem := ACell^.RPNFormulaValue[i];
|
elem := formula[i];
|
||||||
ptr := Pointer(elem.ElementKind);
|
ptr := Pointer(elem.ElementKind);
|
||||||
case elem.ElementKind of
|
case elem.ElementKind of
|
||||||
fekNum:
|
fekNum:
|
||||||
@ -2698,6 +2742,8 @@ begin
|
|||||||
L.AddObject(GetCellString(elem.Row, elem.Col, elem.RelFlags), ptr);
|
L.AddObject(GetCellString(elem.Row, elem.Col, elem.RelFlags), ptr);
|
||||||
fekCellRange:
|
fekCellRange:
|
||||||
L.AddObject(GetCellRangeString(elem.Row, elem.Col, elem.Row2, elem.Col2, elem.RelFlags), ptr);
|
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:
|
// Operations:
|
||||||
else
|
else
|
||||||
L.AddObject(FEProps[elem.ElementKind].Symbol, ptr);
|
L.AddObject(FEProps[elem.ElementKind].Symbol, ptr);
|
||||||
@ -2752,7 +2798,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
else
|
else
|
||||||
if fek >= fekAdd then begin
|
if fek >= fekAdd then begin
|
||||||
elem := ACell^.RPNFormulaValue[Length(ACell^.RPNFormulaValue) - 1 - i];
|
elem := formula[Length(formula) - 1 - i];
|
||||||
s := '';
|
s := '';
|
||||||
for j:= i+elem.ParamsNum downto i+1 do begin
|
for j:= i+elem.ParamsNum downto i+1 do begin
|
||||||
if j < L.Count then 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
|
Creates a pointer to a new RPN item. This represents an element in the array
|
||||||
of token of an RPN formula.
|
of token of an RPN formula.
|
||||||
|
|
||||||
@return Pointer the the RPN item
|
@return Pointer to the RPN item
|
||||||
}
|
}
|
||||||
function NewRPNItem: PRPNItem;
|
function NewRPNItem: PRPNItem;
|
||||||
begin
|
begin
|
||||||
New(Result);
|
New(Result);
|
||||||
// Result := GetMem(SizeOf(TRPNItem));
|
|
||||||
FillChar(Result^.FE, SizeOf(Result^.FE), 0);
|
FillChar(Result^.FE, SizeOf(Result^.FE), 0);
|
||||||
Result^.FE.StringValue := '';
|
Result^.FE.StringValue := '';
|
||||||
end;
|
end;
|
||||||
@ -6979,14 +7024,9 @@ end;
|
|||||||
}
|
}
|
||||||
procedure DisposeRPNItem(AItem: PRPNItem);
|
procedure DisposeRPNItem(AItem: PRPNItem);
|
||||||
begin
|
begin
|
||||||
if AItem <> nil then begin
|
if AItem <> nil then
|
||||||
{
|
|
||||||
AItem.FE.StringValue := '';;
|
|
||||||
FreeMem(AItem, SizeOf(TRPNItem));
|
|
||||||
}
|
|
||||||
Dispose(AItem);
|
Dispose(AItem);
|
||||||
end;
|
end;
|
||||||
end;
|
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
Creates a boolean value entry in the RPN array.
|
Creates a boolean value entry in the RPN array.
|
||||||
@ -7125,6 +7165,25 @@ begin
|
|||||||
Result^.Next := ANext;
|
Result^.Next := ANext;
|
||||||
end;
|
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.
|
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 ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override;
|
||||||
procedure ReadRowInfo(AStream: TStream); override;
|
procedure ReadRowInfo(AStream: TStream); override;
|
||||||
function ReadRPNFunc(AStream: TStream): Word; override;
|
function ReadRPNFunc(AStream: TStream): Word; override;
|
||||||
|
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal);
|
||||||
function ReadRPNTokenArraySize(AStream: TStream): Word; override;
|
function ReadRPNTokenArraySize(AStream: TStream): Word; override;
|
||||||
procedure ReadStringRecord(AStream: TStream); override;
|
procedure ReadStringRecord(AStream: TStream); override;
|
||||||
procedure ReadWindow2(AStream: TStream); override;
|
procedure ReadWindow2(AStream: TStream); override;
|
||||||
@ -601,7 +602,7 @@ begin
|
|||||||
|
|
||||||
{ Formula token array }
|
{ Formula token array }
|
||||||
if FWorkbook.ReadFormulas then begin
|
if FWorkbook.ReadFormulas then begin
|
||||||
ok := ReadRPNTokenArray(AStream, cell^.RPNFormulaValue);
|
ok := ReadRPNTokenArray(AStream, cell);
|
||||||
if not ok then FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
|
if not ok then FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -784,6 +785,19 @@ begin
|
|||||||
Result := b;
|
Result := b;
|
||||||
end;
|
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.
|
{ Helper funtion for reading of the size of the token array of an RPN formula.
|
||||||
Is overridden because BIFF2 uses 1 byte only. }
|
Is overridden because BIFF2 uses 1 byte only. }
|
||||||
function TsSpreadBIFF2Reader.ReadRPNTokenArraySize(AStream: TStream): Word;
|
function TsSpreadBIFF2Reader.ReadRPNTokenArraySize(AStream: TStream): Word;
|
||||||
|
@ -87,6 +87,8 @@ type
|
|||||||
procedure ReadRichString(const AStream: TStream);
|
procedure ReadRichString(const AStream: TStream);
|
||||||
procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal;
|
procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal;
|
||||||
out AFlags: TsRelFlags); override;
|
out AFlags: TsRelFlags); override;
|
||||||
|
procedure ReadRPNCellAddressOffset(AStream: TStream;
|
||||||
|
out ARowOffset, AColOffset: Integer); override;
|
||||||
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
||||||
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); override;
|
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); override;
|
||||||
procedure ReadSST(const AStream: TStream);
|
procedure ReadSST(const AStream: TStream);
|
||||||
@ -1479,6 +1481,7 @@ begin
|
|||||||
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
|
INT_EXCEL_ID_NUMBER : ReadNumber(AStream);
|
||||||
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
|
INT_EXCEL_ID_LABEL : ReadLabel(AStream);
|
||||||
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
INT_EXCEL_ID_FORMULA : ReadFormula(AStream);
|
||||||
|
INT_EXCEL_ID_SHAREDFMLA: ReadSharedFormula(AStream);
|
||||||
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
|
INT_EXCEL_ID_STRING : ReadStringRecord(AStream);
|
||||||
//(RSTRING) This record stores a formatted text cell (Rich-Text).
|
//(RSTRING) This record stores a formatted text cell (Rich-Text).
|
||||||
// In BIFF8 it is usually replaced by the LABELSST record. Excel still
|
// In BIFF8 it is usually replaced by the LABELSST record. Excel still
|
||||||
@ -1703,6 +1706,24 @@ begin
|
|||||||
if (c and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow);
|
if (c and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow);
|
||||||
end;
|
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.
|
{ Reads a cell range address used in an RPN formula element.
|
||||||
Evaluates the corresponding bits to distinguish between absolute and
|
Evaluates the corresponding bits to distinguish between absolute and
|
||||||
relative addresses.
|
relative addresses.
|
||||||
|
@ -60,6 +60,7 @@ const
|
|||||||
INT_EXCEL_ID_MULBLANK = $00BE; // does not exist before BIFF5
|
INT_EXCEL_ID_MULBLANK = $00BE; // does not exist before BIFF5
|
||||||
INT_EXCEL_ID_XF = $00E0; // BIFF2:$0043, BIFF3:$0243, BIFF4:$0443
|
INT_EXCEL_ID_XF = $00E0; // BIFF2:$0043, BIFF3:$0243, BIFF4:$0443
|
||||||
INT_EXCEL_ID_RSTRING = $00D6; // does not exist before BIFF5
|
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
|
INT_EXCEL_ID_BOF = $0809; // BIFF2:$0009, BIFF3:$0209; BIFF4:$0409
|
||||||
|
|
||||||
{ FONT record constants }
|
{ FONT record constants }
|
||||||
@ -105,6 +106,9 @@ const
|
|||||||
INT_EXCEL_TOKEN_TAREA_R = $25;
|
INT_EXCEL_TOKEN_TAREA_R = $25;
|
||||||
INT_EXCEL_TOKEN_TAREA_V = $45;
|
INT_EXCEL_TOKEN_TAREA_V = $45;
|
||||||
INT_EXCEL_TOKEN_TAREA_A = $65;
|
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 }
|
{ Function Tokens }
|
||||||
// _R: reference; _V: value; _A: array
|
// _R: reference; _V: value; _A: array
|
||||||
@ -118,6 +122,9 @@ const
|
|||||||
INT_EXCEL_TOKEN_FUNCVAR_V = $42;
|
INT_EXCEL_TOKEN_FUNCVAR_V = $42;
|
||||||
INT_EXCEL_TOKEN_FUNCVAR_A = $62;
|
INT_EXCEL_TOKEN_FUNCVAR_A = $62;
|
||||||
|
|
||||||
|
{ Special tokens }
|
||||||
|
INT_EXCEL_TOKEN_TEXP = $01; // cell belongs to shared formula
|
||||||
|
|
||||||
{ Built-in/worksheet functions }
|
{ Built-in/worksheet functions }
|
||||||
INT_EXCEL_SHEET_FUNC_COUNT = 0;
|
INT_EXCEL_SHEET_FUNC_COUNT = 0;
|
||||||
INT_EXCEL_SHEET_FUNC_IF = 1;
|
INT_EXCEL_SHEET_FUNC_IF = 1;
|
||||||
@ -431,11 +438,15 @@ type
|
|||||||
// Read the array of RPN tokens of a formula
|
// Read the array of RPN tokens of a formula
|
||||||
procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal;
|
procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal;
|
||||||
out AFlags: TsRelFlags); virtual;
|
out AFlags: TsRelFlags); virtual;
|
||||||
|
procedure ReadRPNCellAddressOffset(AStream: TStream;
|
||||||
|
out ARowOffset, AColOffset: Integer); virtual;
|
||||||
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
procedure ReadRPNCellRangeAddress(AStream: TStream;
|
||||||
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); virtual;
|
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); virtual;
|
||||||
function ReadRPNFunc(AStream: TStream): Word; 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;
|
function ReadRPNTokenArraySize(AStream: TStream): word; virtual;
|
||||||
|
procedure ReadSharedFormula(AStream: TStream);
|
||||||
|
|
||||||
// Helper function for reading a string with 8-bit length
|
// Helper function for reading a string with 8-bit length
|
||||||
function ReadString_8bitLen(AStream: TStream): String; virtual;
|
function ReadString_8bitLen(AStream: TStream): String; virtual;
|
||||||
@ -542,6 +553,7 @@ const
|
|||||||
INT_EXCEL_TOKEN_TREFV, {fekCell}
|
INT_EXCEL_TOKEN_TREFV, {fekCell}
|
||||||
INT_EXCEL_TOKEN_TREFR, {fekCellRef}
|
INT_EXCEL_TOKEN_TREFR, {fekCellRef}
|
||||||
INT_EXCEL_TOKEN_TAREA_R, {fekCellRange}
|
INT_EXCEL_TOKEN_TAREA_R, {fekCellRange}
|
||||||
|
INT_EXCEL_TOKEN_TREFN_V, {fekCellOffset}
|
||||||
INT_EXCEL_TOKEN_TNUM, {fekNum}
|
INT_EXCEL_TOKEN_TNUM, {fekNum}
|
||||||
INT_EXCEL_TOKEN_TINT, {fekInteger}
|
INT_EXCEL_TOKEN_TINT, {fekInteger}
|
||||||
INT_EXCEL_TOKEN_TSTR, {fekString}
|
INT_EXCEL_TOKEN_TSTR, {fekString}
|
||||||
@ -1166,8 +1178,6 @@ var
|
|||||||
cell: PCell;
|
cell: PCell;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
{ BIFF Record header }
|
|
||||||
{ BIFF Record data }
|
|
||||||
{ Index to XF Record }
|
{ Index to XF Record }
|
||||||
ReadRowColXF(AStream, ARow, ACol, XF);
|
ReadRowColXF(AStream, ARow, ACol, XF);
|
||||||
|
|
||||||
@ -1230,8 +1240,9 @@ begin
|
|||||||
|
|
||||||
{ Formula token array }
|
{ Formula token array }
|
||||||
if FWorkbook.ReadFormulas then begin
|
if FWorkbook.ReadFormulas then begin
|
||||||
ok := ReadRPNTokenArray(AStream, cell^.RPNFormulaValue);
|
ok := ReadRPNTokenArray(AStream, cell);
|
||||||
if not ok then FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
|
if not ok then
|
||||||
|
FWorksheet.WriteErrorValue(cell, errFormulaNotSupported);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{Add attributes}
|
{Add attributes}
|
||||||
@ -1533,6 +1544,24 @@ begin
|
|||||||
if (r and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow);
|
if (r and MASK_EXCEL_RELATIVE_ROW <> 0) then Include(AFlags, rfRelRow);
|
||||||
end;
|
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
|
{ Reads the cell address used in an RPN formula element. Evaluates the corresponding
|
||||||
bits to distinguish between absolute and relative addresses.
|
bits to distinguish between absolute and relative addresses.
|
||||||
Implemented here for BIFF2-BIFF5. BIFF8 must be overridden. }
|
Implemented here for BIFF2-BIFF5. BIFF8 must be overridden. }
|
||||||
@ -1565,8 +1594,22 @@ begin
|
|||||||
Result := WordLEToN(AStream.ReadWord);
|
Result := WordLEToN(AStream.ReadWord);
|
||||||
end;
|
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;
|
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
|
||||||
var AFormula: TsRPNFormula): Boolean;
|
ACell: PCell): Boolean;
|
||||||
var
|
var
|
||||||
n: Word;
|
n: Word;
|
||||||
p0: Int64;
|
p0: Int64;
|
||||||
@ -1576,6 +1619,7 @@ var
|
|||||||
dblVal: Double = 0.0; // IEEE 8 byte floating point number
|
dblVal: Double = 0.0; // IEEE 8 byte floating point number
|
||||||
flags: TsRelFlags;
|
flags: TsRelFlags;
|
||||||
r, c, r2, c2: Cardinal;
|
r, c, r2, c2: Cardinal;
|
||||||
|
dr, dc: Integer;
|
||||||
fek: TFEKind;
|
fek: TFEKind;
|
||||||
func: Word;
|
func: Word;
|
||||||
b: Byte;
|
b: Byte;
|
||||||
@ -1603,6 +1647,15 @@ begin
|
|||||||
ReadRPNCellRangeAddress(AStream, r, c, r2, c2, flags);
|
ReadRPNCellRangeAddress(AStream, r, c, r2, c2, flags);
|
||||||
rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem);
|
rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem);
|
||||||
end;
|
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:
|
INT_EXCEL_TOKEN_TMISSARG:
|
||||||
rpnItem := RPNMissingArg(rpnItem);
|
rpnItem := RPNMissingArg(rpnItem);
|
||||||
INT_EXCEL_TOKEN_TSTR:
|
INT_EXCEL_TOKEN_TSTR:
|
||||||
@ -1655,7 +1708,15 @@ begin
|
|||||||
end;
|
end;
|
||||||
if not found then
|
if not found then
|
||||||
supported := false;
|
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
|
else
|
||||||
found := false;
|
found := false;
|
||||||
@ -1671,11 +1732,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
if not supported then begin
|
if not supported then begin
|
||||||
DestroyRPNFormula(rpnItem);
|
DestroyRPNFormula(rpnItem);
|
||||||
SetLength(AFormula, 0);
|
SetLength(ACell^.RPNFormulaValue, 0);
|
||||||
Result := false;
|
Result := false;
|
||||||
end
|
end
|
||||||
else begin
|
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;
|
Result := true;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -1688,6 +1749,40 @@ begin
|
|||||||
Result := WordLEToN(AStream.ReadWord);
|
Result := WordLEToN(AStream.ReadWord);
|
||||||
end;
|
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
|
{ 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
|
version for ansistrings since it is valid for all BIFF versions except for
|
||||||
BIFF8 where it has to be overridden. }
|
BIFF8 where it has to be overridden. }
|
||||||
|
Loading…
Reference in New Issue
Block a user