You've already forked lazarus-ccr
fpspreadsheet: Writing of shared formulas to biff5 and biff8 is working again now.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3525 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -1097,7 +1097,6 @@ type
|
||||
procedure FixCellColors(ACell: PCell);
|
||||
function FixColor(AColor: TsColor): TsColor; virtual;
|
||||
procedure FixFormat(ACell: PCell); virtual;
|
||||
procedure FixRelativeReferences(ACell: PCell; var AElement: TsFormulaElement); virtual;
|
||||
procedure GetSheetDimensions(AWorksheet: TsWorksheet;
|
||||
out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual;
|
||||
procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream);
|
||||
@ -1749,6 +1748,8 @@ end;
|
||||
Helper function which constructs an rpn formula from the cell's string
|
||||
formula. This is needed, for example, when writing a formula to xls biff
|
||||
file format.
|
||||
If the cell belongs to a shared formula the formula is taken from the
|
||||
shared formula base cell, cell references used are adapted accordingly.
|
||||
}
|
||||
function TsWorksheet.BuildRPNFormula(ACell: PCell): TsRPNFormula;
|
||||
var
|
||||
@ -6666,44 +6667,6 @@ begin
|
||||
// to be overridden
|
||||
end;
|
||||
|
||||
{@@
|
||||
Adjusts relative references in the formula element to the position of cell
|
||||
and the shared formula base. }
|
||||
procedure TsCustomSpreadWriter.FixRelativeReferences(ACell: PCell;
|
||||
var AElement: TsFormulaElement);
|
||||
var
|
||||
rowOffset: Integer;
|
||||
colOffset: Integer;
|
||||
begin
|
||||
if (ACell = nil) or (ACell^.SharedFormulaBase = nil) then
|
||||
exit;
|
||||
|
||||
case AElement.ElementKind of
|
||||
fekCell:
|
||||
begin
|
||||
rowOffset := AElement.Row - ACell^.SharedFormulaBase^.Row;
|
||||
colOffset := AElement.Col - ACell^.SharedFormulaBase^.Col;
|
||||
if (rfRelRow in AElement.RelFlags) then
|
||||
AElement.Row := Integer(ACell^.Row) + rowOffset;
|
||||
if (rfRelCol in AElement.RelFlags) then
|
||||
AElement.Col := Integer(ACell^.Col) + colOffset;
|
||||
end;
|
||||
fekCellRange:
|
||||
begin
|
||||
rowOffset := AElement.Row - ACell^.SharedFormulaBase^.Row;
|
||||
colOffset := AElement.Col - ACell^.SharedFormulaBase^.Col;
|
||||
if (rfRelRow in AElement.RelFlags) then
|
||||
AElement.Row := Integer(ACell^.Row) + rowOffset;
|
||||
if (rfRelCol in AElement.RelFlags) then
|
||||
AElement.Col := Integer(ACell^.Col) + colOffset;
|
||||
if (rfRelRow2 in AElement.RelFlags) then
|
||||
AElement.Row2 := Integer(ACell^.Row) + rowOffset;
|
||||
if (rfRelCol2 in AElement.RelFlags) then
|
||||
AElement.Col2 := Integer(ACell^.Col) + colOffset;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@
|
||||
Determines the size of the worksheet to be written. VirtualMode is respected.
|
||||
Is called when the writer needs the size for output. Column and row count
|
||||
|
@ -112,7 +112,6 @@ type
|
||||
procedure WriteXFRecords(AStream: TStream);
|
||||
protected
|
||||
procedure CreateNumFormatList; override;
|
||||
procedure FixRelativeReferences(ACell: PCell; var AElement: TsFormulaElement); override;
|
||||
procedure ListAllNumFormats; override;
|
||||
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override;
|
||||
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
|
||||
@ -993,26 +992,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Since BIFF2 does not support shared formulas it cannot handle the fekCellOffset
|
||||
token. It is replaced by a fekCellValue token and correct relative references. }
|
||||
procedure TsSpreadBIFF2Writer.FixRelativeReferences(ACell: PCell;
|
||||
var AElement: TsFormulaElement);
|
||||
begin
|
||||
inherited FixRelativeReferences(ACell, AElement);
|
||||
|
||||
if (ACell = nil) or (ACell^.SharedFormulaBase = nil) then
|
||||
exit;
|
||||
|
||||
if AElement.ElementKind = fekCellOffset then
|
||||
begin
|
||||
AElement.ElementKind := fekCell;
|
||||
if (rfRelRow in AElement.RelFlags) then
|
||||
AElement.Row := ACell^.Row + Integer(AElement.Row); // AElement.Row means here: "RowOffsset"
|
||||
if (rfRelCol in AElement.RelFlags) then
|
||||
AElement.Col := ACell^.Col + Integer(AElement.Col); // AElement.Col means here: "ColOffsset"
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Determines the cell attributes needed for writing a cell content record, such
|
||||
as WriteLabel, WriteNumber, etc.
|
||||
The cell attributes contain, in bit masks, xf record index, font index, borders, etc.}
|
||||
@ -1627,7 +1606,7 @@ begin
|
||||
if ACell^.SharedFormulaBase <> nil then
|
||||
WriteRPNSharedFormulaLink(AStream, ACell, RPNLength)
|
||||
else
|
||||
WriteRPNTokenArray(AStream, AFormula, true, RPNLength);
|
||||
WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength);
|
||||
|
||||
{ Finally write sizes after we know them }
|
||||
FinalPos := AStream.Position;
|
||||
@ -1660,14 +1639,14 @@ var
|
||||
formula: TsRPNFormula;
|
||||
begin
|
||||
// Create RPN formula from the shared formula base's string formula
|
||||
formula := FWorksheet.BuildRPNFormula(ACell^.SharedFormulaBase);
|
||||
|
||||
// Adapt relative cell references
|
||||
for i:=0 to Length(formula)-1 do
|
||||
FixRelativeReferences(ACell, formula[i]);
|
||||
formula := FWorksheet.BuildRPNFormula(ACell);
|
||||
// Don't use ACell^.SharedFormulaBase here because this lookup is made
|
||||
// by the worksheet automatically.
|
||||
|
||||
// Write adapted copy of shared formula to stream.
|
||||
WriteRPNTokenArray(AStream, formula, true, RPNLength);
|
||||
WriteRPNTokenArray(AStream, ACell, formula, false, RPNLength);
|
||||
// false --> "do not convert cess addresses to relative offsets", because
|
||||
// biff2 does not support shared formulas!
|
||||
|
||||
// Clean up
|
||||
SetLength(formula, 0);
|
||||
|
@ -360,8 +360,8 @@ type
|
||||
procedure WriteRPNResult(AStream: TStream; ACell: PCell);
|
||||
procedure WriteRPNSharedFormulaLink(AStream: TStream; ACell: PCell;
|
||||
var RPNLength: Word); virtual;
|
||||
procedure WriteRPNTokenArray(AStream: TStream; const AFormula: TsRPNFormula;
|
||||
WriteTokenArraySize: Boolean; var RPNLength: Word);
|
||||
procedure WriteRPNTokenArray(AStream: TStream; ACell: PCell;
|
||||
const AFormula: TsRPNFormula; UseRelAddr: Boolean; var RPNLength: Word);
|
||||
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual;
|
||||
// Writes out a SELECTION record
|
||||
procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte);
|
||||
@ -2368,7 +2368,7 @@ begin
|
||||
if ACell^.SharedFormulaBase <> nil then
|
||||
WriteRPNSharedFormulaLink(AStream, ACell, RPNLength)
|
||||
else
|
||||
WriteRPNTokenArray(AStream, AFormula, true, RPNLength);
|
||||
WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength);
|
||||
|
||||
{ Write sizes in the end, after we known them }
|
||||
FinalPos := AStream.Position;
|
||||
@ -2479,10 +2479,12 @@ end;
|
||||
|
||||
{ Writes the token array of the given RPN formula and returns its size }
|
||||
procedure TsSpreadBIFFWriter.WriteRPNTokenArray(AStream: TStream;
|
||||
const AFormula: TsRPNFormula; WriteTokenArraySize: Boolean; var RPNLength: Word);
|
||||
ACell: PCell; const AFormula: TsRPNFormula; UseRelAddr: boolean;
|
||||
var RPNLength: Word);
|
||||
var
|
||||
i: Integer;
|
||||
n: Word;
|
||||
dr, dc: Integer;
|
||||
TokenArraySizePos: Int64;
|
||||
FinalPos: Int64;
|
||||
exprDef: TsExprIdentifierDef;
|
||||
@ -2491,12 +2493,10 @@ var
|
||||
begin
|
||||
RPNLength := 0;
|
||||
|
||||
if WriteTokenArraySize then begin
|
||||
{ The size of the token array is written later, because it's necessary to
|
||||
calculate it first, and this is done at the same time it is written }
|
||||
TokenArraySizePos := AStream.Position;
|
||||
WriteRPNTokenArraySize(AStream, 0);
|
||||
end;
|
||||
{ The size of the token array is written later, because it's necessary to
|
||||
calculate it first, and this is done at the same time it is written }
|
||||
TokenArraySizePos := AStream.Position;
|
||||
WriteRPNTokenArraySize(AStream, 0);
|
||||
|
||||
{ Formula data (RPN token array) }
|
||||
for i := 0 to Length(AFormula) - 1 do begin
|
||||
@ -2513,6 +2513,18 @@ begin
|
||||
primaryExcelCode := TokenIDs[AFormula[i].ElementKind];
|
||||
secondaryExcelCode := 0;
|
||||
end;
|
||||
|
||||
if UseRelAddr then
|
||||
case primaryExcelCode of
|
||||
INT_EXCEL_TOKEN_TREFR : primaryExcelCode := INT_EXCEL_TOKEN_TREFN_R;
|
||||
INT_EXCEL_TOKEN_TREFV : primaryExcelCode := INT_EXCEL_TOKEN_TREFN_V;
|
||||
INT_EXCEL_TOKEN_TREFA : primaryExcelCode := INT_EXCEL_TOKEN_TREFN_A;
|
||||
|
||||
INT_EXCEL_TOKEN_TAREA_R: primaryExcelCode := INT_EXCEL_TOKEN_TAREAN_R;
|
||||
INT_EXCEL_TOKEN_TAREA_V: primaryExcelCode := INT_EXCEL_TOKEN_TAREAN_V;
|
||||
INT_EXCEL_TOKEN_TAREA_A: primaryExcelCode := INT_EXCEL_TOKEN_TAREAN_A;
|
||||
end;
|
||||
|
||||
AStream.WriteByte(primaryExcelCode);
|
||||
inc(RPNLength);
|
||||
|
||||
@ -2520,7 +2532,6 @@ begin
|
||||
case primaryExcelCode of
|
||||
{ Operand Tokens }
|
||||
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA: { fekCell }
|
||||
// INT_EXCEL_TOKEN_TREFN_R, INT_EXCEL_TOKEN_TREFN_V, INT_EXCEL_TOKEN_TREFN_A: { fekCellOffset}
|
||||
begin
|
||||
n := WriteRPNCellAddress(
|
||||
AStream,
|
||||
@ -2545,11 +2556,13 @@ begin
|
||||
INT_EXCEL_TOKEN_TREFN_V,
|
||||
INT_EXCEL_TOKEN_TREFN_A: { fekCellOffset }
|
||||
begin
|
||||
n := WriteRPNCellOffset(
|
||||
AStream,
|
||||
integer(AFormula[i].Row), integer(AFormula[i].Col),
|
||||
AFormula[i].RelFlags
|
||||
);
|
||||
if rfRelRow in AFormula[i].RelFlags
|
||||
then dr := integer(AFormula[i].Row) - ACell^.Row
|
||||
else dr := integer(AFormula[i].Row) - ACell^.SharedFormulaBase^.Row;
|
||||
if rfRelCol in AFormula[i].RelFlags
|
||||
then dc := integer(AFormula[i].Col) - ACell^.Col
|
||||
else dc := integer(AFormula[i].Col) - ACell^.SharedFormulaBase^.Col;
|
||||
n := WriteRPNCellOffset(AStream, dr, dc, AFormula[i].RelFlags);
|
||||
inc(RPNLength, n);
|
||||
end;
|
||||
|
||||
@ -2608,17 +2621,15 @@ begin
|
||||
end; // for
|
||||
|
||||
// Now update the size of the token array.
|
||||
if WriteTokenArraySize then begin
|
||||
finalPos := AStream.Position;
|
||||
AStream.Position := TokenArraySizePos;
|
||||
WriteRPNTokenArraySize(AStream, RPNLength);
|
||||
AStream.Position := finalPos;
|
||||
end;
|
||||
finalPos := AStream.Position;
|
||||
AStream.Position := TokenArraySizePos;
|
||||
WriteRPNTokenArraySize(AStream, RPNLength);
|
||||
AStream.Position := finalPos;
|
||||
end;
|
||||
|
||||
{ Writes the size of the RPN token array. Called from WriteRPNFormula.
|
||||
Valid for BIFF3-BIFF8. Override in BIFF2. }
|
||||
procedure TsSPREADBIFFWriter.WriteRPNTokenArraySize(AStream: TStream; ASize: Word);
|
||||
procedure TsSpreadBIFFWriter.WriteRPNTokenArraySize(AStream: TStream; ASize: Word);
|
||||
begin
|
||||
AStream.WriteWord(WordToLE(ASize));
|
||||
end;
|
||||
@ -2850,11 +2861,11 @@ begin
|
||||
// Create an RPN formula from the shared formula base's string formula
|
||||
// and adjust relative references
|
||||
formula := FWorksheet.BuildRPNFormula(ACell^.SharedFormulaBase);
|
||||
for i:=0 to Length(formula)-1 do
|
||||
{ for i:=0 to Length(formula)-1 do
|
||||
FixRelativeReferences(ACell, formula[i]);
|
||||
|
||||
}
|
||||
// Writes the rpn token array
|
||||
WriteRPNTokenArray(AStream, formula, true, RPNLength);
|
||||
WriteRPNTokenArray(AStream, ACell, formula, true, RPNLength);
|
||||
|
||||
{ Write record size at the end after we known it }
|
||||
finalPos := AStream.Position;
|
||||
|
@ -9,60 +9,63 @@ const
|
||||
{ Formula constants TokenID values }
|
||||
|
||||
{ Binary Operator Tokens 3.6}
|
||||
INT_EXCEL_TOKEN_TADD = $03;
|
||||
INT_EXCEL_TOKEN_TSUB = $04;
|
||||
INT_EXCEL_TOKEN_TMUL = $05;
|
||||
INT_EXCEL_TOKEN_TDIV = $06;
|
||||
INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation ^
|
||||
INT_EXCEL_TOKEN_TCONCAT = $08; // Concatenation &
|
||||
INT_EXCEL_TOKEN_TLT = $09; // Less than <
|
||||
INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal <=
|
||||
INT_EXCEL_TOKEN_TEQ = $0B; // Equal =
|
||||
INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal >=
|
||||
INT_EXCEL_TOKEN_TGT = $0D; // Greater than >
|
||||
INT_EXCEL_TOKEN_TNE = $0E; // Not equal <>
|
||||
INT_EXCEL_TOKEN_TISECT = $0F; // Cell range intersection
|
||||
INT_EXCEL_TOKEN_TLIST = $10; // Cell range list
|
||||
INT_EXCEL_TOKEN_TRANGE = $11; // Cell range
|
||||
INT_EXCEL_TOKEN_TUPLUS = $12; // Unary plus +
|
||||
INT_EXCEL_TOKEN_TUMINUS = $13; // Unary minus +
|
||||
INT_EXCEL_TOKEN_TPERCENT= $14; // Percent (%, divides operand by 100)
|
||||
INT_EXCEL_TOKEN_TPAREN = $15; // Operator in parenthesis
|
||||
INT_EXCEL_TOKEN_TADD = $03;
|
||||
INT_EXCEL_TOKEN_TSUB = $04;
|
||||
INT_EXCEL_TOKEN_TMUL = $05;
|
||||
INT_EXCEL_TOKEN_TDIV = $06;
|
||||
INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation ^
|
||||
INT_EXCEL_TOKEN_TCONCAT = $08; // Concatenation &
|
||||
INT_EXCEL_TOKEN_TLT = $09; // Less than <
|
||||
INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal <=
|
||||
INT_EXCEL_TOKEN_TEQ = $0B; // Equal =
|
||||
INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal >=
|
||||
INT_EXCEL_TOKEN_TGT = $0D; // Greater than >
|
||||
INT_EXCEL_TOKEN_TNE = $0E; // Not equal <>
|
||||
INT_EXCEL_TOKEN_TISECT = $0F; // Cell range intersection
|
||||
INT_EXCEL_TOKEN_TLIST = $10; // Cell range list
|
||||
INT_EXCEL_TOKEN_TRANGE = $11; // Cell range
|
||||
INT_EXCEL_TOKEN_TUPLUS = $12; // Unary plus +
|
||||
INT_EXCEL_TOKEN_TUMINUS = $13; // Unary minus +
|
||||
INT_EXCEL_TOKEN_TPERCENT = $14; // Percent (%, divides operand by 100)
|
||||
INT_EXCEL_TOKEN_TPAREN = $15; // Operator in parenthesis
|
||||
|
||||
{ Constant Operand Tokens, 3.8}
|
||||
INT_EXCEL_TOKEN_TMISSARG= $16; //missing operand
|
||||
INT_EXCEL_TOKEN_TSTR = $17; //string
|
||||
INT_EXCEL_TOKEN_TERR = $1C; //error value
|
||||
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
|
||||
INT_EXCEL_TOKEN_TINT = $1E; //(unsigned) integer
|
||||
INT_EXCEL_TOKEN_TNUM = $1F; //floating-point
|
||||
INT_EXCEL_TOKEN_TMISSARG = $16; //missing operand
|
||||
INT_EXCEL_TOKEN_TSTR = $17; //string
|
||||
INT_EXCEL_TOKEN_TERR = $1C; //error value
|
||||
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
|
||||
INT_EXCEL_TOKEN_TINT = $1E; //(unsigned) integer
|
||||
INT_EXCEL_TOKEN_TNUM = $1F; //floating-point
|
||||
|
||||
{ Operand Tokens }
|
||||
// _R: reference; _V: value; _A: array
|
||||
INT_EXCEL_TOKEN_TREFR = $24;
|
||||
INT_EXCEL_TOKEN_TREFV = $44;
|
||||
INT_EXCEL_TOKEN_TREFA = $64;
|
||||
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;
|
||||
INT_EXCEL_TOKEN_TREFR = $24;
|
||||
INT_EXCEL_TOKEN_TREFV = $44;
|
||||
INT_EXCEL_TOKEN_TREFA = $64;
|
||||
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;
|
||||
INT_EXCEL_TOKEN_TAREAN_R = $2D;
|
||||
INT_EXCEL_TOKEN_TAREAN_V = $4D;
|
||||
INT_EXCEL_TOKEN_TAREAN_A = $6D;
|
||||
|
||||
{ Function Tokens }
|
||||
// _R: reference; _V: value; _A: array
|
||||
// Offset 0: token; offset 1: index to a built-in sheet function ( ➜ 3.111)
|
||||
INT_EXCEL_TOKEN_FUNC_R = $21;
|
||||
INT_EXCEL_TOKEN_FUNC_V = $41;
|
||||
INT_EXCEL_TOKEN_FUNC_A = $61;
|
||||
INT_EXCEL_TOKEN_FUNC_R = $21;
|
||||
INT_EXCEL_TOKEN_FUNC_V = $41;
|
||||
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
|
||||
INT_EXCEL_TOKEN_TEXP = $01; // cell belongs to shared formula
|
||||
|
||||
{ Built-in/worksheet functions }
|
||||
INT_EXCEL_SHEET_FUNC_COUNT = 0;
|
||||
|
Reference in New Issue
Block a user