You've already forked lazarus-ccr
fpspreadsheet: Unified writing code for rpn formulas. Calculation results of rpn formulas are now stored correctly for all biff versions.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3265 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -908,7 +908,8 @@ type
|
||||
{@@ abstract method for a formula to a cell. Must be overridden by descendent classes. }
|
||||
procedure WriteFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsFormula; ACell: PCell); virtual;
|
||||
{@@ abstract method for am RPN formula to a cell. Must be overridden by descendent classes. }
|
||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); virtual;
|
||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AFormula: TsRPNFormula; ACell: PCell); virtual;
|
||||
{@@ abstract method for a string to a cell. Must be overridden by descendent classes. }
|
||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal; const AValue: string; ACell: PCell); virtual; abstract;
|
||||
{@@ abstract method for a number value to a cell. Must be overridden by descendent classes. }
|
||||
@ -5290,8 +5291,8 @@ end;
|
||||
@param ACell Pointer to the cell containing the formula and being written
|
||||
to the stream
|
||||
}
|
||||
procedure TsCustomSpreadWriter.WriteRPNFormula(AStream: TStream; const ARow,
|
||||
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||
procedure TsCustomSpreadWriter.WriteRPNFormula(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||
begin
|
||||
Unused(AStream, ARow, ACol);
|
||||
Unused(AFormula, ACell);
|
||||
|
@ -46,6 +46,10 @@ type
|
||||
procedure TestWriteRead_BIFF8_FormulaStrings;
|
||||
|
||||
// Writes out and calculates formulas, read back
|
||||
{ BIFF2 Tests }
|
||||
procedure TestWriteRead_BIFF2_CalcRPNFormula;
|
||||
{ BIFF5 Tests }
|
||||
procedure TestWriteRead_BIFF5_CalcRPNFormula;
|
||||
{ BIFF8 Tests }
|
||||
procedure TestWriteRead_BIFF8_CalcRPNFormula;
|
||||
end;
|
||||
@ -223,6 +227,15 @@ begin
|
||||
DeleteFile(TempFile);
|
||||
end;
|
||||
|
||||
procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF2_CalcRPNFormula;
|
||||
begin
|
||||
TestCalcRPNFormulas(sfExcel2);
|
||||
end;
|
||||
|
||||
procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF5_CalcRPNFormula;
|
||||
begin
|
||||
TestCalcRPNFormulas(sfExcel5);
|
||||
end;
|
||||
|
||||
procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF8_CalcRPNFormula;
|
||||
begin
|
||||
|
@ -47,9 +47,6 @@
|
||||
<UseExternalDbgSyms Value="True"/>
|
||||
</Debugging>
|
||||
</Linking>
|
||||
<Other>
|
||||
<CompilerPath Value="$(CompPath)"/>
|
||||
</Other>
|
||||
</CompilerOptions>
|
||||
</Item2>
|
||||
</BuildModes>
|
||||
@ -157,9 +154,6 @@
|
||||
<OptimizationLevel Value="0"/>
|
||||
</Optimizations>
|
||||
</CodeGeneration>
|
||||
<Other>
|
||||
<CompilerPath Value="$(CompPath)"/>
|
||||
</Other>
|
||||
</CompilerOptions>
|
||||
<Debugging>
|
||||
<Exceptions Count="6">
|
||||
|
@ -1,6 +1,9 @@
|
||||
{ include file for "formulatests.pas", containing the test cases for the
|
||||
calcrpnformula test. }
|
||||
|
||||
{------------------------------------------------------------------------------}
|
||||
{ Basic operations }
|
||||
{------------------------------------------------------------------------------}
|
||||
// Addition
|
||||
Row := 0;
|
||||
MyWorksheet.WriteUTF8Text(Row, 0, '=1+1');
|
||||
@ -327,7 +330,7 @@
|
||||
{ Math }
|
||||
{------------------------------------------------------------------------------}
|
||||
|
||||
// ABS
|
||||
// ABS
|
||||
inc(Row);
|
||||
MyWorksheet.WriteUTF8Text(Row, 0, '=abs(-1)');
|
||||
MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula(
|
||||
|
@ -112,7 +112,11 @@ type
|
||||
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override;
|
||||
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
||||
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); override;
|
||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell); override;
|
||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AFormula: TsRPNFormula; ACell: PCell); override;
|
||||
function WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word; override;
|
||||
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); override;
|
||||
procedure WriteStringRecord(AStream: TStream; AString: String); override;
|
||||
procedure WriteWindow1(AStream: TStream); override;
|
||||
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
||||
public
|
||||
@ -1283,8 +1287,8 @@ end;
|
||||
MyFormula[1].Row := 0;
|
||||
MyFormula[2].TokenID := INT_EXCEL_TOKEN_TADD; +
|
||||
}
|
||||
procedure TsSpreadBIFF2Writer.WriteRPNFormula(AStream: TStream; const ARow,
|
||||
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||
procedure TsSpreadBIFF2Writer.WriteRPNFormula(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||
var
|
||||
FormulaResult: double;
|
||||
i: Integer;
|
||||
@ -1315,14 +1319,18 @@ begin
|
||||
{ BIFF2 Attributes }
|
||||
WriteCellFormatting(AStream, ACell, xf);
|
||||
|
||||
{ Result of the formula in IEEE 754 floating-point value }
|
||||
AStream.WriteBuffer(FormulaResult, 8);
|
||||
{ Encoded result of RPN formula }
|
||||
WriteRPNResult(AStream, ACell);
|
||||
|
||||
{ 0 = Do not recalculate
|
||||
1 = Always recalculate }
|
||||
AStream.WriteByte($1);
|
||||
AStream.WriteByte(1);
|
||||
|
||||
{ Formula }
|
||||
{ Formula data (RPN token array) }
|
||||
WriteRPNTokenArray(AStream, AFormula, RPNLength);
|
||||
|
||||
(*
|
||||
{ Formula }
|
||||
|
||||
{ The size of the token array is written later,
|
||||
because it's necessary to calculate if first,
|
||||
@ -1420,8 +1428,58 @@ begin
|
||||
AStream.Position := RecordSizePos;
|
||||
AStream.WriteWord(WordToLE(17 + RPNLength));
|
||||
AStream.position := FinalPos;
|
||||
*)
|
||||
|
||||
{ Write sizes in the end, after we known them }
|
||||
FinalPos := AStream.Position;
|
||||
AStream.Position := RecordSizePos;
|
||||
AStream.WriteWord(WordToLE(17 + RPNLength));
|
||||
AStream.Position := FinalPos;
|
||||
|
||||
{ Write following STRING record if formula result is a non-empty string }
|
||||
if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then
|
||||
WriteStringRecord(AStream, ACell^.UTF8StringValue);
|
||||
|
||||
end;
|
||||
|
||||
{ Writes the identifier for an RPN function with fixed argument count and
|
||||
returns the number of bytes written. }
|
||||
function TsSpreadBIFF2Writer.WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word;
|
||||
begin
|
||||
AStream.WriteByte(Lo(AIdentifier));
|
||||
Result := 1;
|
||||
end;
|
||||
|
||||
{ Writes the size of the RPN token array. Called from WriteRPNFormula.
|
||||
Overrides xlscommon. }
|
||||
procedure TsSpreadBIFF2Writer.WriteRPNTokenArraySize(AStream: TStream;
|
||||
ASize: Word);
|
||||
begin
|
||||
AStream.WriteByte(Lo(ASize));
|
||||
end;
|
||||
|
||||
{ Writes an Excel 2 STRING record which immediately follows a FORMULA record
|
||||
when the formula result is a string. }
|
||||
procedure TsSpreadBIFF2Writer.WriteStringRecord(AStream: TStream;
|
||||
AString: String);
|
||||
var
|
||||
s: ansistring;
|
||||
len: Integer;
|
||||
begin
|
||||
s := AString;
|
||||
len := Length(s);
|
||||
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_STRING));
|
||||
AStream.WriteWord(WordToLE(1 + len*SizeOf(Char)));
|
||||
|
||||
{ Write string length }
|
||||
AStream.WriteByte(len);
|
||||
{ Write characters }
|
||||
AStream.WriteBuffer(s[1], len * SizeOf(Char));
|
||||
end;
|
||||
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF2Writer.WriteBlank ()
|
||||
*
|
||||
|
@ -120,8 +120,11 @@ type
|
||||
procedure WriteIndex(AStream: TStream);
|
||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AValue: string; ACell: PCell); override;
|
||||
{
|
||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AFormula: TsRPNFormula; ACell: PCell); override;
|
||||
}
|
||||
procedure WriteStringRecord(AStream: TStream; AString: String); override;
|
||||
procedure WriteStyle(AStream: TStream);
|
||||
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
||||
procedure WriteXF(AStream: TStream; AFontIndex: Word;
|
||||
@ -677,7 +680,7 @@ begin
|
||||
AStream.WriteByte(len); // AnsiString, char count in 1 byte
|
||||
AStream.WriteBuffer(s[1], len * SizeOf(AnsiChar)); // String data
|
||||
end;
|
||||
|
||||
(*
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF5Writer.WriteRPNFormula ()
|
||||
*
|
||||
@ -688,8 +691,8 @@ end;
|
||||
* AFormula array
|
||||
*
|
||||
*******************************************************************}
|
||||
procedure TsSpreadBIFF5Writer.WriteRPNFormula(AStream: TStream; const ARow,
|
||||
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||
procedure TsSpreadBIFF5Writer.WriteRPNFormula(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||
var
|
||||
FormulaResult: double;
|
||||
i: Integer;
|
||||
@ -835,6 +838,7 @@ begin
|
||||
AStream.WriteWord(WordToLE(22 + RPNLength));
|
||||
AStream.position := FinalPos;
|
||||
end;
|
||||
*)
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF5Writer.WriteIndex ()
|
||||
@ -943,6 +947,28 @@ begin
|
||||
}
|
||||
end;
|
||||
|
||||
{ Writes an Excel 5 STRING record which immediately follows a FORMULA record
|
||||
when the formula result is a string.
|
||||
BIFF5 writes a byte-string, but uses a 16-bit length here! }
|
||||
procedure TsSpreadBIFF5Writer.WriteStringRecord(AStream: TStream;
|
||||
AString: String);
|
||||
var
|
||||
s: ansistring;
|
||||
len: Integer;
|
||||
begin
|
||||
s := AString;
|
||||
len := Length(s);
|
||||
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_STRING));
|
||||
AStream.WriteWord(WordToLE(2 + len*SizeOf(Char)));
|
||||
|
||||
{ Write string length }
|
||||
AStream.WriteWord(WordToLE(len));
|
||||
{ Write characters }
|
||||
AStream.WriteBuffer(s[1], len * SizeOf(Char));
|
||||
end;
|
||||
|
||||
{*******************************************************************
|
||||
* TsSpreadBIFF5Writer.WriteStyle ()
|
||||
*
|
||||
|
@ -123,10 +123,16 @@ type
|
||||
procedure WriteIndex(AStream: TStream);
|
||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AValue: string; ACell: PCell); override;
|
||||
function WriteRPNCellAddress(AStream: TStream; ARow, ACol: Cardinal;
|
||||
AFlags: TsRelFlags): word; override;
|
||||
function WriteRPNCellRangeAddress(AStream: TStream; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags): Word; override;
|
||||
{
|
||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AFormula: TsRPNFormula; ACell: PCell); override;
|
||||
}
|
||||
function WriteString_8bitLen(AStream: TStream; AString: String): Integer; override;
|
||||
procedure WriteStringRecord(AStream: TStream; AString: string);
|
||||
procedure WriteStringRecord(AStream: TStream; AString: string); override;
|
||||
procedure WriteStyle(AStream: TStream);
|
||||
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
||||
procedure WriteXF(AStream: TStream; AFontIndex: Word;
|
||||
@ -225,9 +231,9 @@ const
|
||||
INT_EXCEL_ID_FORCEFULLCALCULATION = $08A3;
|
||||
|
||||
{ Cell Addresses constants }
|
||||
MASK_EXCEL_COL_BITS_BIFF8=$00FF;
|
||||
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
|
||||
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
|
||||
MASK_EXCEL_COL_BITS_BIFF8 = $00FF;
|
||||
MASK_EXCEL_RELATIVE_COL_BIFF8 = $4000; // This is according to Microsoft documentation,
|
||||
MASK_EXCEL_RELATIVE_ROW_BIFF8 = $8000; // but opposite to OpenOffice documentation!
|
||||
|
||||
{ BOF record constants }
|
||||
INT_BOF_BIFF8_VER = $0600;
|
||||
@ -240,11 +246,6 @@ const
|
||||
INT_BOF_BUILD_ID = $1FD2;
|
||||
INT_BOF_BUILD_YEAR = $07CD;
|
||||
|
||||
{ FORMULA record constants }
|
||||
MASK_FORMULA_RECALCULATE_ALWAYS = $0001;
|
||||
MASK_FORMULA_RECALCULATE_ON_OPEN = $0002;
|
||||
MASK_FORMULA_SHARED_FORMULA = $0008;
|
||||
|
||||
{ STYLE record constants }
|
||||
MASK_STYLE_BUILT_IN = $8000;
|
||||
|
||||
@ -796,6 +797,54 @@ begin
|
||||
AStream.position := FinalPos;*)
|
||||
end;
|
||||
|
||||
{ Writes the address of a cell as used in an RPN formula and returns the
|
||||
number of bytes written.
|
||||
Valid for BIFF8. }
|
||||
function TsSpreadBIFF8Writer.WriteRPNCellAddress(AStream: TStream;
|
||||
ARow, ACol: Cardinal; AFlags: TsRelFlags): Word;
|
||||
var
|
||||
c: Cardinal; // column index with encoded relative/absolute address info
|
||||
begin
|
||||
AStream.WriteWord(WordToLE(ARow));
|
||||
c := ACol and MASK_EXCEL_COL_BITS_BIFF8;
|
||||
if (rfRelRow in AFlags) then c := c or MASK_EXCEL_RELATIVE_ROW_BIFF8;
|
||||
if (rfRelCol in AFlags) then c := c or MASK_EXCEL_RELATIVE_COL_BIFF8;
|
||||
AStream.WriteWord(WordToLE(c));
|
||||
Result := 4;
|
||||
end;
|
||||
|
||||
{ Writes the address of a cell range as used in an RPN formula and returns the
|
||||
count of bytes written.
|
||||
Valid for BIFF2-BIFF5. }
|
||||
function TsSpreadBIFF8Writer.WriteRPNCellRangeAddress(AStream: TStream;
|
||||
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags): Word;
|
||||
{ Cell range address, BIFF8:
|
||||
Offset Size Contents
|
||||
0 2 Index to first row (0…65535) or offset of first row (method [B], -32768…32767)
|
||||
2 2 Index to last row (0…65535) or offset of last row (method [B], -32768…32767)
|
||||
4 2 Index to first column or offset of first column, with relative flags (see table above)
|
||||
6 2 Index to last column or offset of last column, with relative flags (see table above)
|
||||
}
|
||||
var
|
||||
c: Cardinal; // column index with encoded relative/absolute address info
|
||||
begin
|
||||
AStream.WriteWord(WordToLE(ARow1));
|
||||
AStream.WriteWord(WordToLE(ARow2));
|
||||
|
||||
c := ACol1;
|
||||
if (rfRelCol in AFlags) then c := c or MASK_EXCEL_RELATIVE_COL;
|
||||
if (rfRelRow in AFlags) then c := c or MASK_EXCEL_RELATIVE_ROW;
|
||||
AStream.WriteWord(WordToLE(c));
|
||||
|
||||
c := ACol2;
|
||||
if (rfRelCol2 in AFlags) then c := c or MASK_EXCEL_RELATIVE_COL;
|
||||
if (rfRelRow2 in AFlags) then c := c or MASK_EXCEL_RELATIVE_ROW;
|
||||
AStream.WriteWord(WordToLE(c));
|
||||
|
||||
Result := 8;
|
||||
end;
|
||||
|
||||
(*
|
||||
procedure TsSpreadBIFF8Writer.WriteRPNFormula(AStream: TStream; const ARow,
|
||||
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||
var
|
||||
@ -869,11 +918,12 @@ begin
|
||||
|
||||
{ Formula }
|
||||
|
||||
{ The size of the token array is written later,
|
||||
because it's necessary to calculate if first,
|
||||
and this is done at the same time it is written }
|
||||
{ The size of the token array is written later, because it's necessary to
|
||||
calculate if first, and this is done at the same time it is written }
|
||||
TokenArraySizePos := AStream.Position;
|
||||
AStream.WriteWord(RPNLength);
|
||||
WriteRPNTokenArraySize(AStream, RPNLength);
|
||||
|
||||
WriteRPNTokenArray(AStream, AFormula, RPNLength);
|
||||
|
||||
{ Formula data (RPN token array) }
|
||||
for i := 0 to Length(AFormula) - 1 do
|
||||
@ -988,12 +1038,13 @@ begin
|
||||
AStream.WriteByte(RPNLength);
|
||||
AStream.Position := RecordSizePos;
|
||||
AStream.WriteWord(WordToLE(22 + RPNLength));
|
||||
AStream.position := FinalPos;
|
||||
AStream.Position := FinalPos;
|
||||
|
||||
{ Write following STRING record if formula result is a non-empty string }
|
||||
if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then
|
||||
WriteStringRecord(AStream, ACell^.UTF8StringValue);
|
||||
end;
|
||||
*)
|
||||
|
||||
{ Helper function for writing a string with 8-bit length. Overridden version
|
||||
for BIFF8. Called for writing rpn formula string tokens.
|
||||
|
@ -320,6 +320,11 @@ const
|
||||
{ Note: The assignment of the RELATIVE_COL and _ROW masks is according to
|
||||
Microsoft's documentation, but opposite to the OpenOffice documentation. }
|
||||
|
||||
{ FORMULA record constants }
|
||||
MASK_FORMULA_RECALCULATE_ALWAYS = $0001;
|
||||
MASK_FORMULA_RECALCULATE_ON_OPEN = $0002;
|
||||
MASK_FORMULA_SHARED_FORMULA = $0008;
|
||||
|
||||
{ Error codes }
|
||||
ERR_INTERSECTION_EMPTY = $00; // #NULL!
|
||||
ERR_DIVIDE_BY_ZERO = $07; // #DIV/0!
|
||||
@ -478,6 +483,17 @@ type
|
||||
// Writes out a PANE record
|
||||
procedure WritePane(AStream: TStream; ASheet: TsWorksheet; IsBiff58: Boolean;
|
||||
out ActivePane: Byte);
|
||||
function WriteRPNCellAddress(AStream: TStream; ARow, ACol: Cardinal;
|
||||
AFlags: TsRelFlags): Word; virtual;
|
||||
function WriteRPNCellRangeAddress(AStream: TStream; ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||
AFlags: TsRelFlags): Word; virtual;
|
||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||
const AFormula: TsRPNFormula; ACell: PCell); override;
|
||||
function WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word; virtual;
|
||||
procedure WriteRPNResult(AStream: TStream; ACell: PCell);
|
||||
procedure WriteRPNTokenArray(AStream: TStream; const AFormula: TsRPNFormula;
|
||||
var RPNLength: Word);
|
||||
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual;
|
||||
// Writes out a ROW record
|
||||
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
||||
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); virtual;
|
||||
@ -487,6 +503,7 @@ type
|
||||
procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte);
|
||||
procedure WriteSelections(AStream: TStream; ASheet: TsWorksheet);
|
||||
procedure WriteSheetPR(AStream: TStream);
|
||||
procedure WriteStringRecord(AStream: TStream; AString: String); virtual;
|
||||
// Writes out a WINDOW1 record
|
||||
procedure WriteWindow1(AStream: TStream); virtual;
|
||||
// Writes the index of the XF record used in the given cell
|
||||
@ -1537,8 +1554,8 @@ begin
|
||||
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 BIFF8
|
||||
where it has to overridden. }
|
||||
version for ansistrings since it is valid for all BIFF versions except for
|
||||
BIFF8 where it has to be overridden. }
|
||||
function TsSpreadBIFFReader.ReadString_8bitLen(AStream: TStream): String;
|
||||
var
|
||||
len: Byte;
|
||||
@ -1968,6 +1985,272 @@ begin
|
||||
{ Not used (BIFF5-BIFF8 only, not written in BIFF2-BIFF4 }
|
||||
end;
|
||||
|
||||
{ Writes the address of a cell as used in an RPN formula and returns the
|
||||
count of bytes written.
|
||||
Valid for BIFF2-BIFF5. }
|
||||
function TsSpreadBIFFWriter.WriteRPNCellAddress(AStream: TStream;
|
||||
ARow, ACol: Cardinal; AFlags: TsRelFlags): Word;
|
||||
var
|
||||
r: Cardinal; // row index containing the relativ/absolute address info
|
||||
begin
|
||||
r := ARow and MASK_EXCEL_ROW;
|
||||
if (rfRelRow in AFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||
if (rfRelCol in AFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||
AStream.WriteWord(r);
|
||||
AStream.WriteByte(ACol);
|
||||
Result := 3;
|
||||
end;
|
||||
|
||||
|
||||
{ Writes the address of a cell range as used in an RPN formula and returns the
|
||||
count of bytes written.
|
||||
Valid for BIFF2-BIFF5. }
|
||||
function TsSpreadBIFFWriter.WriteRPNCellRangeAddress(AStream: TStream;
|
||||
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags): Word;
|
||||
var
|
||||
r: Cardinal;
|
||||
begin
|
||||
r := ARow1 and MASK_EXCEL_ROW;
|
||||
if (rfRelRow in AFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||
if (rfRelCol in AFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||
AStream.WriteWord(WordToLE(r));
|
||||
|
||||
r := ARow2 and MASK_EXCEL_ROW;
|
||||
if (rfRelRow2 in AFlags) then r := r or MASK_EXCEL_RELATIVE_ROW;
|
||||
if (rfRelCol2 in AFlags) then r := r or MASK_EXCEL_RELATIVE_COL;
|
||||
AStream.WriteWord(WordToLE(r));
|
||||
|
||||
AStream.WriteByte(ACol1);
|
||||
AStream.WriteByte(ACol2);
|
||||
|
||||
Result := 6;
|
||||
end;
|
||||
|
||||
{ Writes an Excel FORMULA record
|
||||
The formula needs to be converted from usual user-readable string
|
||||
to an RPN array
|
||||
Valid for BIFF5-BIFF8.
|
||||
}
|
||||
procedure TsSpreadBIFFWriter.WriteRPNFormula(AStream: TStream;
|
||||
const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||
var
|
||||
i: Integer;
|
||||
len: Integer;
|
||||
RPNLength: Word;
|
||||
RecordSizePos, FinalPos: Int64;
|
||||
// TokenID: Word;
|
||||
// lSecondaryID: Word;
|
||||
// c: Cardinal;
|
||||
wideStr: WideString;
|
||||
begin
|
||||
{ BIFF Record header }
|
||||
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMULA));
|
||||
RecordSizePos := AStream.Position;
|
||||
AStream.WriteWord(0); // This is the record size which is not yet known here
|
||||
|
||||
{ BIFF Record data }
|
||||
AStream.WriteWord(WordToLE(ARow));
|
||||
AStream.WriteWord(WordToLE(ACol));
|
||||
|
||||
{ Index to XF record, according to formatting }
|
||||
//AStream.WriteWord(0);
|
||||
WriteXFIndex(AStream, ACell);
|
||||
|
||||
{ Encoded result of RPN formula }
|
||||
WriteRPNResult(AStream, ACell);
|
||||
|
||||
{ Options flags }
|
||||
AStream.WriteWord(WordToLE(MASK_FORMULA_RECALCULATE_ALWAYS));
|
||||
|
||||
{ Not used }
|
||||
AStream.WriteDWord(0);
|
||||
|
||||
{ Formula data (RPN token array) }
|
||||
WriteRPNTokenArray(AStream, AFormula, RPNLength);
|
||||
|
||||
{ Write sizes in the end, after we known them }
|
||||
FinalPos := AStream.Position;
|
||||
AStream.Position := RecordSizePos;
|
||||
AStream.WriteWord(WordToLE(22 + RPNLength));
|
||||
AStream.Position := FinalPos;
|
||||
|
||||
{ Write following STRING record if formula result is a non-empty string }
|
||||
if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then
|
||||
WriteStringRecord(AStream, ACell^.UTF8StringValue);
|
||||
end;
|
||||
|
||||
{ Writes the identifier for an RPN function with fixed argument count and
|
||||
returns the number of bytes written.
|
||||
Valid for BIFF4-BIFF8. Override in BIFF2-BIFF3 }
|
||||
function TsSpreadBIFFWriter.WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word;
|
||||
begin
|
||||
AStream.WriteWord(WordToLE(AIdentifier));
|
||||
Result := 2;
|
||||
end;
|
||||
|
||||
{ Writes the result of an RPN formula. }
|
||||
procedure TsSpreadBIFFWriter.WriteRPNResult(AStream: TStream; ACell: PCell);
|
||||
var
|
||||
FormulaResult: double;
|
||||
FormulaResultWords: array[0..3] of word absolute FormulaResult;
|
||||
begin
|
||||
{ Determine encoded result bytes }
|
||||
FormulaResult := 0.0;
|
||||
case ACell^.ContentType of
|
||||
cctNumber:
|
||||
FormulaResult := ACell^.NumberValue;
|
||||
cctDateTime:
|
||||
FormulaResult := ACell^.DateTimeValue;
|
||||
cctUTF8String:
|
||||
begin
|
||||
if ACell^.UTF8StringValue = '' then
|
||||
FormulaResultWords[0] := 3;
|
||||
FormulaResultWords[3] := $FFFF;
|
||||
end;
|
||||
cctBool:
|
||||
begin
|
||||
FormulaResultWords[0] := 1;
|
||||
FormulaResultWords[1] := ord(ACell^.BoolValue);
|
||||
FormulaResultWords[3] := $FFFF;
|
||||
end;
|
||||
cctError:
|
||||
begin
|
||||
FormulaResultWords[0] := 2;
|
||||
case ACell^.ErrorValue of
|
||||
errEmptyIntersection: FormulaResultWords[1] := ERR_INTERSECTION_EMPTY;// #NULL!
|
||||
errDivideByZero : FormulaResultWords[1] := ERR_DIVIDE_BY_ZERO; // #DIV/0!
|
||||
errWrongType : FormulaResultWords[1] := ERR_WRONG_TYPE_OF_OPERAND; // #VALUE!
|
||||
errIllegalRef : FormulaResultWords[1] := ERR_ILLEGAL_REFERENCE; // #REF!
|
||||
errWrongName : FormulaResultWords[1] := ERR_WRONG_NAME; // #NAME?
|
||||
errOverflow : FormulaResultWords[1] := ERR_OVERFLOW; // #NUM!
|
||||
errArgError : FormulaResultWords[1] := ERR_ARG_ERROR; // #N/A;
|
||||
end;
|
||||
FormulaResultWords[3] := $FFFF;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Write result of the formula, encoded above }
|
||||
AStream.WriteBuffer(FormulaResult, 8);
|
||||
end;
|
||||
|
||||
{ Writes the token array of the given RPN formula and returns its size }
|
||||
procedure TsSpreadBIFFWriter.WriteRPNTokenArray(AStream: TStream;
|
||||
const AFormula: TsRPNFormula; var RPNLength: Word);
|
||||
var
|
||||
i: Integer;
|
||||
tokenID, secondaryID: Word;
|
||||
n: Word;
|
||||
TokenArraySizePos: Int64;
|
||||
FinalPos: Int64;
|
||||
begin
|
||||
RPNLength := 0;
|
||||
|
||||
{ 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
|
||||
|
||||
{ Token identifier }
|
||||
tokenID := FormulaElementKindToExcelTokenID(AFormula[i].ElementKind, secondaryID);
|
||||
AStream.WriteByte(tokenID);
|
||||
inc(RPNLength);
|
||||
|
||||
{ Token data }
|
||||
case tokenID of
|
||||
{ Operand Tokens }
|
||||
INT_EXCEL_TOKEN_TREFR,
|
||||
INT_EXCEL_TOKEN_TREFV,
|
||||
INT_EXCEL_TOKEN_TREFA: { fekCell }
|
||||
begin
|
||||
n := WriteRPNCellAddress(
|
||||
AStream,
|
||||
AFormula[i].Row, AFormula[i].Col,
|
||||
AFormula[i].RelFlags
|
||||
);
|
||||
inc(RPNLength, n);
|
||||
end;
|
||||
|
||||
INT_EXCEL_TOKEN_TAREA_R: { fekCellRange }
|
||||
begin
|
||||
n := WriteRPNCellRangeAddress(
|
||||
AStream,
|
||||
AFormula[i].Row, AFormula[i].Col,
|
||||
AFormula[i].Row2, AFormula[i].Col2,
|
||||
AFormula[i].RelFlags
|
||||
);
|
||||
inc(RPNLength, n);
|
||||
end;
|
||||
|
||||
INT_EXCEL_TOKEN_TNUM: { fekNum }
|
||||
begin
|
||||
AStream.WriteBuffer(AFormula[i].DoubleValue, 8);
|
||||
inc(RPNLength, 8);
|
||||
end;
|
||||
|
||||
INT_EXCEL_TOKEN_TSTR: { fekString }
|
||||
{ string constant is stored as widestring in BIFF8, otherwise as ansistring
|
||||
Writing is done by the virtual method WriteString_8bitLen. }
|
||||
begin
|
||||
inc(RPNLength, WriteString_8bitLen(AStream, AFormula[i].StringValue));
|
||||
end;
|
||||
|
||||
INT_EXCEL_TOKEN_TBOOL: { fekBool }
|
||||
begin
|
||||
AStream.WriteByte(ord(AFormula[i].DoubleValue <> 0.0));
|
||||
inc(RPNLength, 1);
|
||||
end;
|
||||
|
||||
{ binary operation tokens }
|
||||
INT_EXCEL_TOKEN_TADD, INT_EXCEL_TOKEN_TSUB, INT_EXCEL_TOKEN_TMUL,
|
||||
INT_EXCEL_TOKEN_TDIV, INT_EXCEL_TOKEN_TPOWER:
|
||||
begin
|
||||
end;
|
||||
|
||||
{ Other operations }
|
||||
INT_EXCEL_TOKEN_TATTR: { fekOpSUM }
|
||||
{ 3.10, page 71: e.g. =SUM(1) is represented by token array tInt(1),tAttrRum }
|
||||
begin
|
||||
// Unary SUM Operation
|
||||
AStream.WriteByte($10); //tAttrSum token (SUM with one parameter)
|
||||
AStream.WriteByte(0); // not used
|
||||
AStream.WriteByte(0); // not used
|
||||
inc(RPNLength, 3);
|
||||
end;
|
||||
|
||||
// Functions with fixed parameter count
|
||||
INT_EXCEL_TOKEN_FUNC_R, INT_EXCEL_TOKEN_FUNC_V, INT_EXCEL_TOKEN_FUNC_A:
|
||||
begin
|
||||
n := WriteRPNFunc(AStream, secondaryID);
|
||||
inc(RPNLength, n);
|
||||
end;
|
||||
|
||||
// Functions with variable parameter count
|
||||
INT_EXCEL_TOKEN_FUNCVAR_V:
|
||||
begin
|
||||
AStream.WriteByte(AFormula[i].ParamsNum);
|
||||
n := WriteRPNFunc(AStream, secondaryID);
|
||||
inc(RPNLength, 1 + n);
|
||||
end;
|
||||
end; // case
|
||||
end; // for
|
||||
|
||||
// Now update the size of the token array.
|
||||
finalPos := AStream.Position;
|
||||
AStream.Position := TokenArraySizePos;
|
||||
AStream.WriteByte(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);
|
||||
begin
|
||||
AStream.WriteWord(WordToLE(ASize));
|
||||
end;
|
||||
|
||||
{ Writes an Excel 3-8 ROW record
|
||||
Valid for BIFF3-BIFF8 }
|
||||
procedure TsSpreadBIFFWriter.WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
||||
@ -2153,13 +2436,22 @@ var
|
||||
len: Byte;
|
||||
s: ansistring;
|
||||
begin
|
||||
s := AString;
|
||||
s := UTF8ToAnsi(AString);
|
||||
len := Length(s);
|
||||
AStream.WriteByte(len);
|
||||
AStream.WriteBuffer(s[1], len);
|
||||
Result := 1 + len;
|
||||
end;
|
||||
|
||||
{ Write STRING record following immediately after RPN formula if formula result
|
||||
is a non-empty string.
|
||||
Must be overridden because depends of BIFF version }
|
||||
procedure TsSpreadBIFFWriter.WriteStringRecord(AStream: TStream;
|
||||
AString: String);
|
||||
begin
|
||||
Unused(AStream, AString);
|
||||
end;
|
||||
|
||||
{ Writes an Excel 5/8 WINDOW1 record
|
||||
This record contains general settings for the document window and
|
||||
global workbook settings.
|
||||
|
Reference in New Issue
Block a user