
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@697 8e941d3f-bd1b-0410-a28a-d453659cc2b4
431 lines
11 KiB
ObjectPascal
Executable File
431 lines
11 KiB
ObjectPascal
Executable File
{
|
|
xlsbiff8.pas
|
|
|
|
Writes an Excel 8 file
|
|
|
|
An Excel worksheet stream consists of a number of subsequent records.
|
|
To ensure a properly formed file, the following order must be respected:
|
|
|
|
1st record: BOF
|
|
2nd to Nth record: Any record
|
|
Last record: EOF
|
|
|
|
Excel 8 files are OLE compound document files, and must be written using the
|
|
fpOLE library.
|
|
|
|
Records Needed to Make a BIFF5 File Microsoft Excel Can Use:
|
|
|
|
Required Records:
|
|
|
|
BOF - Set the 6 byte offset to 0x0005 (workbook globals)
|
|
Window1
|
|
FONT - At least five of these records must be included
|
|
XF - At least 15 Style XF records and 1 Cell XF record must be included
|
|
STYLE
|
|
BOUNDSHEET - Include one BOUNDSHEET record per worksheet
|
|
EOF
|
|
|
|
BOF - Set the 6 byte offset to 0x0010 (worksheet)
|
|
INDEX
|
|
DIMENSIONS
|
|
WINDOW2
|
|
EOF
|
|
|
|
Excel file format specification obtained from:
|
|
|
|
http://sc.openoffice.org/excelfileformat.pdf
|
|
|
|
Records Needed to Make a BIFF5 File Microsoft Excel Can Use obtained from:
|
|
|
|
http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q147732&ID=KB;EN-US;Q147732&LN=EN-US&rnk=2&SD=msdn&FR=0&qry=BIFF&src=DHCS_MSPSS_msdn_SRCH&SPR=MSALL&
|
|
|
|
AUTHORS: Felipe Monteiro de Carvalho
|
|
}
|
|
unit xlsbiff8;
|
|
|
|
{$ifdef fpc}
|
|
{$mode delphi}{$H+}
|
|
{$endif}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
fpspreadsheet;
|
|
|
|
type
|
|
|
|
{ TsSpreadBIFF5Writer }
|
|
|
|
TsSpreadBIFF5Writer = class(TsCustomSpreadWriter)
|
|
public
|
|
{ General writing methods }
|
|
procedure WriteToStream(AStream: TStream; AData: TsWorkbook); override;
|
|
{ Record writing methods }
|
|
procedure WriteBOF(AStream: TStream);
|
|
procedure WriteEOF(AStream: TStream);
|
|
procedure WriteFont(AStream: TStream; AFontName: Widestring = 'Arial');
|
|
procedure WriteFormat(AStream: TStream; AIndex: Word = 0; AFormatString: Widestring = 'General');
|
|
procedure WriteFormula(AStream: TStream; const ARow, ACol: Word; const AFormula: TsFormula); override;
|
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string); override;
|
|
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double); override;
|
|
procedure WriteXF(AStream: TStream);
|
|
end;
|
|
|
|
implementation
|
|
|
|
const
|
|
{ Excel record IDs }
|
|
INT_EXCEL_ID_BOF = $0809;
|
|
INT_EXCEL_ID_EOF = $000A;
|
|
INT_EXCEL_ID_FONT = $0031;
|
|
INT_EXCEL_ID_FORMAT = $041E;
|
|
INT_EXCEL_ID_FORMULA = $0006;
|
|
INT_EXCEL_ID_LABEL = $0004;
|
|
INT_EXCEL_ID_NUMBER = $0203;
|
|
INT_EXCEL_ID_XF = $00E0;
|
|
|
|
{ Cell Addresses constants }
|
|
MASK_EXCEL_ROW = $3FFF;
|
|
MASK_EXCEL_RELATIVE_ROW = $4000;
|
|
MASK_EXCEL_RELATIVE_COL = $8000;
|
|
|
|
{ Unicode string constants }
|
|
INT_EXCEL_UNCOMPRESSED_STRING = $01;
|
|
|
|
{ BOF record constants }
|
|
INT_EXCEL_BIFF8_VER = $0600;
|
|
INT_EXCEL_WORKBOOK = $0005;
|
|
INT_EXCEL_SHEET = $0010;
|
|
INT_EXCEL_CHART = $0020;
|
|
INT_EXCEL_MACRO_SHEET = $0040;
|
|
INT_EXCEL_BUILD_ID = $1FD2;
|
|
INT_EXCEL_BUILD_YEAR = $07CD;
|
|
INT_EXCEL_FILE_HISTORY = $0000C0C1;
|
|
INT_EXCEL_LOWEST_VER = $00000306;
|
|
|
|
{ FONT record constants}
|
|
INT_EXCEL_FONTWEIGHT_NORMAL = $0190;
|
|
|
|
{ XF record constants }
|
|
INT_EXCEL_XF_TYPE_PROT_STYLEXF = $FFF4;
|
|
|
|
{
|
|
Excel files are all written with Little Endian number,
|
|
so it's necessary to swap the numbers to be able to build a
|
|
correct file on big endian systems.
|
|
|
|
Endianess helper functions
|
|
}
|
|
|
|
function WordToLE(AValue: Word): Word;
|
|
begin
|
|
{$IFDEF BIG_ENDIAN}
|
|
Result := ((AValue shl 8) and $FF00) or ((AValue shr 8) and $00FF);
|
|
{$ELSE}
|
|
Result := AValue;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{
|
|
Exported functions
|
|
}
|
|
|
|
{ TsSpreadBIFF5Writer }
|
|
|
|
procedure TsSpreadBIFF5Writer.WriteToStream(AStream: TStream; AData: TsWorkbook);
|
|
begin
|
|
|
|
end;
|
|
|
|
{*******************************************************************
|
|
* TsSpreadBIFF5Writer.WriteBOF ()
|
|
*
|
|
* DESCRIPTION: Writes an Excel 5 BOF record
|
|
*
|
|
* This must be the first record on an Excel 5 stream
|
|
*
|
|
*******************************************************************}
|
|
procedure TsSpreadBIFF5Writer.WriteBOF(AStream: TStream);
|
|
begin
|
|
{ BIFF Record header }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOF));
|
|
AStream.WriteWord(WordToLE(16));
|
|
|
|
{ BIFF version }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_BIFF8_VER));
|
|
|
|
{ Data type }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_WORKBOOK));
|
|
|
|
{ Build identifier, must not be 0 }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_BUILD_ID));
|
|
|
|
{ Build year, must not be 0 }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_BUILD_YEAR));
|
|
|
|
{ File history flags }
|
|
// AStream.WriteDWord($00000000);
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_FILE_HISTORY));
|
|
|
|
{ Lowest Excel version that can read all records of this file }
|
|
// AStream.WriteDWord($00000000);
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_LOWEST_VER));
|
|
end;
|
|
|
|
{*******************************************************************
|
|
* TsSpreadBIFF5Writer.WriteEOF ()
|
|
*
|
|
* DESCRIPTION: Writes an Excel 5 EOF record
|
|
*
|
|
* This must be the last record on an Excel 5 stream
|
|
*
|
|
*******************************************************************}
|
|
procedure TsSpreadBIFF5Writer.WriteEOF(AStream: TStream);
|
|
begin
|
|
{ BIFF Record header }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_EOF));
|
|
AStream.WriteWord($0000);
|
|
end;
|
|
|
|
procedure TsSpreadBIFF5Writer.WriteFont(AStream: TStream;
|
|
AFontName: Widestring);
|
|
var
|
|
Len: Byte;
|
|
begin
|
|
Len := Length(AFontName);
|
|
|
|
{ BIFF Record header }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FONT));
|
|
AStream.WriteWord(WordToLE(14 + 2 + Len*2));
|
|
|
|
{ Height of the font in twips = 1/20 of a point }
|
|
AStream.WriteWord(WordToLE(200));
|
|
|
|
{ Option flags }
|
|
AStream.WriteWord(0);
|
|
|
|
{ Colour index }
|
|
AStream.WriteWord(0);
|
|
|
|
{ Font weight }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_FONTWEIGHT_NORMAL));
|
|
|
|
{ Underline type }
|
|
AStream.WriteByte(0);
|
|
|
|
{ Font family }
|
|
AStream.WriteByte(0);
|
|
|
|
{ Character set }
|
|
AStream.WriteByte(0);
|
|
|
|
{ Not used }
|
|
AStream.WriteByte(0);
|
|
|
|
{ Font name: Unicode string, 8-bit length }
|
|
AStream.WriteByte(Len);
|
|
AStream.WriteByte(INT_EXCEL_UNCOMPRESSED_STRING);
|
|
AStream.WriteBuffer(AFontName[1], Len*2);
|
|
end;
|
|
|
|
procedure TsSpreadBIFF5Writer.WriteFormat(AStream: TStream; AIndex: Word;
|
|
AFormatString: Widestring);
|
|
var
|
|
Len: Integer;
|
|
begin
|
|
Len := Length(AFormatString);
|
|
|
|
{ BIFF Record header }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMAT));
|
|
AStream.WriteWord(WordToLE(2 + 3 + Len*2));
|
|
|
|
{ Format index used by other records }
|
|
AStream.WriteWord(WordToLE(AIndex));
|
|
|
|
{ Unicode string, 16-bit length }
|
|
AStream.WriteWord(WordToLE(Len));
|
|
AStream.WriteByte(INT_EXCEL_UNCOMPRESSED_STRING);
|
|
AStream.WriteBuffer(AFormatString[1], Len*2);
|
|
end;
|
|
|
|
{*******************************************************************
|
|
* TsSpreadBIFF5Writer.WriteFormula ()
|
|
*
|
|
* DESCRIPTION: Writes an Excel 5 FORMULA record
|
|
*
|
|
* To input a formula to this method, first convert it
|
|
* to RPN, and then list all it's members in the
|
|
* AFormula array
|
|
*
|
|
*******************************************************************}
|
|
procedure TsSpreadBIFF5Writer.WriteFormula(AStream: TStream; const ARow,
|
|
ACol: Word; const AFormula: TsFormula);
|
|
{var
|
|
FormulaResult: double;
|
|
i: Integer;
|
|
RPNLength: Word;
|
|
TokenArraySizePos, RecordSizePos, FinalPos: Cardinal;}
|
|
begin
|
|
(* RPNLength := 0;
|
|
FormulaResult := 0.0;
|
|
|
|
{ BIFF Record header }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMULA));
|
|
RecordSizePos := AStream.Position;
|
|
AStream.WriteWord(WordToLE(17 + RPNLength));
|
|
|
|
{ BIFF Record data }
|
|
AStream.WriteWord(WordToLE(ARow));
|
|
AStream.WriteWord(WordToLE(ACol));
|
|
|
|
{ BIFF2 Attributes }
|
|
AStream.WriteByte($0);
|
|
AStream.WriteByte($0);
|
|
AStream.WriteByte($0);
|
|
|
|
{ Result of the formula in IEE 754 floating-point value }
|
|
AStream.WriteBuffer(FormulaResult, 8);
|
|
|
|
{ 0 = Do not recalculate
|
|
1 = Always recalculate }
|
|
AStream.WriteByte($1);
|
|
|
|
{ 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 }
|
|
TokenArraySizePos := AStream.Position;
|
|
AStream.WriteByte(RPNLength);
|
|
|
|
{ Formula data (RPN token array) }
|
|
for i := 0 to Length(AFormula) - 1 do
|
|
begin
|
|
{ Token identifier }
|
|
AStream.WriteByte(AFormula[i].TokenID);
|
|
Inc(RPNLength);
|
|
|
|
{ Additional data }
|
|
case AFormula[i].TokenID of
|
|
|
|
{ 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;
|
|
|
|
INT_EXCEL_TOKEN_TNUM:
|
|
begin
|
|
AStream.WriteBuffer(AFormula[i].DoubleValue, 8);
|
|
Inc(RPNLength, 8);
|
|
end;
|
|
|
|
INT_EXCEL_TOKEN_TREFR, INT_EXCEL_TOKEN_TREFV, INT_EXCEL_TOKEN_TREFA:
|
|
begin
|
|
AStream.WriteWord(AFormula[i].Row and MASK_EXCEL_ROW);
|
|
AStream.WriteByte(AFormula[i].Col);
|
|
Inc(RPNLength, 3);
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
{ Write sizes in the end, after we known them }
|
|
FinalPos := AStream.Position;
|
|
AStream.position := TokenArraySizePos;
|
|
AStream.WriteByte(RPNLength);
|
|
AStream.Position := RecordSizePos;
|
|
AStream.WriteWord(WordToLE(17 + RPNLength));
|
|
AStream.position := FinalPos;*)
|
|
end;
|
|
|
|
{*******************************************************************
|
|
* TsSpreadBIFF5Writer.WriteLabel ()
|
|
*
|
|
* DESCRIPTION: Writes an Excel 8 LABEL record
|
|
*
|
|
* Writes a string to the sheet
|
|
*
|
|
*******************************************************************}
|
|
procedure TsSpreadBIFF5Writer.WriteLabel(AStream: TStream; const ARow,
|
|
ACol: Word; const AValue: string);
|
|
var
|
|
L: Byte;
|
|
begin
|
|
L := Length(AValue);
|
|
|
|
{ BIFF Record header }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_LABEL));
|
|
AStream.WriteWord(WordToLE(8 + L));
|
|
|
|
{ BIFF Record data }
|
|
AStream.WriteWord(WordToLE(ARow));
|
|
AStream.WriteWord(WordToLE(ACol));
|
|
|
|
{ BIFF2 Attributes }
|
|
AStream.WriteByte($0);
|
|
AStream.WriteByte($0);
|
|
AStream.WriteByte($0);
|
|
|
|
{ String with 8-bit size }
|
|
AStream.WriteByte(L);
|
|
AStream.WriteBuffer(Pointer(AValue)^, L);
|
|
end;
|
|
|
|
{*******************************************************************
|
|
* TsSpreadBIFF5Writer.WriteNumber ()
|
|
*
|
|
* DESCRIPTION: Writes an Excel 5 NUMBER record
|
|
*
|
|
* Writes a number (64-bit floating point) to the sheet
|
|
*
|
|
*******************************************************************}
|
|
procedure TsSpreadBIFF5Writer.WriteNumber(AStream: TStream; const ARow,
|
|
ACol: Cardinal; const AValue: double);
|
|
begin
|
|
{ BIFF Record header }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_NUMBER));
|
|
AStream.WriteWord(WordToLE(14));
|
|
|
|
{ BIFF Record data }
|
|
AStream.WriteWord(WordToLE(ARow));
|
|
AStream.WriteWord(WordToLE(ACol));
|
|
|
|
{ Index to XF record }
|
|
AStream.WriteWord($0);
|
|
|
|
{ IEE 754 floating-point value }
|
|
AStream.WriteBuffer(AValue, 8);
|
|
end;
|
|
|
|
procedure TsSpreadBIFF5Writer.WriteXF(AStream: TStream);
|
|
begin
|
|
{ BIFF Record header }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_XF));
|
|
AStream.WriteWord(WordToLE(12));
|
|
|
|
{ Index to FONT record }
|
|
AStream.WriteByte($00);
|
|
|
|
{ Index to FORMAT record }
|
|
AStream.WriteByte($00);
|
|
|
|
{ XF type, cell protection and parent style XF }
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_XF_TYPE_PROT_STYLEXF));
|
|
|
|
{ Alignment, text break and text orientation }
|
|
AStream.WriteByte($00);
|
|
|
|
{ Flags for used attribute groups }
|
|
AStream.WriteByte($00);
|
|
|
|
{ XF_AREA_34 - Cell background area }
|
|
AStream.WriteWord($0000);
|
|
|
|
{ XF_BORDER_34 - Cell border lines }
|
|
AStream.WriteDWord($00000000);
|
|
end;
|
|
|
|
end.
|
|
|