lazarus-ccr/components/fpspreadsheet/xlsbiff8.pas
sekelsenmat c064101224 Adds epiktimer
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@697 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2009-02-05 10:21:42 +00:00

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.