2008-02-24 13:18:34 +00:00
|
|
|
{
|
2009-02-05 10:21:42 +00:00
|
|
|
xlsbiff8.pas
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-02-05 10:21:42 +00:00
|
|
|
Writes an Excel 8 file
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2009-09-01 16:47:45 +00:00
|
|
|
Excel 8 files are OLE compound document files, and must be written using the
|
2008-02-24 13:18:34 +00:00
|
|
|
fpOLE library.
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
Records Needed to Make a BIFF8 File Microsoft Excel Can Use:
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
The row and column numbering in BIFF files is zero-based.
|
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
Excel file format specification obtained from:
|
|
|
|
|
|
|
|
http://sc.openoffice.org/excelfileformat.pdf
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
AUTHORS: Felipe Monteiro de Carvalho
|
|
|
|
Jose Mejuto
|
2008-02-24 13:18:34 +00:00
|
|
|
}
|
|
|
|
unit xlsbiff8;
|
|
|
|
|
|
|
|
{$ifdef fpc}
|
2009-04-21 15:08:43 +00:00
|
|
|
{$mode delphi}
|
2008-02-24 13:18:34 +00:00
|
|
|
{$endif}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2009-04-21 15:08:43 +00:00
|
|
|
Classes, SysUtils, fpcanvas,
|
2009-05-26 18:32:09 +00:00
|
|
|
fpspreadsheet,
|
|
|
|
{$ifdef USE_NEW_OLE}
|
|
|
|
fpolebasic,
|
|
|
|
{$else}
|
|
|
|
fpolestorage,
|
|
|
|
{$endif}
|
|
|
|
fpsutils;
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ TsSpreadBIFF8Reader }
|
|
|
|
|
|
|
|
TsSpreadBIFF8Reader = class(TsCustomSpreadReader)
|
|
|
|
private
|
|
|
|
RecordSize: Word;
|
|
|
|
FWorksheet: TsWorksheet;
|
|
|
|
FWorksheetNames: TStringList;
|
|
|
|
FCurrentWorksheet: Integer;
|
|
|
|
function ReadWideString(const AStream: TStream;const ALength: WORD): WideString;
|
|
|
|
procedure ReadWorkbookGlobals(AStream: TStream; AData: TsWorkbook);
|
|
|
|
procedure ReadWorksheet(AStream: TStream; AData: TsWorkbook);
|
|
|
|
procedure ReadBoundsheet(AStream: TStream);
|
|
|
|
public
|
|
|
|
{ General reading methods }
|
|
|
|
procedure ReadFromFile(AFileName: string; AData: TsWorkbook); override;
|
|
|
|
procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); override;
|
|
|
|
{ Record writing methods }
|
|
|
|
procedure ReadFormula(AStream: TStream); override;
|
|
|
|
procedure ReadLabel(AStream: TStream); override;
|
|
|
|
procedure ReadNumber(AStream: TStream); override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TsSpreadBIFF8Writer }
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
TsSpreadBIFF8Writer = class(TsCustomSpreadWriter)
|
2008-02-24 13:18:34 +00:00
|
|
|
public
|
2009-04-21 15:08:43 +00:00
|
|
|
// constructor Create;
|
|
|
|
// destructor Destroy; override;
|
2008-02-24 13:18:34 +00:00
|
|
|
{ General writing methods }
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure WriteToFile(AFileName: string; AData: TsWorkbook); override;
|
2008-02-24 13:18:34 +00:00
|
|
|
procedure WriteToStream(AStream: TStream; AData: TsWorkbook); override;
|
|
|
|
{ Record writing methods }
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure WriteBOF(AStream: TStream; ADataType: Word);
|
|
|
|
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
|
|
|
|
procedure WriteDimensions(AStream: TStream);
|
2008-02-24 13:18:34 +00:00
|
|
|
procedure WriteEOF(AStream: TStream);
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure WriteFont(AStream: TStream; AFont: TFPCustomFont);
|
2009-02-02 09:58:51 +00:00
|
|
|
procedure WriteFormula(AStream: TStream; const ARow, ACol: Word; const AFormula: TsFormula); override;
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure WriteIndex(AStream: TStream);
|
2008-02-24 13:18:34 +00:00
|
|
|
procedure WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string); override;
|
|
|
|
procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double); override;
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure WriteStyle(AStream: TStream);
|
|
|
|
procedure WriteWindow1(AStream: TStream);
|
|
|
|
procedure WriteWindow2(AStream: TStream; ASheetSelected: Boolean);
|
|
|
|
procedure WriteXF(AStream: TStream; AFontIndex: Word; AXF_TYPE_PROT: Byte);
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
const
|
|
|
|
{ Excel record IDs }
|
|
|
|
INT_EXCEL_ID_BOF = $0809;
|
2009-08-12 13:08:44 +00:00
|
|
|
INT_EXCEL_ID_BOUNDSHEET = $0085; // Renamed to SHEET in the latest OpenOffice docs
|
2008-02-24 13:18:34 +00:00
|
|
|
INT_EXCEL_ID_EOF = $000A;
|
2009-04-21 15:08:43 +00:00
|
|
|
INT_EXCEL_ID_DIMENSIONS = $0200;
|
2008-02-24 13:18:34 +00:00
|
|
|
INT_EXCEL_ID_FONT = $0031;
|
|
|
|
INT_EXCEL_ID_FORMULA = $0006;
|
2009-04-21 15:08:43 +00:00
|
|
|
INT_EXCEL_ID_INDEX = $020B;
|
|
|
|
INT_EXCEL_ID_LABEL = $0204;
|
2008-02-24 13:18:34 +00:00
|
|
|
INT_EXCEL_ID_NUMBER = $0203;
|
2009-04-21 15:08:43 +00:00
|
|
|
INT_EXCEL_ID_STYLE = $0293;
|
|
|
|
INT_EXCEL_ID_WINDOW1 = $003D;
|
|
|
|
INT_EXCEL_ID_WINDOW2 = $023E;
|
2008-02-24 13:18:34 +00:00
|
|
|
INT_EXCEL_ID_XF = $00E0;
|
|
|
|
|
|
|
|
{ Cell Addresses constants }
|
|
|
|
MASK_EXCEL_ROW = $3FFF;
|
|
|
|
MASK_EXCEL_RELATIVE_ROW = $4000;
|
|
|
|
MASK_EXCEL_RELATIVE_COL = $8000;
|
|
|
|
|
|
|
|
{ BOF record constants }
|
2009-04-21 15:08:43 +00:00
|
|
|
INT_BOF_BIFF8_VER = $0600;
|
|
|
|
INT_BOF_WORKBOOK_GLOBALS= $0005;
|
|
|
|
INT_BOF_VB_MODULE = $0006;
|
|
|
|
INT_BOF_SHEET = $0010;
|
|
|
|
INT_BOF_CHART = $0020;
|
|
|
|
INT_BOF_MACRO_SHEET = $0040;
|
|
|
|
INT_BOF_WORKSPACE = $0100;
|
|
|
|
INT_BOF_BUILD_ID = $1FD2;
|
|
|
|
INT_BOF_BUILD_YEAR = $07CD;
|
|
|
|
|
|
|
|
{ FONT record constants }
|
|
|
|
INT_FONT_WEIGHT_NORMAL = $0190;
|
|
|
|
|
|
|
|
{ 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;
|
|
|
|
|
|
|
|
{ WINDOW1 record constants }
|
|
|
|
MASK_WINDOW1_OPTION_WINDOW_HIDDEN = $0001;
|
|
|
|
MASK_WINDOW1_OPTION_WINDOW_MINIMISED = $0002;
|
|
|
|
MASK_WINDOW1_OPTION_HORZ_SCROLL_VISIBLE = $0008;
|
|
|
|
MASK_WINDOW1_OPTION_VERT_SCROLL_VISIBLE = $0010;
|
|
|
|
MASK_WINDOW1_OPTION_WORKSHEET_TAB_VISIBLE = $0020;
|
|
|
|
|
|
|
|
{ WINDOW2 record constants }
|
|
|
|
MASK_WINDOW2_OPTION_SHOW_FORMULAS = $0001;
|
|
|
|
MASK_WINDOW2_OPTION_SHOW_GRID_LINES = $0002;
|
|
|
|
MASK_WINDOW2_OPTION_SHOW_SHEET_HEADERS = $0004;
|
|
|
|
MASK_WINDOW2_OPTION_PANES_ARE_FROZEN = $0008;
|
|
|
|
MASK_WINDOW2_OPTION_SHOW_ZERO_VALUES = $0010;
|
|
|
|
MASK_WINDOW2_OPTION_AUTO_GRIDLINE_COLOR = $0020;
|
|
|
|
MASK_WINDOW2_OPTION_COLUMNS_RIGHT_TO_LEFT = $0040;
|
|
|
|
MASK_WINDOW2_OPTION_SHOW_OUTLINE_SYMBOLS = $0080;
|
|
|
|
MASK_WINDOW2_OPTION_REMOVE_SPLITS_ON_UNFREEZE = $0100;
|
|
|
|
MASK_WINDOW2_OPTION_SHEET_SELECTED = $0200;
|
|
|
|
MASK_WINDOW2_OPTION_SHEET_ACTIVE = $0400;
|
|
|
|
|
|
|
|
{ XF substructures }
|
|
|
|
|
|
|
|
{ XF_TYPE_PROT - XF Type and Cell protection (3 Bits) - BIFF3-BIFF8 }
|
|
|
|
MASK_XF_TYPE_PROT_LOCKED = $1;
|
|
|
|
MASK_XF_TYPE_PROT_FORMULA_HIDDEN = $2;
|
|
|
|
MASK_XF_TYPE_PROT_STYLE_XF = $4; // 0 = CELL XF
|
|
|
|
|
|
|
|
{ XF_USED_ATTRIB - Attributes from parent Style XF (6 Bits) - BIFF3-BIFF8
|
|
|
|
|
|
|
|
In a CELL XF a cleared bit means that the parent attribute is used,
|
|
|
|
while a set bit indicates that the data in this XF is used
|
|
|
|
|
|
|
|
In a STYLE XF a cleared bit means that the data in this XF is used,
|
|
|
|
while a set bit indicates that the attribute should be ignored }
|
|
|
|
MASK_XF_USED_ATTRIB_NUMBER_FORMAT = $04;
|
|
|
|
MASK_XF_USED_ATTRIB_FONT = $08;
|
|
|
|
MASK_XF_USED_ATTRIB_TEXT = $10;
|
|
|
|
MASK_XF_USED_ATTRIB_BORDER_LINES = $20;
|
|
|
|
MASK_XF_USED_ATTRIB_BACKGROUND = $40;
|
|
|
|
MASK_XF_USED_ATTRIB_CELL_PROTECTION = $80;
|
|
|
|
|
|
|
|
{ XF_VERT_ALIGN }
|
|
|
|
MASK_XF_VERT_ALIGN_TOP = $00;
|
|
|
|
MASK_XF_VERT_ALIGN_CENTRED = $10;
|
|
|
|
MASK_XF_VERT_ALIGN_BOTTOM = $20;
|
|
|
|
MASK_XF_VERT_ALIGN_JUSTIFIED = $30;
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ XF record constants }
|
2009-04-21 15:08:43 +00:00
|
|
|
MASK_XF_TYPE_PROT = $0007;
|
|
|
|
MASK_XF_TYPE_PROT_PARENT = $FFF0;
|
|
|
|
|
|
|
|
MASK_XF_VERT_ALIGN = $70;
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{
|
2009-04-21 15:08:43 +00:00
|
|
|
Exported functions
|
2008-02-24 13:18:34 +00:00
|
|
|
}
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ TsSpreadBIFF8Writer }
|
|
|
|
|
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteToFile ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel BIFF8 file to the disc
|
|
|
|
*
|
|
|
|
* The BIFF 8 writer overrides this method because
|
|
|
|
* BIFF 8 is written as an OLE document, and our
|
|
|
|
* current OLE document writing method involves:
|
|
|
|
*
|
|
|
|
* 1 - Writing the BIFF data to a memory stream
|
|
|
|
*
|
|
|
|
* 2 - Write the memory stream data to disk using
|
|
|
|
* COM functions
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteToFile(AFileName: string; AData: TsWorkbook);
|
|
|
|
var
|
|
|
|
MemStream: TMemoryStream;
|
|
|
|
OutputStorage: TOLEStorage;
|
|
|
|
OLEDocument: TOLEDocument;
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
2009-04-21 15:08:43 +00:00
|
|
|
MemStream := TMemoryStream.Create;
|
|
|
|
OutputStorage := TOLEStorage.Create;
|
|
|
|
try
|
|
|
|
WriteToStream(MemStream, AData);
|
|
|
|
|
|
|
|
// Only one stream is necessary for any number of worksheets
|
|
|
|
OLEDocument.Stream := MemStream;
|
|
|
|
|
|
|
|
OutputStorage.WriteOLEFile(AFileName, OLEDocument,'Workbook');
|
|
|
|
finally
|
|
|
|
MemStream.Free;
|
|
|
|
OutputStorage.Free;
|
|
|
|
end;
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteToStream ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel BIFF8 record structure
|
|
|
|
*
|
|
|
|
* Be careful as this method doesn't write the OLE
|
|
|
|
* part of the document, just the BIFF records
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteToStream(AStream: TStream; AData: TsWorkbook);
|
|
|
|
var
|
|
|
|
FontData: TFPCustomFont;
|
|
|
|
MyData: TMemoryStream;
|
|
|
|
CurrentPos: Int64;
|
|
|
|
Boundsheets: array of Int64;
|
|
|
|
i, len: Integer;
|
|
|
|
begin
|
|
|
|
{ Write workbook globals }
|
|
|
|
|
|
|
|
WriteBOF(AStream, INT_BOF_WORKBOOK_GLOBALS);
|
|
|
|
|
|
|
|
WriteWindow1(AStream);
|
|
|
|
|
|
|
|
FontData := TFPCustomFont.Create;
|
|
|
|
try
|
|
|
|
FontData.Name := 'Arial';
|
|
|
|
|
|
|
|
// FONT0
|
|
|
|
WriteFont(AStream, FontData);
|
|
|
|
// FONT1
|
|
|
|
WriteFont(AStream, FontData);
|
|
|
|
// FONT2
|
|
|
|
WriteFont(AStream, FontData);
|
|
|
|
// FONT3
|
|
|
|
WriteFont(AStream, FontData);
|
|
|
|
// FONT5
|
|
|
|
WriteFont(AStream, FontData);
|
|
|
|
finally
|
|
|
|
FontData.Free;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// XF0
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF1
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF2
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF3
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF4
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF5
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF6
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF7
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF8
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF9
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF10
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF11
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF12
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF13
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF14
|
|
|
|
WriteXF(AStream, 0, MASK_XF_TYPE_PROT_STYLE_XF);
|
|
|
|
// XF15
|
|
|
|
WriteXF(AStream, 0, 0);
|
|
|
|
|
|
|
|
WriteStyle(AStream);
|
|
|
|
|
|
|
|
// A BOUNDSHEET for each worksheet
|
|
|
|
for i := 0 to AData.GetWorksheetCount - 1 do
|
|
|
|
begin
|
|
|
|
len := Length(Boundsheets);
|
|
|
|
SetLength(Boundsheets, len + 1);
|
|
|
|
Boundsheets[len] := WriteBoundsheet(AStream, AData.GetWorksheetByIndex(i).Name);
|
|
|
|
end;
|
|
|
|
|
|
|
|
WriteEOF(AStream);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Write each worksheet }
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
for i := 0 to AData.GetWorksheetCount - 1 do
|
|
|
|
begin
|
|
|
|
{ First goes back and writes the position of the BOF of the
|
|
|
|
sheet on the respective BOUNDSHEET record }
|
|
|
|
CurrentPos := AStream.Position;
|
|
|
|
AStream.Position := Boundsheets[i];
|
|
|
|
AStream.WriteDWord(DWordToLE(DWORD(CurrentPos)));
|
|
|
|
AStream.Position := CurrentPos;
|
|
|
|
|
|
|
|
WriteBOF(AStream, INT_BOF_SHEET);
|
|
|
|
|
|
|
|
WriteIndex(AStream);
|
|
|
|
|
|
|
|
WriteDimensions(AStream);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
WriteWindow2(AStream, True);
|
|
|
|
|
|
|
|
WriteCellsToStream(AStream, AData.GetWorksheetByIndex(i).FCells);
|
|
|
|
|
|
|
|
WriteEOF(AStream);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Cleanup }
|
|
|
|
|
|
|
|
SetLength(Boundsheets, 0);
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
2009-04-21 15:08:43 +00:00
|
|
|
* TsSpreadBIFF8Writer.WriteBOF ()
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
2009-04-21 15:08:43 +00:00
|
|
|
* DESCRIPTION: Writes an Excel 8 BOF record
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
2009-04-21 15:08:43 +00:00
|
|
|
* This must be the first record on an Excel 8 stream
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
|
|
|
*******************************************************************}
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure TsSpreadBIFF8Writer.WriteBOF(AStream: TStream; ADataType: Word);
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOF));
|
|
|
|
AStream.WriteWord(WordToLE(16));
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ BIFF version. Should only be used if this BOF is for the workbook globals }
|
|
|
|
{ OpenOffice rejects to correctly read xls files if this field is
|
|
|
|
omitted as docs. says, or even if it is being written to zero value,
|
|
|
|
Not tested with Excel, but MSExcel reader opens it as expected }
|
|
|
|
AStream.WriteWord(WordToLE(INT_BOF_BIFF8_VER));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Data type }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(ADataType));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Build identifier, must not be 0 }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(INT_BOF_BUILD_ID));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Build year, must not be 0 }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(INT_BOF_BUILD_YEAR));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ File history flags }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteDWord(DWordToLE(0));
|
|
|
|
|
|
|
|
{ Lowest Excel version that can read all records in this file 5?}
|
|
|
|
AStream.WriteDWord(DWordToLE(0)); //?????????
|
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteBoundsheet ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 BOUNDSHEET record
|
|
|
|
*
|
|
|
|
* Always located on the workbook globals substream.
|
|
|
|
*
|
|
|
|
* One BOUNDSHEET is written for each worksheet.
|
|
|
|
*
|
|
|
|
* RETURNS: The stream position where the absolute stream position
|
|
|
|
* of the BOF of this sheet should be written (4 bytes size).
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
function TsSpreadBIFF8Writer.WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
|
|
|
|
var
|
|
|
|
Len: Byte;
|
|
|
|
WideSheetName: WideString;
|
|
|
|
begin
|
|
|
|
WideSheetName:=UTF8Decode(ASheetName);
|
|
|
|
Len := Length(WideSheetName);
|
|
|
|
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOUNDSHEET));
|
|
|
|
AStream.WriteWord(WordToLE(6 + 1 + 1 + Len * Sizeof(WideChar)));
|
|
|
|
|
|
|
|
{ Absolute stream position of the BOF record of the sheet represented
|
|
|
|
by this record }
|
|
|
|
Result := AStream.Position;
|
|
|
|
AStream.WriteDWord(DWordToLE(0));
|
|
|
|
|
|
|
|
{ Visibility }
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
|
|
|
|
{ Sheet type }
|
|
|
|
AStream.WriteByte(0);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Sheet name: Unicode string char count 1 byte }
|
|
|
|
AStream.WriteByte(Len);
|
|
|
|
{String flags}
|
|
|
|
AStream.WriteByte(1);
|
|
|
|
AStream.WriteBuffer(WideStringToLE(WideSheetName)[1], Len * Sizeof(WideChar));
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
2009-04-21 15:08:43 +00:00
|
|
|
* TsSpreadBIFF8Writer.WriteIndex ()
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
2009-04-21 15:08:43 +00:00
|
|
|
* DESCRIPTION: Writes an Excel 8 DIMENSIONS record
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
2009-04-21 15:08:43 +00:00
|
|
|
* nm = (rl - rf - 1) / 32 + 1 (using integer division)
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
|
|
|
*******************************************************************}
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure TsSpreadBIFF8Writer.WriteDimensions(AStream: TStream);
|
|
|
|
begin
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_DIMENSIONS));
|
|
|
|
AStream.WriteWord(WordToLE(14));
|
|
|
|
|
|
|
|
{ Index to first used row }
|
|
|
|
AStream.WriteDWord(DWordToLE(0));
|
|
|
|
|
|
|
|
{ Index to last used row, increased by 1 }
|
|
|
|
AStream.WriteDWord(DWordToLE(33));
|
|
|
|
|
|
|
|
{ Index to first used column }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
|
|
|
|
{ Index to last used column, increased by 1 }
|
|
|
|
AStream.WriteWord(WordToLE(10));
|
|
|
|
|
|
|
|
{ Not used }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteEOF ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 EOF record
|
|
|
|
*
|
|
|
|
* This must be the last record on an Excel 8 stream
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteEOF(AStream: TStream);
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_EOF));
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE($0000));
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteFont ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 FONT record
|
|
|
|
*
|
|
|
|
* The font data is passed in an instance of TFPCustomFont
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteFont(AStream: TStream; AFont: TFPCustomFont);
|
2008-02-24 13:18:34 +00:00
|
|
|
var
|
|
|
|
Len: Byte;
|
2009-04-21 15:08:43 +00:00
|
|
|
WideFontName: WideString;
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
2009-04-21 15:08:43 +00:00
|
|
|
WideFontName:=AFont.Name;
|
|
|
|
Len := Length(WideFontName);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FONT));
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(14 + 1 + 1 + Len * Sizeof(WideChar)));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Height of the font in twips = 1/20 of a point }
|
|
|
|
AStream.WriteWord(WordToLE(200));
|
|
|
|
|
|
|
|
{ Option flags }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(0));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Colour index }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE($7FFF));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Font weight }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(INT_FONT_WEIGHT_NORMAL));
|
|
|
|
|
|
|
|
{ Escapement type }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Underline type }
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
|
|
|
|
{ Font family }
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
|
|
|
|
{ Character set }
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
|
|
|
|
{ Not used }
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Font name: Unicodestring, char count in 1 byte }
|
2008-02-24 13:18:34 +00:00
|
|
|
AStream.WriteByte(Len);
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Widestring flags, 1=regular unicode LE string }
|
|
|
|
AStream.WriteByte(1);
|
|
|
|
AStream.WriteBuffer(WideStringToLE(WideFontName)[1], Len * Sizeof(WideChar));
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
2009-04-21 15:08:43 +00:00
|
|
|
* TsSpreadBIFF8Writer.WriteFormula ()
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure TsSpreadBIFF8Writer.WriteFormula(AStream: TStream; const ARow,
|
2009-02-02 09:58:51 +00:00
|
|
|
ACol: Word; const AFormula: TsFormula);
|
|
|
|
{var
|
2008-02-24 13:18:34 +00:00
|
|
|
FormulaResult: double;
|
|
|
|
i: Integer;
|
|
|
|
RPNLength: Word;
|
2009-04-21 15:08:43 +00:00
|
|
|
TokenArraySizePos, RecordSizePos, FinalPos: Int64;}
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
2009-02-02 09:58:51 +00:00
|
|
|
(* RPNLength := 0;
|
2008-02-24 13:18:34 +00:00
|
|
|
FormulaResult := 0.0;
|
|
|
|
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMULA));
|
|
|
|
RecordSizePos := AStream.Position;
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(22 + RPNLength));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ BIFF Record data }
|
|
|
|
AStream.WriteWord(WordToLE(ARow));
|
|
|
|
AStream.WriteWord(WordToLE(ACol));
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Index to XF Record }
|
|
|
|
AStream.WriteWord($0000);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Result of the formula in IEE 754 floating-point value }
|
|
|
|
AStream.WriteBuffer(FormulaResult, 8);
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Options flags }
|
|
|
|
AStream.WriteWord(WordToLE(MASK_FORMULA_RECALCULATE_ALWAYS));
|
|
|
|
|
|
|
|
{ Not used }
|
|
|
|
AStream.WriteDWord(0);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ 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;
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(RPNLength);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ 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;
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(22 + RPNLength));
|
2009-02-02 09:58:51 +00:00
|
|
|
AStream.position := FinalPos;*)
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
2009-04-21 15:08:43 +00:00
|
|
|
* TsSpreadBIFF8Writer.WriteIndex ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 INDEX record
|
|
|
|
*
|
|
|
|
* nm = (rl - rf - 1) / 32 + 1 (using integer division)
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteIndex(AStream: TStream);
|
|
|
|
begin
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_INDEX));
|
|
|
|
AStream.WriteWord(WordToLE(16));
|
|
|
|
|
|
|
|
{ Not used }
|
|
|
|
AStream.WriteDWord(DWordToLE(0));
|
|
|
|
|
|
|
|
{ Index to first used row, rf, 0 based }
|
|
|
|
AStream.WriteDWord(DWordToLE(0));
|
|
|
|
|
|
|
|
{ Index to first row of unused tail of sheet, rl, last used row + 1, 0 based }
|
|
|
|
AStream.WriteDWord(DWordToLE(0));
|
|
|
|
|
|
|
|
{ Absolute stream position of the DEFCOLWIDTH record of the current sheet.
|
|
|
|
If it doesn't exist, the offset points to where it would occur. }
|
|
|
|
AStream.WriteDWord(DWordToLE($00));
|
|
|
|
|
|
|
|
{ Array of nm absolute stream positions of the DBCELL record of each Row Block }
|
|
|
|
|
|
|
|
{ OBS: It seams to be no problem just ignoring this part of the record }
|
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteLabel ()
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 LABEL record
|
|
|
|
*
|
|
|
|
* Writes a string to the sheet
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure TsSpreadBIFF8Writer.WriteLabel(AStream: TStream; const ARow,
|
2008-02-24 13:18:34 +00:00
|
|
|
ACol: Word; const AValue: string);
|
|
|
|
var
|
2009-04-21 15:08:43 +00:00
|
|
|
L: Word;
|
|
|
|
WideValue: WideString;
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
2009-04-21 15:08:43 +00:00
|
|
|
WideValue := UTF8Decode(AValue);
|
|
|
|
if WideValue = '' then
|
|
|
|
begin
|
|
|
|
// Bad formatted UTF8String (maybe ANSI?)
|
|
|
|
if Length(AValue)<>0 then begin
|
|
|
|
//It was an ANSI string written as UTF8 quite sure, so raise exception.
|
|
|
|
Raise Exception.CreateFmt('Expected UTF8 text but probably ANSI text found in cell [%d,%d]',[ARow,ACol]);
|
|
|
|
end;
|
|
|
|
Exit;
|
|
|
|
end;
|
|
|
|
L := Length(WideValue);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_LABEL));
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(8 + 1 + L * Sizeof(WideChar)));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ BIFF Record data }
|
|
|
|
AStream.WriteWord(WordToLE(ARow));
|
|
|
|
AStream.WriteWord(WordToLE(ACol));
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Index to XF record }
|
|
|
|
AStream.WriteWord(WordToLE(15));
|
|
|
|
|
|
|
|
{ Byte String with 16-bit size }
|
|
|
|
AStream.WriteWord(WordToLE(L));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Byte flags. 1 means regular Unicode LE encoding}
|
|
|
|
AStream.WriteByte(1);
|
|
|
|
AStream.WriteBuffer(WideStringToLE(WideValue)[1], L * Sizeof(WideChar));
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
2009-04-21 15:08:43 +00:00
|
|
|
* TsSpreadBIFF8Writer.WriteNumber ()
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
2009-04-21 15:08:43 +00:00
|
|
|
* DESCRIPTION: Writes an Excel 8 NUMBER record
|
2008-02-24 13:18:34 +00:00
|
|
|
*
|
|
|
|
* Writes a number (64-bit floating point) to the sheet
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure TsSpreadBIFF8Writer.WriteNumber(AStream: TStream; const ARow,
|
2008-02-24 13:18:34 +00:00
|
|
|
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 }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE($0));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ IEE 754 floating-point value (is different in BIGENDIAN???) }
|
2008-02-24 13:18:34 +00:00
|
|
|
AStream.WriteBuffer(AValue, 8);
|
|
|
|
end;
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteStyle ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 STYLE record
|
|
|
|
*
|
|
|
|
* Registers the name of a user-defined style or
|
|
|
|
* specific options for a built-in cell style.
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteStyle(AStream: TStream);
|
|
|
|
begin
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_STYLE));
|
|
|
|
AStream.WriteWord(WordToLE(4));
|
|
|
|
|
|
|
|
{ Index to style XF and defines if it's a built-in or used defined style }
|
|
|
|
AStream.WriteWord(WordToLE(MASK_STYLE_BUILT_IN));
|
|
|
|
|
|
|
|
{ Built-in cell style identifier }
|
|
|
|
AStream.WriteByte($00);
|
|
|
|
|
|
|
|
{ Level if the identifier for a built-in style is RowLevel or ColLevel, $FF otherwise }
|
|
|
|
AStream.WriteByte($FF);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteWindow1 ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 WINDOW1 record
|
|
|
|
*
|
|
|
|
* This record contains general settings for the
|
|
|
|
* document window and global workbook settings.
|
|
|
|
*
|
|
|
|
* The values written here are reasonable defaults,
|
|
|
|
* which should work for most sheets.
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteWindow1(AStream: TStream);
|
|
|
|
begin
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_WINDOW1));
|
|
|
|
AStream.WriteWord(WordToLE(18));
|
|
|
|
|
|
|
|
{ Horizontal position of the document window, in twips = 1 / 20 of a point }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
|
|
|
|
{ Vertical position of the document window, in twips = 1 / 20 of a point }
|
|
|
|
AStream.WriteWord(WordToLE($0069));
|
|
|
|
|
|
|
|
{ Width of the document window, in twips = 1 / 20 of a point }
|
|
|
|
AStream.WriteWord(WordToLE($339F));
|
|
|
|
|
|
|
|
{ Height of the document window, in twips = 1 / 20 of a point }
|
|
|
|
AStream.WriteWord(WordToLE($1B5D));
|
|
|
|
|
|
|
|
{ Option flags }
|
|
|
|
AStream.WriteWord(WordToLE(
|
|
|
|
MASK_WINDOW1_OPTION_HORZ_SCROLL_VISIBLE or
|
|
|
|
MASK_WINDOW1_OPTION_VERT_SCROLL_VISIBLE or
|
|
|
|
MASK_WINDOW1_OPTION_WORKSHEET_TAB_VISIBLE));
|
|
|
|
|
|
|
|
{ Index to active (displayed) worksheet }
|
|
|
|
AStream.WriteWord(WordToLE($00));
|
|
|
|
|
|
|
|
{ Index of first visible tab in the worksheet tab bar }
|
|
|
|
AStream.WriteWord(WordToLE($00));
|
|
|
|
|
|
|
|
{ Number of selected worksheets }
|
|
|
|
AStream.WriteWord(WordToLE(1));
|
|
|
|
|
|
|
|
{ Width of worksheet tab bar (in 1/1000 of window width).
|
|
|
|
The remaining space is used by the horizontal scroll bar }
|
|
|
|
AStream.WriteWord(WordToLE(600));
|
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteWindow2 ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 WINDOW2 record
|
|
|
|
*
|
|
|
|
* This record contains aditional settings for the
|
|
|
|
* document window (BIFF2-BIFF4) or for a specific
|
|
|
|
* worksheet (BIFF5-BIFF8).
|
|
|
|
*
|
|
|
|
* The values written here are reasonable defaults,
|
|
|
|
* which should work for most sheets.
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteWindow2(AStream: TStream;
|
|
|
|
ASheetSelected: Boolean);
|
|
|
|
var
|
|
|
|
Options: Word;
|
|
|
|
begin
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_WINDOW2));
|
|
|
|
AStream.WriteWord(WordToLE(18));
|
|
|
|
|
|
|
|
{ Options flags }
|
|
|
|
Options := MASK_WINDOW2_OPTION_SHOW_GRID_LINES or
|
|
|
|
MASK_WINDOW2_OPTION_SHOW_SHEET_HEADERS or
|
|
|
|
MASK_WINDOW2_OPTION_SHOW_ZERO_VALUES or
|
|
|
|
MASK_WINDOW2_OPTION_AUTO_GRIDLINE_COLOR or
|
|
|
|
MASK_WINDOW2_OPTION_SHOW_OUTLINE_SYMBOLS or
|
|
|
|
MASK_WINDOW2_OPTION_SHEET_ACTIVE;
|
|
|
|
|
|
|
|
if ASheetSelected then Options := Options or MASK_WINDOW2_OPTION_SHEET_SELECTED;
|
|
|
|
|
|
|
|
AStream.WriteWord(WordToLE(Options));
|
|
|
|
|
|
|
|
{ Index to first visible row }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
|
|
|
|
{ Index to first visible column }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
|
|
|
|
{ Grid line index colour }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
|
|
|
|
{ Not used }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
|
|
|
|
{ Cached magnification factor in page break preview (in percent); 0 = Default (60%) }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
|
|
|
|
{ Cached magnification factor in normal view (in percent); 0 = Default (100%) }
|
|
|
|
AStream.WriteWord(WordToLE(0));
|
|
|
|
|
|
|
|
{ Not used }
|
|
|
|
AStream.WriteDWord(DWordToLE(0));
|
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
|
|
|
* TsSpreadBIFF8Writer.WriteXF ()
|
|
|
|
*
|
|
|
|
* DESCRIPTION: Writes an Excel 8 XF record
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
procedure TsSpreadBIFF8Writer.WriteXF(AStream: TStream; AFontIndex: Word;
|
|
|
|
AXF_TYPE_PROT: Byte);
|
|
|
|
var
|
|
|
|
XFOptions: Word;
|
|
|
|
XFAlignment, XFOrientationAttrib: Byte;
|
2008-02-24 13:18:34 +00:00
|
|
|
begin
|
|
|
|
{ BIFF Record header }
|
|
|
|
AStream.WriteWord(WordToLE(INT_EXCEL_ID_XF));
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(20));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Index to FONT record }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE(AFontIndex));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ Index to FORMAT record }
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteWord(WordToLE($00));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
|
|
|
{ XF type, cell protection and parent style XF }
|
2009-04-21 15:08:43 +00:00
|
|
|
XFOptions := AXF_TYPE_PROT and MASK_XF_TYPE_PROT;
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
if AXF_TYPE_PROT and MASK_XF_TYPE_PROT_STYLE_XF <> 0 then
|
|
|
|
XFOptions := XFOptions or MASK_XF_TYPE_PROT_PARENT;
|
|
|
|
|
|
|
|
AStream.WriteWord(WordToLE(XFOptions));
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Alignment and text break }
|
|
|
|
XFAlignment := MASK_XF_VERT_ALIGN_BOTTOM;
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
AStream.WriteByte(XFAlignment);
|
|
|
|
|
|
|
|
{ Text rotation }
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
|
|
|
|
{ Indentation, shrink and text direction }
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
|
|
|
|
{ Used attributes }
|
|
|
|
XFOrientationAttrib :=
|
|
|
|
MASK_XF_USED_ATTRIB_NUMBER_FORMAT or
|
|
|
|
MASK_XF_USED_ATTRIB_FONT or
|
|
|
|
MASK_XF_USED_ATTRIB_TEXT or
|
|
|
|
MASK_XF_USED_ATTRIB_BORDER_LINES or
|
|
|
|
MASK_XF_USED_ATTRIB_BACKGROUND or
|
|
|
|
MASK_XF_USED_ATTRIB_CELL_PROTECTION;
|
|
|
|
|
|
|
|
AStream.WriteByte(XFOrientationAttrib);
|
2008-02-24 13:18:34 +00:00
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
{ Cell border lines and background area }
|
|
|
|
AStream.WriteDWord(DWordToLE($00000000));
|
|
|
|
AStream.WriteDWord(DWordToLE($00000000));
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
AStream.WriteByte(0);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ TsSpreadBIFF8Reader }
|
|
|
|
|
|
|
|
function TsSpreadBIFF8Reader.ReadWideString(const AStream: TStream;
|
|
|
|
const ALength: WORD): WideString;
|
|
|
|
var
|
|
|
|
StringFlags: BYTE;
|
|
|
|
AnsiStrValue: AnsiString;
|
|
|
|
begin
|
|
|
|
StringFlags:=AStream.ReadByte;
|
|
|
|
if StringFlags and 1 = 1 Then begin
|
|
|
|
//String is WideStringLE
|
|
|
|
SetLength(Result,ALength);
|
|
|
|
AStream.ReadBuffer(Result[1],ALength * SizeOf(WideChar));
|
|
|
|
Result:=WideStringLEToN(Result);
|
|
|
|
end else begin
|
|
|
|
//String is 1 byte per char, maybe ANSI ?
|
|
|
|
SetLength(AnsiStrValue,ALength);
|
|
|
|
AStream.ReadBuffer(AnsiStrValue[1],ALength);
|
|
|
|
Result:=AnsiStrValue; //implicit conversion.
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadBIFF8Reader.ReadWorkbookGlobals(AStream: TStream;
|
|
|
|
AData: TsWorkbook);
|
|
|
|
var
|
|
|
|
SectionEOF: Boolean = False;
|
|
|
|
RecordType: Word;
|
|
|
|
CurStreamPos: Int64;
|
|
|
|
begin
|
|
|
|
while (not SectionEOF) do
|
|
|
|
begin
|
|
|
|
{ Read the record header }
|
|
|
|
RecordType := WordLEToN(AStream.ReadWord);
|
|
|
|
RecordSize := WordLEToN(AStream.ReadWord);
|
|
|
|
|
|
|
|
CurStreamPos := AStream.Position;
|
|
|
|
|
|
|
|
case RecordType of
|
|
|
|
INT_EXCEL_ID_BOF: ;
|
|
|
|
INT_EXCEL_ID_BOUNDSHEET: ReadBoundSheet(AStream);
|
|
|
|
INT_EXCEL_ID_EOF: SectionEOF := True;
|
|
|
|
else
|
|
|
|
// nothing
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Make sure we are in the right position for the next record
|
|
|
|
AStream.Seek(CurStreamPos + RecordSize, soFromBeginning);
|
|
|
|
|
|
|
|
// Check for the end of the file
|
|
|
|
if AStream.Position >= AStream.Size then SectionEOF := True;
|
|
|
|
end;
|
2008-02-24 13:18:34 +00:00
|
|
|
end;
|
|
|
|
|
2009-04-21 15:08:43 +00:00
|
|
|
procedure TsSpreadBIFF8Reader.ReadWorksheet(AStream: TStream; AData: TsWorkbook);
|
|
|
|
var
|
|
|
|
SectionEOF: Boolean = False;
|
|
|
|
RecordType: Word;
|
|
|
|
CurStreamPos: Int64;
|
|
|
|
begin
|
|
|
|
FWorksheet := AData.AddWorksheet(FWorksheetNames.Strings[FCurrentWorksheet]);
|
|
|
|
|
|
|
|
while (not SectionEOF) do
|
|
|
|
begin
|
|
|
|
{ Read the record header }
|
|
|
|
RecordType := WordLEToN(AStream.ReadWord);
|
|
|
|
RecordSize := WordLEToN(AStream.ReadWord);
|
|
|
|
|
|
|
|
CurStreamPos := AStream.Position;
|
|
|
|
|
|
|
|
case RecordType of
|
|
|
|
|
|
|
|
INT_EXCEL_ID_NUMBER: ReadNumber(AStream);
|
|
|
|
INT_EXCEL_ID_LABEL: ReadLabel(AStream);
|
|
|
|
INT_EXCEL_ID_FORMULA: ReadFormula(AStream);
|
|
|
|
INT_EXCEL_ID_BOF: ;
|
|
|
|
INT_EXCEL_ID_EOF: SectionEOF := True;
|
|
|
|
else
|
|
|
|
// nothing
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Make sure we are in the right position for the next record
|
|
|
|
AStream.Seek(CurStreamPos + RecordSize, soFromBeginning);
|
|
|
|
|
|
|
|
// Check for the end of the file
|
|
|
|
if AStream.Position >= AStream.Size then SectionEOF := True;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadBIFF8Reader.ReadBoundsheet(AStream: TStream);
|
|
|
|
var
|
|
|
|
Len: Byte;
|
|
|
|
WideName: WideString;
|
|
|
|
begin
|
|
|
|
{ Absolute stream position of the BOF record of the sheet represented
|
|
|
|
by this record }
|
|
|
|
// Just assume that they are in order
|
|
|
|
AStream.ReadDWord();
|
|
|
|
|
|
|
|
{ Visibility }
|
|
|
|
AStream.ReadByte();
|
|
|
|
|
|
|
|
{ Sheet type }
|
|
|
|
AStream.ReadByte();
|
|
|
|
|
|
|
|
{ Sheet name: 8-bit length }
|
|
|
|
Len := AStream.ReadByte();
|
|
|
|
|
|
|
|
{ Read string with flags }
|
|
|
|
WideName:=ReadWideString(AStream,Len);
|
|
|
|
|
|
|
|
FWorksheetNames.Add(UTF8Encode(WideName));
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadBIFF8Reader.ReadFromFile(AFileName: string; AData: TsWorkbook);
|
|
|
|
var
|
|
|
|
MemStream: TMemoryStream;
|
|
|
|
OLEStorage: TOLEStorage;
|
|
|
|
OLEDocument: TOLEDocument;
|
|
|
|
begin
|
|
|
|
MemStream := TMemoryStream.Create;
|
|
|
|
OLEStorage := TOLEStorage.Create;
|
|
|
|
try
|
|
|
|
// Only one stream is necessary for any number of worksheets
|
|
|
|
OLEDocument.Stream := MemStream;
|
|
|
|
|
2009-05-10 17:59:25 +00:00
|
|
|
OLEStorage.ReadOLEFile(AFileName, OLEDocument,'Workbook');
|
2009-04-21 15:08:43 +00:00
|
|
|
|
|
|
|
// Rewind the stream and read from it
|
|
|
|
MemStream.Position := 0;
|
|
|
|
ReadFromStream(MemStream, AData);
|
|
|
|
|
|
|
|
// Uncomment to verify if the data was correctly optained from the OLE file
|
|
|
|
// MemStream.SaveToFile(SysUtils.ChangeFileExt(AFileName, 'bin.xls'));
|
|
|
|
finally
|
|
|
|
MemStream.Free;
|
|
|
|
OLEStorage.Free;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadBIFF8Reader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
|
|
|
|
var
|
|
|
|
BIFF8EOF: Boolean;
|
|
|
|
begin
|
|
|
|
{ Initializations }
|
|
|
|
|
|
|
|
FWorksheetNames := TStringList.Create;
|
|
|
|
FWorksheetNames.Clear;
|
|
|
|
FCurrentWorksheet := 0;
|
|
|
|
BIFF8EOF := False;
|
|
|
|
|
|
|
|
{ Read workbook globals }
|
|
|
|
|
|
|
|
ReadWorkbookGlobals(AStream, AData);
|
|
|
|
|
|
|
|
// Check for the end of the file
|
|
|
|
if AStream.Position >= AStream.Size then BIFF8EOF := True;
|
|
|
|
|
|
|
|
{ Now read all worksheets }
|
|
|
|
|
|
|
|
while (not BIFF8EOF) do
|
|
|
|
begin
|
|
|
|
ReadWorksheet(AStream, AData);
|
|
|
|
|
|
|
|
// Check for the end of the file
|
|
|
|
if AStream.Position >= AStream.Size then BIFF8EOF := True;
|
|
|
|
|
|
|
|
// Final preparations
|
|
|
|
Inc(FCurrentWorksheet);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Finalizations }
|
|
|
|
|
|
|
|
FWorksheetNames.Free;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadBIFF8Reader.ReadFormula(AStream: TStream);
|
|
|
|
begin
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadBIFF8Reader.ReadLabel(AStream: TStream);
|
|
|
|
var
|
|
|
|
L: Word;
|
|
|
|
StringFlags: BYTE;
|
|
|
|
ARow, ACol: Word;
|
|
|
|
WideStrValue: WideString;
|
|
|
|
AnsiStrValue: AnsiString;
|
|
|
|
begin
|
|
|
|
{ BIFF Record data }
|
|
|
|
ARow := WordLEToN(AStream.ReadWord);
|
|
|
|
ACol := WordLEToN(AStream.ReadWord);
|
|
|
|
|
|
|
|
{ Index to XF record, not used }
|
|
|
|
AStream.ReadWord();
|
|
|
|
|
|
|
|
{ Byte String with 16-bit size }
|
|
|
|
L := WordLEtoN(AStream.ReadWord());
|
|
|
|
|
|
|
|
{ Read string with flags }
|
|
|
|
WideStrValue:=ReadWideString(AStream,L);
|
|
|
|
|
|
|
|
{ Save the data }
|
|
|
|
FWorksheet.WriteUTF8Text(ARow, ACol, UTF8Encode(WideStrValue));
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TsSpreadBIFF8Reader.ReadNumber(AStream: TStream);
|
|
|
|
var
|
|
|
|
ARow, ACol: Word;
|
|
|
|
AValue: Double;
|
|
|
|
begin
|
|
|
|
{ BIFF Record data }
|
|
|
|
ARow := WordLEToN(AStream.ReadWord);
|
|
|
|
ACol := WordLEToN(AStream.ReadWord);
|
|
|
|
|
|
|
|
{ Index to XF record, not used }
|
|
|
|
AStream.ReadWord();
|
|
|
|
|
|
|
|
{ IEE 754 floating-point value }
|
|
|
|
AStream.ReadBuffer(AValue, 8);
|
|
|
|
|
|
|
|
{ Save the data }
|
|
|
|
FWorksheet.WriteNumber(ARow, ACol, AValue);
|
|
|
|
end;
|
|
|
|
|
|
|
|
{*******************************************************************
|
|
|
|
* Initialization section
|
|
|
|
*
|
|
|
|
* Registers this reader / writer on fpSpreadsheet
|
|
|
|
*
|
|
|
|
*******************************************************************}
|
|
|
|
|
|
|
|
initialization
|
|
|
|
|
2009-05-10 17:59:25 +00:00
|
|
|
RegisterSpreadFormat(TsSpreadBIFF8Reader, TsSpreadBIFF8Writer, sfExcel8);
|
2009-04-21 15:08:43 +00:00
|
|
|
|
2008-02-24 13:18:34 +00:00
|
|
|
end.
|
|
|
|
|