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:
wp_xxyyzz
2014-09-04 15:27:28 +00:00
parent 2d26e91522
commit cbc6beae9a
4 changed files with 90 additions and 134 deletions

View File

@ -1097,7 +1097,6 @@ type
procedure FixCellColors(ACell: PCell); procedure FixCellColors(ACell: PCell);
function FixColor(AColor: TsColor): TsColor; virtual; function FixColor(AColor: TsColor): TsColor; virtual;
procedure FixFormat(ACell: PCell); virtual; procedure FixFormat(ACell: PCell); virtual;
procedure FixRelativeReferences(ACell: PCell; var AElement: TsFormulaElement); virtual;
procedure GetSheetDimensions(AWorksheet: TsWorksheet; procedure GetSheetDimensions(AWorksheet: TsWorksheet;
out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual; out AFirstRow, ALastRow, AFirstCol, ALastCol: Cardinal); virtual;
procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream); procedure ListAllFormattingStylesCallback(ACell: PCell; AStream: TStream);
@ -1749,6 +1748,8 @@ end;
Helper function which constructs an rpn formula from the cell's string 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 formula. This is needed, for example, when writing a formula to xls biff
file format. 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; function TsWorksheet.BuildRPNFormula(ACell: PCell): TsRPNFormula;
var var
@ -6666,44 +6667,6 @@ begin
// to be overridden // to be overridden
end; 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. 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 Is called when the writer needs the size for output. Column and row count

View File

