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. }
|
{@@ 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;
|
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. }
|
{@@ 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. }
|
{@@ 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;
|
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. }
|
{@@ 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
|
@param ACell Pointer to the cell containing the formula and being written
|
||||||
to the stream
|
to the stream
|
||||||
}
|
}
|
||||||
procedure TsCustomSpreadWriter.WriteRPNFormula(AStream: TStream; const ARow,
|
procedure TsCustomSpreadWriter.WriteRPNFormula(AStream: TStream;
|
||||||
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||||
begin
|
begin
|
||||||
Unused(AStream, ARow, ACol);
|
Unused(AStream, ARow, ACol);
|
||||||
Unused(AFormula, ACell);
|
Unused(AFormula, ACell);
|
||||||
|
@ -46,6 +46,10 @@ type
|
|||||||
procedure TestWriteRead_BIFF8_FormulaStrings;
|
procedure TestWriteRead_BIFF8_FormulaStrings;
|
||||||
|
|
||||||
// Writes out and calculates formulas, read back
|
// Writes out and calculates formulas, read back
|
||||||
|
{ BIFF2 Tests }
|
||||||
|
procedure TestWriteRead_BIFF2_CalcRPNFormula;
|
||||||
|
{ BIFF5 Tests }
|
||||||
|
procedure TestWriteRead_BIFF5_CalcRPNFormula;
|
||||||
{ BIFF8 Tests }
|
{ BIFF8 Tests }
|
||||||
procedure TestWriteRead_BIFF8_CalcRPNFormula;
|
procedure TestWriteRead_BIFF8_CalcRPNFormula;
|
||||||
end;
|
end;
|
||||||
@ -223,6 +227,15 @@ begin
|
|||||||
DeleteFile(TempFile);
|
DeleteFile(TempFile);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF2_CalcRPNFormula;
|
||||||
|
begin
|
||||||
|
TestCalcRPNFormulas(sfExcel2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF5_CalcRPNFormula;
|
||||||
|
begin
|
||||||
|
TestCalcRPNFormulas(sfExcel5);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF8_CalcRPNFormula;
|
procedure TSpreadWriteReadFormulaTests.TestWriteRead_BIFF8_CalcRPNFormula;
|
||||||
begin
|
begin
|
||||||
|
@ -47,9 +47,6 @@
|
|||||||
<UseExternalDbgSyms Value="True"/>
|
<UseExternalDbgSyms Value="True"/>
|
||||||
</Debugging>
|
</Debugging>
|
||||||
</Linking>
|
</Linking>
|
||||||
<Other>
|
|
||||||
<CompilerPath Value="$(CompPath)"/>
|
|
||||||
</Other>
|
|
||||||
</CompilerOptions>
|
</CompilerOptions>
|
||||||
</Item2>
|
</Item2>
|
||||||
</BuildModes>
|
</BuildModes>
|
||||||
@ -157,9 +154,6 @@
|
|||||||
<OptimizationLevel Value="0"/>
|
<OptimizationLevel Value="0"/>
|
||||||
</Optimizations>
|
</Optimizations>
|
||||||
</CodeGeneration>
|
</CodeGeneration>
|
||||||
<Other>
|
|
||||||
<CompilerPath Value="$(CompPath)"/>
|
|
||||||
</Other>
|
|
||||||
</CompilerOptions>
|
</CompilerOptions>
|
||||||
<Debugging>
|
<Debugging>
|
||||||
<Exceptions Count="6">
|
<Exceptions Count="6">
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{ include file for "formulatests.pas", containing the test cases for the
|
{ include file for "formulatests.pas", containing the test cases for the
|
||||||
calcrpnformula test. }
|
calcrpnformula test. }
|
||||||
|
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
|
{ Basic operations }
|
||||||
|
{------------------------------------------------------------------------------}
|
||||||
// Addition
|
// Addition
|
||||||
Row := 0;
|
Row := 0;
|
||||||
MyWorksheet.WriteUTF8Text(Row, 0, '=1+1');
|
MyWorksheet.WriteUTF8Text(Row, 0, '=1+1');
|
||||||
|
@ -112,7 +112,11 @@ type
|
|||||||
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override;
|
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double; ACell: PCell); override;
|
||||||
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
||||||
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); override;
|
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 WriteWindow1(AStream: TStream); override;
|
||||||
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
||||||
public
|
public
|
||||||
@ -1283,8 +1287,8 @@ end;
|
|||||||
MyFormula[1].Row := 0;
|
MyFormula[1].Row := 0;
|
||||||
MyFormula[2].TokenID := INT_EXCEL_TOKEN_TADD; +
|
MyFormula[2].TokenID := INT_EXCEL_TOKEN_TADD; +
|
||||||
}
|
}
|
||||||
procedure TsSpreadBIFF2Writer.WriteRPNFormula(AStream: TStream; const ARow,
|
procedure TsSpreadBIFF2Writer.WriteRPNFormula(AStream: TStream;
|
||||||
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||||
var
|
var
|
||||||
FormulaResult: double;
|
FormulaResult: double;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
@ -1315,13 +1319,17 @@ begin
|
|||||||
{ BIFF2 Attributes }
|
{ BIFF2 Attributes }
|
||||||
WriteCellFormatting(AStream, ACell, xf);
|
WriteCellFormatting(AStream, ACell, xf);
|
||||||
|
|
||||||
{ Result of the formula in IEEE 754 floating-point value }
|
{ Encoded result of RPN formula }
|
||||||
AStream.WriteBuffer(FormulaResult, 8);
|
WriteRPNResult(AStream, ACell);
|
||||||
|
|
||||||
{ 0 = Do not recalculate
|
{ 0 = Do not recalculate
|
||||||
1 = Always recalculate }
|
1 = Always recalculate }
|
||||||
AStream.WriteByte($1);
|
AStream.WriteByte(1);
|
||||||
|
|
||||||
|
{ Formula data (RPN token array) }
|
||||||
|
WriteRPNTokenArray(AStream, AFormula, RPNLength);
|
||||||
|
|
||||||
|
(*
|
||||||
{ Formula }
|
{ Formula }
|
||||||
|
|
||||||
{ The size of the token array is written later,
|
{ The size of the token array is written later,
|
||||||
@ -1420,8 +1428,58 @@ begin
|
|||||||
AStream.Position := RecordSizePos;
|
AStream.Position := RecordSizePos;
|
||||||
AStream.WriteWord(WordToLE(17 + RPNLength));
|
AStream.WriteWord(WordToLE(17 + RPNLength));
|
||||||
AStream.position := FinalPos;
|
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;
|
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 ()
|
* TsSpreadBIFF2Writer.WriteBlank ()
|
||||||
*
|
*
|
||||||
|
@ -120,8 +120,11 @@ type
|
|||||||
procedure WriteIndex(AStream: TStream);
|
procedure WriteIndex(AStream: TStream);
|
||||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: string; ACell: PCell); override;
|
const AValue: string; ACell: PCell); override;
|
||||||
|
{
|
||||||
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AFormula: TsRPNFormula; ACell: PCell); override;
|
const AFormula: TsRPNFormula; ACell: PCell); override;
|
||||||
|
}
|
||||||
|
procedure WriteStringRecord(AStream: TStream; AString: String); override;
|
||||||
procedure WriteStyle(AStream: TStream);
|
procedure WriteStyle(AStream: TStream);
|
||||||
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
||||||
procedure WriteXF(AStream: TStream; AFontIndex: Word;
|
procedure WriteXF(AStream: TStream; AFontIndex: Word;
|
||||||
@ -677,7 +680,7 @@ begin
|
|||||||
AStream.WriteByte(len); // AnsiString, char count in 1 byte
|
AStream.WriteByte(len); // AnsiString, char count in 1 byte
|
||||||
AStream.WriteBuffer(s[1], len * SizeOf(AnsiChar)); // String data
|
AStream.WriteBuffer(s[1], len * SizeOf(AnsiChar)); // String data
|
||||||
end;
|
end;
|
||||||
|
(*
|
||||||
{*******************************************************************
|
{*******************************************************************
|
||||||
* TsSpreadBIFF5Writer.WriteRPNFormula ()
|
* TsSpreadBIFF5Writer.WriteRPNFormula ()
|
||||||
*
|
*
|
||||||
@ -688,8 +691,8 @@ end;
|
|||||||
* AFormula array
|
* AFormula array
|
||||||
*
|
*
|
||||||
*******************************************************************}
|
*******************************************************************}
|
||||||
procedure TsSpreadBIFF5Writer.WriteRPNFormula(AStream: TStream; const ARow,
|
procedure TsSpreadBIFF5Writer.WriteRPNFormula(AStream: TStream;
|
||||||
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
const ARow, ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||||
var
|
var
|
||||||
FormulaResult: double;
|
FormulaResult: double;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
@ -835,6 +838,7 @@ begin
|
|||||||
AStream.WriteWord(WordToLE(22 + RPNLength));
|
AStream.WriteWord(WordToLE(22 + RPNLength));
|
||||||
AStream.position := FinalPos;
|
AStream.position := FinalPos;
|
||||||
end;
|
end;
|
||||||
|
*)
|
||||||
|
|
||||||
{*******************************************************************
|
{*******************************************************************
|
||||||
* TsSpreadBIFF5Writer.WriteIndex ()
|
* TsSpreadBIFF5Writer.WriteIndex ()
|
||||||
@ -943,6 +947,28 @@ begin
|
|||||||
}
|
}
|
||||||
end;
|
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 ()
|
* TsSpreadBIFF5Writer.WriteStyle ()
|
||||||
*
|
*
|
||||||
|
@ -123,10 +123,16 @@ type
|
|||||||
procedure WriteIndex(AStream: TStream);
|
procedure WriteIndex(AStream: TStream);
|
||||||
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AValue: string; ACell: PCell); override;
|
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;
|
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
|
||||||
const AFormula: TsRPNFormula; ACell: PCell); override;
|
const AFormula: TsRPNFormula; ACell: PCell); override;
|
||||||
|
}
|
||||||
function WriteString_8bitLen(AStream: TStream; AString: String): Integer; 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 WriteStyle(AStream: TStream);
|
||||||
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
|
||||||
procedure WriteXF(AStream: TStream; AFontIndex: Word;
|
procedure WriteXF(AStream: TStream; AFontIndex: Word;
|
||||||
@ -226,8 +232,8 @@ const
|
|||||||
|
|
||||||
{ Cell Addresses constants }
|
{ Cell Addresses constants }
|
||||||
MASK_EXCEL_COL_BITS_BIFF8 = $00FF;
|
MASK_EXCEL_COL_BITS_BIFF8 = $00FF;
|
||||||
MASK_EXCEL_RELATIVE_COL = $4000; // This is according to Microsoft documentation,
|
MASK_EXCEL_RELATIVE_COL_BIFF8 = $4000; // This is according to Microsoft documentation,
|
||||||
MASK_EXCEL_RELATIVE_ROW = $8000; // but opposite to OpenOffice documentation!
|
MASK_EXCEL_RELATIVE_ROW_BIFF8 = $8000; // but opposite to OpenOffice documentation!
|
||||||
|
|
||||||
{ BOF record constants }
|
{ BOF record constants }
|
||||||
INT_BOF_BIFF8_VER = $0600;
|
INT_BOF_BIFF8_VER = $0600;
|
||||||
@ -240,11 +246,6 @@ const
|
|||||||
INT_BOF_BUILD_ID = $1FD2;
|
INT_BOF_BUILD_ID = $1FD2;
|
||||||
INT_BOF_BUILD_YEAR = $07CD;
|
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 }
|
{ STYLE record constants }
|
||||||
MASK_STYLE_BUILT_IN = $8000;
|
MASK_STYLE_BUILT_IN = $8000;
|
||||||
|
|
||||||
@ -796,6 +797,54 @@ begin
|
|||||||
AStream.position := FinalPos;*)
|
AStream.position := FinalPos;*)
|
||||||
end;
|
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,
|
procedure TsSpreadBIFF8Writer.WriteRPNFormula(AStream: TStream; const ARow,
|
||||||
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
ACol: Cardinal; const AFormula: TsRPNFormula; ACell: PCell);
|
||||||
var
|
var
|
||||||
@ -869,11 +918,12 @@ begin
|
|||||||
|
|
||||||
{ Formula }
|
{ Formula }
|
||||||
|
|
||||||
{ The size of the token array is written later,
|
{ The size of the token array is written later, because it's necessary to
|
||||||
because it's necessary to calculate if first,
|
calculate if first, and this is done at the same time it is written }
|
||||||
and this is done at the same time it is written }
|
|
||||||
TokenArraySizePos := AStream.Position;
|
TokenArraySizePos := AStream.Position;
|
||||||
AStream.WriteWord(RPNLength);
|
WriteRPNTokenArraySize(AStream, RPNLength);
|
||||||
|
|
||||||
|
WriteRPNTokenArray(AStream, AFormula, RPNLength);
|
||||||
|
|
||||||
{ Formula data (RPN token array) }
|
{ Formula data (RPN token array) }
|
||||||
for i := 0 to Length(AFormula) - 1 do
|
for i := 0 to Length(AFormula) - 1 do
|
||||||
@ -988,12 +1038,13 @@ begin
|
|||||||
AStream.WriteByte(RPNLength);
|
AStream.WriteByte(RPNLength);
|
||||||
AStream.Position := RecordSizePos;
|
AStream.Position := RecordSizePos;
|
||||||
AStream.WriteWord(WordToLE(22 + RPNLength));
|
AStream.WriteWord(WordToLE(22 + RPNLength));
|
||||||
AStream.position := FinalPos;
|
AStream.Position := FinalPos;
|
||||||
|
|
||||||
{ Write following STRING record if formula result is a non-empty string }
|
{ Write following STRING record if formula result is a non-empty string }
|
||||||
if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then
|
if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then
|
||||||
WriteStringRecord(AStream, ACell^.UTF8StringValue);
|
WriteStringRecord(AStream, ACell^.UTF8StringValue);
|
||||||
end;
|
end;
|
||||||
|
*)
|
||||||
|
|
||||||
{ Helper function for writing a string with 8-bit length. Overridden version
|
{ Helper function for writing a string with 8-bit length. Overridden version
|
||||||
for BIFF8. Called for writing rpn formula string tokens.
|
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
|
{ Note: The assignment of the RELATIVE_COL and _ROW masks is according to
|
||||||
Microsoft's documentation, but opposite to the OpenOffice documentation. }
|
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 }
|
{ Error codes }
|
||||||
ERR_INTERSECTION_EMPTY = $00; // #NULL!
|
ERR_INTERSECTION_EMPTY = $00; // #NULL!
|
||||||
ERR_DIVIDE_BY_ZERO = $07; // #DIV/0!
|
ERR_DIVIDE_BY_ZERO = $07; // #DIV/0!
|
||||||
@ -478,6 +483,17 @@ type
|
|||||||
// Writes out a PANE record
|
// Writes out a PANE record
|
||||||
procedure WritePane(AStream: TStream; ASheet: TsWorksheet; IsBiff58: Boolean;
|
procedure WritePane(AStream: TStream; ASheet: TsWorksheet; IsBiff58: Boolean;
|
||||||
out ActivePane: Byte);
|
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
|
// Writes out a ROW record
|
||||||
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
procedure WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
||||||
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); virtual;
|
ARowIndex, AFirstColIndex, ALastColIndex: Cardinal; ARow: PRow); virtual;
|
||||||
@ -487,6 +503,7 @@ type
|
|||||||
procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte);
|
procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte);
|
||||||
procedure WriteSelections(AStream: TStream; ASheet: TsWorksheet);
|
procedure WriteSelections(AStream: TStream; ASheet: TsWorksheet);
|
||||||
procedure WriteSheetPR(AStream: TStream);
|
procedure WriteSheetPR(AStream: TStream);
|
||||||
|
procedure WriteStringRecord(AStream: TStream; AString: String); virtual;
|
||||||
// Writes out a WINDOW1 record
|
// Writes out a WINDOW1 record
|
||||||
procedure WriteWindow1(AStream: TStream); virtual;
|
procedure WriteWindow1(AStream: TStream); virtual;
|
||||||
// Writes the index of the XF record used in the given cell
|
// Writes the index of the XF record used in the given cell
|
||||||
@ -1537,8 +1554,8 @@ begin
|
|||||||
end;
|
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 BIFF8
|
version for ansistrings since it is valid for all BIFF versions except for
|
||||||
where it has to overridden. }
|
BIFF8 where it has to be overridden. }
|
||||||
function TsSpreadBIFFReader.ReadString_8bitLen(AStream: TStream): String;
|
function TsSpreadBIFFReader.ReadString_8bitLen(AStream: TStream): String;
|
||||||
var
|
var
|
||||||
len: Byte;
|
len: Byte;
|
||||||
@ -1968,6 +1985,272 @@ begin
|
|||||||
{ Not used (BIFF5-BIFF8 only, not written in BIFF2-BIFF4 }
|
{ Not used (BIFF5-BIFF8 only, not written in BIFF2-BIFF4 }
|
||||||
end;
|
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
|
{ Writes an Excel 3-8 ROW record
|
||||||
Valid for BIFF3-BIFF8 }
|
Valid for BIFF3-BIFF8 }
|
||||||
procedure TsSpreadBIFFWriter.WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
procedure TsSpreadBIFFWriter.WriteRow(AStream: TStream; ASheet: TsWorksheet;
|
||||||
@ -2153,13 +2436,22 @@ var
|
|||||||
len: Byte;
|
len: Byte;
|
||||||
s: ansistring;
|
s: ansistring;
|
||||||
begin
|
begin
|
||||||
s := AString;
|
s := UTF8ToAnsi(AString);
|
||||||
len := Length(s);
|
len := Length(s);
|
||||||
AStream.WriteByte(len);
|
AStream.WriteByte(len);
|
||||||
AStream.WriteBuffer(s[1], len);
|
AStream.WriteBuffer(s[1], len);
|
||||||
Result := 1 + len;
|
Result := 1 + len;
|
||||||
end;
|
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
|
{ Writes an Excel 5/8 WINDOW1 record
|
||||||
This record contains general settings for the document window and
|
This record contains general settings for the document window and
|
||||||
global workbook settings.
|
global workbook settings.
|
||||||
|
Reference in New Issue
Block a user