@ -112,7 +112,6 @@ type
procedure WriteXFRecords(AStream: TStream); procedure WriteXFRecords(AStream: TStream);
protected protected
procedure CreateNumFormatList; override; procedure CreateNumFormatList; override;
procedure FixRelativeReferences(ACell: PCell; var AElement: TsFormulaElement); override;
procedure ListAllNumFormats; override; procedure ListAllNumFormats; override;
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override; procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal; ACell: PCell); override;
procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData; procedure WriteFormat(AStream: TStream; AFormatData: TsNumFormatData;
@ -993,26 +992,6 @@ begin
end; end;
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 { Determines the cell attributes needed for writing a cell content record, such
as WriteLabel, WriteNumber, etc. as WriteLabel, WriteNumber, etc.
The cell attributes contain, in bit masks, xf record index, font index, borders, 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 if ACell^.SharedFormulaBase <> nil then
WriteRPNSharedFormulaLink(AStream, ACell, RPNLength) WriteRPNSharedFormulaLink(AStream, ACell, RPNLength)
else else
WriteRPNTokenArray(AStream, AFormula, true, RPNLength); WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength);
{ Finally write sizes after we know them } { Finally write sizes after we know them }
FinalPos := AStream.Position; FinalPos := AStream.Position;
@ -1660,14 +1639,14 @@ var
formula: TsRPNFormula; formula: TsRPNFormula;
begin begin
// Create RPN formula from the shared formula base's string formula // Create RPN formula from the shared formula base's string formula
formula := FWorksheet.BuildRPNFormula(ACell^.SharedFormulaBase); formula := FWorksheet.BuildRPNFormula(ACell);
// Don't use ACell^.SharedFormulaBase here because this lookup is made
// Adapt relative cell references // by the worksheet automatically.
for i:=0 to Length(formula)-1 do
FixRelativeReferences(ACell, formula[i]);
// Write adapted copy of shared formula to stream. // 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 // Clean up
SetLength(formula, 0); SetLength(formula, 0);

View File

@ -360,8 +360,8 @@ type
procedure WriteRPNResult(AStream: TStream; ACell: PCell); procedure WriteRPNResult(AStream: TStream; ACell: PCell);
procedure WriteRPNSharedFormulaLink(AStream: TStream; ACell: PCell; procedure WriteRPNSharedFormulaLink(AStream: TStream; ACell: PCell;
var RPNLength: Word); virtual; var RPNLength: Word); virtual;
procedure WriteRPNTokenArray(AStream: TStream; const AFormula: TsRPNFormula; procedure WriteRPNTokenArray(AStream: TStream; ACell: PCell;
WriteTokenArraySize: Boolean; var RPNLength: Word); const AFormula: TsRPNFormula; UseRelAddr: Boolean; var RPNLength: Word);
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual; procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual;
// Writes out a SELECTION record // Writes out a SELECTION record
procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte); procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte);
@ -2368,7 +2368,7 @@ begin
if ACell^.SharedFormulaBase <> nil then if ACell^.SharedFormulaBase <> nil then
WriteRPNSharedFormulaLink(AStream, ACell, RPNLength) WriteRPNSharedFormulaLink(AStream, ACell, RPNLength)
else else
WriteRPNTokenArray(AStream, AFormula, true, RPNLength); WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength);
{ Write sizes in the end, after we known them } { Write sizes in the end, after we known them }
FinalPos := AStream.Position; FinalPos := AStream.Position;
@ -2479,10 +2479,12 @@ end;
{ Writes the token array of the given RPN formula and returns its size } { Writes the token array of the given RPN formula and returns its size }
procedure TsSpreadBIFFWriter.WriteRPNTokenArray(AStream: TStream; procedure TsSpreadBIFFWriter.WriteRPNTokenArray(AStream: TStream;
const AFormula: TsRPNFormula; WriteTokenArraySize: Boolean; var RPNLength: Word); ACell: PCell; const AFormula: TsRPNFormula; UseRelAddr: boolean;
var RPNLength: Word);
var var
i: Integer; i: Integer;
n: Word; n: Word;
dr, dc: Integer;
TokenArraySizePos: Int64; TokenArraySizePos: Int64;
FinalPos: Int64; FinalPos: Int64;
exprDef: TsExprIdentifierDef; exprDef: TsExprIdentifierDef;
@ -2491,12 +2493,10 @@ var
begin begin
RPNLength := 0; RPNLength := 0;
if WriteTokenArraySize then begin { The size of the token array is written later, because it's necessary to
{ 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 }
calculate it first, and this is done at the same time it is written } TokenArraySizePos := AStream.Position;
TokenArraySizePos := AStream.Position; WriteRPNTokenArraySize(AStream, 0);
WriteRPNTokenArraySize(AStream, 0);
end;
{ Formula data (RPN token array) } { Formula data (RPN token array) }
for i := 0 to Length(AFormula) - 1 do begin for i := 0 to Length(AFormula) - 1 do begin
@ -2513,6 +2513,18 @@ begin
primaryExcelCode := TokenIDs[AFormula[i].ElementKind]; primaryExcelCode := TokenIDs[AFormula[i].ElementKind];
secondaryExcelCode := 0; secondaryExcelCode := 0;
end; 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); AStream.WriteByte(primaryExcelCode);
inc(RPNLength); inc(RPNLength);
@ -2520,7 +2532,6 @@ begin
case primaryExcelCode of case primaryExcelCode of
{ Operand Tokens } { Operand Tokens }
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA: { fekCell } 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 begin
n := WriteRPNCellAddress( n := WriteRPNCellAddress(
AStream, AStream,
@ -2545,11 +2556,13 @@ begin
INT_EXCEL_TOKEN_TREFN_V, INT_EXCEL_TOKEN_TREFN_V,
INT_EXCEL_TOKEN_TREFN_A: { fekCellOffset } INT_EXCEL_TOKEN_TREFN_A: { fekCellOffset }
begin begin
n := WriteRPNCellOffset( if rfRelRow in AFormula[i].RelFlags
AStream, then dr := integer(AFormula[i].Row) - ACell^.Row
integer(AFormula[i].Row), integer(AFormula[i].Col), else dr := integer(AFormula[i].Row) - ACell^.SharedFormulaBase^.Row;
AFormula[i].RelFlags 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); inc(RPNLength, n);
end; end;
@ -2608,17 +2621,15 @@ begin
end; // for end; // for
// Now update the size of the token array. // Now update the size of the token array.
if WriteTokenArraySize then begin finalPos := AStream.Position;
finalPos := AStream.Position; AStream.Position := TokenArraySizePos;
AStream.Position := TokenArraySizePos; WriteRPNTokenArraySize(AStream, RPNLength);
WriteRPNTokenArraySize(AStream, RPNLength); AStream.Position := finalPos;
AStream.Position := finalPos;
end;
end; end;
{ Writes the size of the RPN token array. Called from WriteRPNFormula. { Writes the size of the RPN token array. Called from WriteRPNFormula.
Valid for BIFF3-BIFF8. Override in BIFF2. } Valid for BIFF3-BIFF8. Override in BIFF2. }
procedure TsSPREADBIFFWriter.WriteRPNTokenArraySize(AStream: TStream; ASize: Word); procedure TsSpreadBIFFWriter.WriteRPNTokenArraySize(AStream: TStream; ASize: Word);
begin begin
AStream.WriteWord(WordToLE(ASize)); AStream.WriteWord(WordToLE(ASize));
end; end;
@ -2850,11 +2861,11 @@ begin
// Create an RPN formula from the shared formula base's string formula // Create an RPN formula from the shared formula base's string formula
// and adjust relative references // and adjust relative references
formula := FWorksheet.BuildRPNFormula(ACell^.SharedFormulaBase); 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]); FixRelativeReferences(ACell, formula[i]);
}
// Writes the rpn token array // 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 } { Write record size at the end after we known it }
finalPos := AStream.Position; finalPos := AStream.Position;

View File

@ -9,60 +9,63 @@ const
{ Formula constants TokenID values } { Formula constants TokenID values }
{ Binary Operator Tokens 3.6} { Binary Operator Tokens 3.6}
INT_EXCEL_TOKEN_TADD = $03; INT_EXCEL_TOKEN_TADD = $03;
INT_EXCEL_TOKEN_TSUB = $04; INT_EXCEL_TOKEN_TSUB = $04;
INT_EXCEL_TOKEN_TMUL = $05; INT_EXCEL_TOKEN_TMUL = $05;
INT_EXCEL_TOKEN_TDIV = $06; INT_EXCEL_TOKEN_TDIV = $06;
INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation ^ INT_EXCEL_TOKEN_TPOWER = $07; // Power Exponentiation ^
INT_EXCEL_TOKEN_TCONCAT = $08; // Concatenation & INT_EXCEL_TOKEN_TCONCAT = $08; // Concatenation &
INT_EXCEL_TOKEN_TLT = $09; // Less than < INT_EXCEL_TOKEN_TLT = $09; // Less than <
INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal <= INT_EXCEL_TOKEN_TLE = $0A; // Less than or equal <=
INT_EXCEL_TOKEN_TEQ = $0B; // Equal = INT_EXCEL_TOKEN_TEQ = $0B; // Equal =
INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal >= INT_EXCEL_TOKEN_TGE = $0C; // Greater than or equal >=
INT_EXCEL_TOKEN_TGT = $0D; // Greater than > INT_EXCEL_TOKEN_TGT = $0D; // Greater than >
INT_EXCEL_TOKEN_TNE = $0E; // Not equal <> INT_EXCEL_TOKEN_TNE = $0E; // Not equal <>
INT_EXCEL_TOKEN_TISECT = $0F; // Cell range intersection INT_EXCEL_TOKEN_TISECT = $0F; // Cell range intersection
INT_EXCEL_TOKEN_TLIST = $10; // Cell range list INT_EXCEL_TOKEN_TLIST = $10; // Cell range list
INT_EXCEL_TOKEN_TRANGE = $11; // Cell range INT_EXCEL_TOKEN_TRANGE = $11; // Cell range
INT_EXCEL_TOKEN_TUPLUS = $12; // Unary plus + INT_EXCEL_TOKEN_TUPLUS = $12; // Unary plus +
INT_EXCEL_TOKEN_TUMINUS = $13; // Unary minus + INT_EXCEL_TOKEN_TUMINUS = $13; // Unary minus +
INT_EXCEL_TOKEN_TPERCENT= $14; // Percent (%, divides operand by 100) INT_EXCEL_TOKEN_TPERCENT = $14; // Percent (%, divides operand by 100)
INT_EXCEL_TOKEN_TPAREN = $15; // Operator in parenthesis INT_EXCEL_TOKEN_TPAREN = $15; // Operator in parenthesis
{ Constant Operand Tokens, 3.8} { Constant Operand Tokens, 3.8}
INT_EXCEL_TOKEN_TMISSARG= $16; //missing operand INT_EXCEL_TOKEN_TMISSARG = $16; //missing operand
INT_EXCEL_TOKEN_TSTR = $17; //string INT_EXCEL_TOKEN_TSTR = $17; //string
INT_EXCEL_TOKEN_TERR = $1C; //error value INT_EXCEL_TOKEN_TERR = $1C; //error value
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
INT_EXCEL_TOKEN_TINT = $1E; //(unsigned) integer INT_EXCEL_TOKEN_TINT = $1E; //(unsigned) integer
INT_EXCEL_TOKEN_TNUM = $1F; //floating-point INT_EXCEL_TOKEN_TNUM = $1F; //floating-point
{ Operand Tokens } { Operand Tokens }
// _R: reference; _V: value; _A: array // _R: reference; _V: value; _A: array
INT_EXCEL_TOKEN_TREFR = $24; INT_EXCEL_TOKEN_TREFR = $24;
INT_EXCEL_TOKEN_TREFV = $44; INT_EXCEL_TOKEN_TREFV = $44;
INT_EXCEL_TOKEN_TREFA = $64; INT_EXCEL_TOKEN_TREFA = $64;
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_R = $2C;
INT_EXCEL_TOKEN_TREFN_V = $4C; INT_EXCEL_TOKEN_TREFN_V = $4C;
INT_EXCEL_TOKEN_TREFN_A = $6C; 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 } { Function Tokens }
// _R: reference; _V: value; _A: array // _R: reference; _V: value; _A: array
// Offset 0: token; offset 1: index to a built-in sheet function ( ➜ 3.111) // Offset 0: token; offset 1: index to a built-in sheet function ( ➜ 3.111)
INT_EXCEL_TOKEN_FUNC_R = $21; INT_EXCEL_TOKEN_FUNC_R = $21;
INT_EXCEL_TOKEN_FUNC_V = $41; INT_EXCEL_TOKEN_FUNC_V = $41;
INT_EXCEL_TOKEN_FUNC_A = $61; INT_EXCEL_TOKEN_FUNC_A = $61;
//VAR: variable number of arguments: //VAR: variable number of arguments:
INT_EXCEL_TOKEN_FUNCVAR_R = $22; INT_EXCEL_TOKEN_FUNCVAR_R = $22;
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 } { 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 } { Built-in/worksheet functions }
INT_EXCEL_SHEET_FUNC_COUNT = 0; INT_EXCEL_SHEET_FUNC_COUNT = 0;