diff --git a/components/fpspreadsheet/builddoc.bat b/components/fpspreadsheet/builddoc.bat
new file mode 100755
index 000000000..71a2a3b5b
--- /dev/null
+++ b/components/fpspreadsheet/builddoc.bat
@@ -0,0 +1,2 @@
+"C:\Program Files\Doc-O-Matic 6 Express\domexpress.exe" -config "HTML Help" fpspreadsheet.dox-express
+pause
diff --git a/components/fpspreadsheet/examples/excel5demo/excel5demo.lpi b/components/fpspreadsheet/examples/excel5demo/excel5demo.lpi
new file mode 100755
index 000000000..0fb956a7e
--- /dev/null
+++ b/components/fpspreadsheet/examples/excel5demo/excel5demo.lpi
@@ -0,0 +1,251 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/fpspreadsheet/examples/excel5demo/excel5demo.lpr b/components/fpspreadsheet/examples/excel5demo/excel5demo.lpr
new file mode 100755
index 000000000..e5edc4452
--- /dev/null
+++ b/components/fpspreadsheet/examples/excel5demo/excel5demo.lpr
@@ -0,0 +1,59 @@
+{
+excel5demo.dpr
+
+Demonstrates how to write an Excel 5.x file using the fpspreadsheet library
+
+AUTHORS: Felipe Monteiro de Carvalho
+}
+program excel5demo;
+
+{$mode delphi}{$H+}
+
+uses
+ Classes, SysUtils, fpspreadsheet, xlsbiff5;
+
+var
+ MyWorkbook: TsWorkbook;
+ MyWorksheet: TsWorksheet;
+ MyFormula: TRPNFormula;
+ MyDir: string;
+begin
+ // Open the output file
+ MyDir := ExtractFilePath(ParamStr(0));
+
+ // Create the spreadsheet
+ MyWorkbook := TsWorkbook.Create;
+ MyWorksheet := MyWorkbook.AddWorksheet('My Worksheet');
+
+ // Write some number cells
+ MyWorksheet.WriteNumber(0, 0, 1.0);
+ MyWorksheet.WriteNumber(0, 1, 2.0);
+ MyWorksheet.WriteNumber(0, 2, 3.0);
+ MyWorksheet.WriteNumber(0, 3, 4.0);
+
+ // Write the formula E1 = A1 + B1
+ // or, in RPN: A1, B1, +
+ SetLength(MyFormula, 3);
+ MyFormula[0].TokenID := INT_EXCEL_TOKEN_TREFV; {A1}
+ MyFormula[0].Col := 0;
+ MyFormula[0].Row := 0;
+ MyFormula[1].TokenID := INT_EXCEL_TOKEN_TREFV; {B1}
+ MyFormula[1].Col := 1;
+ MyFormula[1].Row := 0;
+ MyFormula[2].TokenID := INT_EXCEL_TOKEN_TADD; {+}
+ MyWorksheet.WriteRPNFormula(0, 4, MyFormula);
+
+ // Creates a new worksheet
+ MyWorksheet := MyWorkbook.AddWorksheet('My Worksheet 2');
+
+ // Write some string cells
+ MyWorksheet.WriteAnsiText(0, 0, 'First');
+ MyWorksheet.WriteAnsiText(0, 1, 'Second');
+ MyWorksheet.WriteAnsiText(0, 2, 'Third');
+ MyWorksheet.WriteAnsiText(0, 3, 'Fourth');
+
+ // Save the spreadsheet to a file
+ MyWorkbook.WriteToFile(MyDir + 'test' + STR_EXCEL_EXTENSION, sfExcel5);
+ MyWorkbook.Free;
+end.
+
diff --git a/components/fpspreadsheet/fpolestorage.pas b/components/fpspreadsheet/fpolestorage.pas
new file mode 100755
index 000000000..ce433dffb
--- /dev/null
+++ b/components/fpspreadsheet/fpolestorage.pas
@@ -0,0 +1,78 @@
+{
+fpolestorage.pas
+
+Writes an OLE document
+
+AUTHORS: Felipe Monteiro de Carvalho
+}
+unit fpolestorage;
+
+{$ifdef fpc}
+{$mode delphi}
+{$endif}
+
+interface
+
+uses
+{$ifdef Windows}
+ ActiveX, ComObj,
+{$endif}
+ Classes, SysUtils;
+
+type
+
+ { TOLEStorage }
+
+ TOLEStorage = class
+ private
+{$ifdef Windows}
+ FStorage: IStorage;
+ FStream: IStream;
+{$endif}
+ public
+ constructor Create;
+ destructor Destroy; override;
+ procedure WriteStreamToOLEFile(AFileName: string; AMemStream: TMemoryStream);
+ end;
+
+implementation
+
+{ TOLEStorage }
+
+constructor TOLEStorage.Create;
+begin
+ inherited Create;
+
+end;
+
+destructor TOLEStorage.Destroy;
+begin
+
+ inherited Destroy;
+end;
+
+procedure TOLEStorage.WriteStreamToOLEFile(AFileName: string; AMemStream: TMemoryStream);
+var
+ cbWritten: Cardinal;
+begin
+{$ifdef Windows}
+ { Initialize the Component Object Model (COM) before calling s functions }
+ OleCheck(CoInitialize(nil));
+
+ { Create a Storage Object }
+ OleCheck(StgCreateDocfile(PWideChar(WideString(AFileName)),
+ STGM_READWRITE or STGM_FAILIFTHERE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT,
+ 0, FStorage));
+
+ { Create a workbook stream in the storage. A BIFF5 file must
+ have at least a workbook stream. This stream *must* be named 'Book' }
+ OleCheck(FStorage.CreateStream('Book',
+ STGM_READWRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, 0, FStream));
+
+ { Write all data }
+ FStream.Write(AMemStream.Memory, AMemStream.Size, @cbWritten);
+{$endif}
+end;
+
+end.
+
diff --git a/components/fpspreadsheet/fpsallformats.pas b/components/fpspreadsheet/fpsallformats.pas
new file mode 100755
index 000000000..c0c45505e
--- /dev/null
+++ b/components/fpspreadsheet/fpsallformats.pas
@@ -0,0 +1,18 @@
+{
+fpsallformats.pas
+
+Unit to quickly add all supported fpspreadsheet formats to the project
+
+AUTHORS: Felipe Monteiro de Carvalho
+}
+unit fpsallformats;
+
+interface
+
+uses
+ xlsbiff2, xlsbiff5;
+
+implementation
+
+end.
+
diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas
new file mode 100755
index 000000000..670bc4d01
--- /dev/null
+++ b/components/fpspreadsheet/fpsopendocument.pas
@@ -0,0 +1,407 @@
+{
+fpsopendocument.pas
+
+Writes an OpenDocument 1.0 Spreadsheet document
+
+An OpenDocument document is a compressed ZIP file with the following files inside:
+
+content.xml
+meta.xml
+settings.xml
+styles.xml
+META-INF\manifest.xml
+
+Specifications obtained from:
+
+write url here
+
+AUTHORS: Felipe Monteiro de Carvalho
+
+IMPORTANT: This writer doesn't work yet!!! This is just initial code.
+}
+unit fpsopendocument;
+
+{$ifdef fpc}
+ {$mode delphi}
+{$endif}
+
+interface
+
+uses
+ Classes, SysUtils, {zipper,}
+ fpspreadsheet;
+
+type
+
+ { TsSpreadOpenDocWriter }
+
+ TsSpreadOpenDocWriter = class(TsCustomSpreadWriter)
+ protected
+// FZip: TZipper;
+ FMetaInfManifest: string;
+ FMeta, FSettings, FStyles: string;
+ FContent: string;
+ public
+ { General writing methods }
+ procedure WriteStringToFile(AFileName, AString: string);
+ procedure WriteToFile(AFileName: string; AData: TsWorkbook); override;
+ procedure WriteToStream(AStream: TStream; AData: TsWorkbook); override;
+ { Record writing methods }
+ procedure WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string); override;
+ procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double); override;
+ end;
+
+implementation
+
+const
+ { OpenDocument general XML constants }
+ XML_HEADER = '';
+
+ { OpenDocument Directory structure constants }
+ OOXML_PATH_CONTENT = 'content.xml';
+ OOXML_PATH_META = 'meta.xml';
+ OOXML_PATH_SETTINGS = 'settings.xml';
+ OOXML_PATH_STYLES = 'styles.xml';
+ OPENDOC_PATH_METAINF = 'META-INF\';
+ OPENDOC_PATH_METAINF_MANIFEST = 'META-INF\manifest.xml';
+
+ { OpenDocument schemas constants }
+ SCHEMAS_XMLNS_OFFICE = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0';
+ SCHEMAS_XMLNS_DCTERMS = 'http://purl.org/dc/terms/';
+ SCHEMAS_XMLNS_META = 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0';
+ SCHEMAS_XMLNS = 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties';
+ SCHEMAS_XMLNS_CONFIG = 'urn:oasis:names:tc:opendocument:xmlns:config:1.0';
+ SCHEMAS_XMLNS_OOO = 'http://openoffice.org/2004/office';
+ SCHEMAS_XMLNS_MANIFEST = 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0';
+ SCHEMAS_XMLNS_FO = 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0';
+ SCHEMAS_XMLNS_STYLE = 'urn:oasis:names:tc:opendocument:xmlns:style:1.0';
+ SCHEMAS_XMLNS_SVG = 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0';
+ SCHEMAS_XMLNS_TABLE = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0';
+ SCHEMAS_XMLNS_TEXT = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0';
+ SCHEMAS_XMLNS_V = 'urn:schemas-microsoft-com:vml';
+ SCHEMAS_XMLNS_NUMBER = 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0';
+ SCHEMAS_XMLNS_CHART = 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0';
+ SCHEMAS_XMLNS_DR3D = 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0';
+ SCHEMAS_XMLNS_MATH = 'http://www.w3.org/1998/Math/MathML';
+ SCHEMAS_XMLNS_FORM = 'urn:oasis:names:tc:opendocument:xmlns:form:1.0';
+ SCHEMAS_XMLNS_SCRIPT = 'urn:oasis:names:tc:opendocument:xmlns:script:1.0';
+ SCHEMAS_XMLNS_OOOW = 'http://openoffice.org/2004/writer';
+ SCHEMAS_XMLNS_OOOC = 'http://openoffice.org/2004/calc';
+ SCHEMAS_XMLNS_DOM = 'http://www.w3.org/2001/xml-events';
+ SCHEMAS_XMLNS_XFORMS = 'http://www.w3.org/2002/xforms';
+ SCHEMAS_XMLNS_XSD = 'http://www.w3.org/2001/XMLSchema';
+ SCHEMAS_XMLNS_XSI = 'http://www.w3.org/2001/XMLSchema-instance';
+
+{ TsSpreadOpenDocWriter }
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteStringToFile ()
+*
+* DESCRIPTION: Writes a string to a file. Helper convenience method.
+*
+*******************************************************************}
+procedure TsSpreadOpenDocWriter.WriteStringToFile(AFileName, AString: string);
+var
+ TheStream : TFileStream;
+ S : String;
+begin
+ TheStream := TFileStream.Create(AFileName, fmCreate);
+ S:=AString;
+ TheStream.WriteBuffer(Pointer(S)^,Length(S));
+ TheStream.Free;
+end;
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteToFile ()
+*
+* DESCRIPTION: Writes an OOXML document to the disc
+*
+*******************************************************************}
+procedure TsSpreadOpenDocWriter.WriteToFile(AFileName: string; AData: TsWorkbook);
+var
+ TempDir: string;
+begin
+ {FZip := TZipper.Create;
+ FZip.ZipFiles(AFileName, x);
+ FZip.Free;}
+
+ WriteToStream(nil, AData);
+
+ TempDir := IncludeTrailingBackslash(AFileName);
+
+ { files on the root path }
+
+ ForceDirectories(TempDir);
+
+ WriteStringToFile(TempDir + OOXML_PATH_CONTENT, FContent);
+
+ WriteStringToFile(TempDir + OOXML_PATH_META, FMeta);
+
+ WriteStringToFile(TempDir + OOXML_PATH_SETTINGS, FSettings);
+
+ WriteStringToFile(TempDir + OOXML_PATH_STYLES, FStyles);
+
+ { META-INF directory }
+
+ ForceDirectories(TempDir + OPENDOC_PATH_METAINF);
+
+ WriteStringToFile(TempDir + OPENDOC_PATH_METAINF_MANIFEST, FMetaInfManifest);
+end;
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteToStream ()
+*
+* DESCRIPTION: Writes an Excel 2 file to a stream
+*
+* Excel 2.x files support only one Worksheet per Workbook,
+* so only the first will be written.
+*
+*******************************************************************}
+procedure TsSpreadOpenDocWriter.WriteToStream(AStream: TStream; AData: TsWorkbook);
+begin
+// WriteCellsToStream(AStream, AData.GetFirstWorksheet.FCells);
+
+ FMetaInfManifest :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ '';
+
+ FMeta :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ ' ' + LineEnding +
+ ' FPSpreadsheet Library' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ '';
+
+ FSettings :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' Tabelle1' + LineEnding +
+ ' 100' + LineEnding +
+ ' 100' + LineEnding +
+ ' false' + LineEnding +
+ ' true' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 3' + LineEnding +
+ ' 2' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ '';
+
+ FStyles :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '';
+
+ FContent :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '1' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '2' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '3' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '4' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ 'First' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ 'Second' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ 'Third' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ 'Fourth' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '';
+end;
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteLabel ()
+*
+* DESCRIPTION: Writes an Excel 2 LABEL record
+*
+* Writes a string to the sheet
+*
+*******************************************************************}
+procedure TsSpreadOpenDocWriter.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(AValue[1], L);
+end;
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteNumber ()
+*
+* DESCRIPTION: Writes an Excel 2 NUMBER record
+*
+* Writes a number (64-bit IEE 754 floating point) to the sheet
+*
+*******************************************************************}
+procedure TsSpreadOpenDocWriter.WriteNumber(AStream: TStream; const ARow,
+ ACol: Cardinal; const AValue: double);
+begin
+ { BIFF Record header }
+// AStream.WriteWord(WordToLE(INT_EXCEL_ID_NUMBER));
+// AStream.WriteWord(WordToLE(15));
+
+ { BIFF Record data }
+// AStream.WriteWord(WordToLE(ARow));
+// AStream.WriteWord(WordToLE(ACol));
+
+ { BIFF2 Attributes }
+ AStream.WriteByte($0);
+ AStream.WriteByte($0);
+ AStream.WriteByte($0);
+
+ { IEE 754 floating-point value }
+ AStream.WriteBuffer(AValue, 8);
+end;
+
+{*******************************************************************
+* Initialization section
+*
+* Registers this reader / writer on fpSpreadsheet
+*
+*******************************************************************}
+initialization
+
+ RegisterSpreadFormat(TsCustomSpreadReader, TsSpreadOpenDocWriter, sfOpenDocument);
+
+end.
+
diff --git a/components/fpspreadsheet/fpspreadsheet.chm b/components/fpspreadsheet/fpspreadsheet.chm
new file mode 100755
index 000000000..b78b128f5
Binary files /dev/null and b/components/fpspreadsheet/fpspreadsheet.chm differ
diff --git a/components/fpspreadsheet/fpspreadsheet.dox-express b/components/fpspreadsheet/fpspreadsheet.dox-express
new file mode 100755
index 000000000..23db64a60
--- /dev/null
+++ b/components/fpspreadsheet/fpspreadsheet.dox-express
@@ -0,0 +1,847 @@
+; This is a Doc-O-Matic version 6.1.0.1246 project file.
+; This file is maintained by Doc-O-Matic, do not edit manually.
+
+[AutoTexts]
+Empty=0
+Saved=1
+Text0=This is unknown %SYMBOLNAME%.
+Text1=This is class %SYMBOLNAME%.
+Text10=This is file %SYMBOLNAME%.
+Text11=This is record %SYMBOLNAME%.
+Text12=This is %SYMBOLNAME%.
+Text13=This is nested type %SYMBOLNAME%.
+Text14=This is namespace %SYMBOLNAME%.
+Text2=This is %MEMBERNAME%, a member of class %CLASSNAME%.
+Text3=This is %MEMBERNAME%, a member of class %CLASSNAME%.
+Text4=This is %MEMBERNAME%, a member of class %CLASSNAME%.
+Text5=This is function %SYMBOLNAME%.
+Text6=This is type %SYMBOLNAME%.
+Text7=This is variable %SYMBOLNAME%.
+Text8=This is constant %SYMBOLNAME%.
+Text9=This is macro %SYMBOLNAME%.
+
+[Configurations]
+Count=3
+Current=0
+Name0=HTML
+Name1=HTML Help
+Name2=Help 2
+
+[Configurations\Help 2\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\AdditionalFiles]
+Count=4
+File0=C:\Program Files\Doc-O-Matic 6 Express\graphics\footer-bkg-whitegradient.gif
+File1=C:\Program Files\Doc-O-Matic 6 Express\graphics\header-bkg-whitegradient.gif
+File2=C:\Program Files\Doc-O-Matic 6 Express\graphics\html_fullframegradient.gif
+File3=C:\Program Files\Doc-O-Matic 6 Express\graphics\html_titlebkg.jpg
+
+[Configurations\Help 2\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\General]
+ClearDirectoryBeforeExport=0
+HelpKind=2
+OutputDir=C:\Documents and Settings\mf75rt\My Documents\Doc-O-Matic\output
+TargetBOM=1
+TargetCodepage=utf-8
+
+[Configurations\Help 2\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\General Build Options]
+ShowDocumentation=1
+
+[Configurations\Help 2\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\HTMLHelp]
+CompileFullTextSearchInformation=1
+CreateBinaryIndex=1
+CreateBinaryTOC=0
+CreateCHIFile=0
+CreateHelp2FullTextIndex=1
+DontIncludeFolders=0
+Help2AdditionalXMLData=
+Help2Collection=
+Help2Compiler=
+Help2CompileResult=0
+Help2Dexplore=
+Help2GenericInfoType=kbRef
+Help2ID=
+Help2Locale=kbEnglish
+Help2Namespace=
+Help2PluginNamespace=0
+Help2PluginNamespaceName=MS.VSIPCC+
+Help2RegisterType=0
+Help2UnregisterNamespace=1
+HelpCompiler=
+HTMLHelp2Filename=
+HTMLHelpDisplayFontScaleButton=0
+HTMLHelpFilename=
+SaveUserPosition=1
+SupportEnhancedDecompilation=0
+WindowPosition_Bottom=0
+WindowPosition_Left=0
+WindowPosition_Right=0
+WindowPosition_Top=0
+
+[Configurations\Help 2\Image Paths]
+Count=2
+Path0=C:\Program Files\Doc-O-Matic 6 Express\graphics
+Path1=C:\Program Files\Doc-O-Matic 6 Express\graphics\en
+
+[Configurations\Help 2\Output Options]
+ClassHierarchyLayoutOutputDir=C:\Documents and Settings\mf75rt\My Documents\Doc-O-Matic\graphics
+
+[Configurations\Help 2\OutputFormat]
+ID={D3A588E0-9472-11D3-BDD1-0080C8BA053D}
+
+[Configurations\HTML Help\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\AdditionalFiles]
+Count=4
+File0=C:\Program Files\Doc-O-Matic 6 Express\graphics\footer-bkg-whitegradient.gif
+File1=C:\Program Files\Doc-O-Matic 6 Express\graphics\header-bkg-whitegradient.gif
+File2=C:\Program Files\Doc-O-Matic 6 Express\graphics\html_fullframegradient.gif
+File3=C:\Program Files\Doc-O-Matic 6 Express\graphics\html_titlebkg.jpg
+
+[Configurations\HTML Help\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\General]
+ClearDirectoryBeforeExport=0
+HelpKind=1
+OutputDir=C:\Documents and Settings\mf75rt\My Documents\Doc-O-Matic\output
+TargetBOM=0
+TargetCodepage=windows-1252
+
+[Configurations\HTML Help\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\General Build Options]
+ShowDocumentation=1
+
+[Configurations\HTML Help\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\HTMLHelp]
+CompileFullTextSearchInformation=1
+CreateBinaryIndex=1
+CreateBinaryTOC=1
+CreateCHIFile=0
+CreateHelp2FullTextIndex=1
+DontIncludeFolders=0
+Help2AdditionalXMLData=
+Help2Collection=
+Help2Compiler=
+Help2CompileResult=0
+Help2Dexplore=
+Help2GenericInfoType=kbRef
+Help2ID=
+Help2Locale=kbEnglish
+Help2Namespace=
+Help2PluginNamespace=0
+Help2PluginNamespaceName=MS.VSIPCC+
+Help2RegisterType=0
+Help2UnregisterNamespace=1
+HelpCompiler=
+HTMLHelp2Filename=
+HTMLHelpDisplayFontScaleButton=0
+HTMLHelpFilename=
+SaveUserPosition=1
+SupportEnhancedDecompilation=0
+WindowPosition_Bottom=0
+WindowPosition_Left=0
+WindowPosition_Right=0
+WindowPosition_Top=0
+
+[Configurations\HTML Help\Image Paths]
+Count=2
+Path0=C:\Program Files\Doc-O-Matic 6 Express\graphics
+Path1=C:\Program Files\Doc-O-Matic 6 Express\graphics\en
+
+[Configurations\HTML Help\Output Options]
+ClassHierarchyLayoutOutputDir=C:\Documents and Settings\mf75rt\My Documents\Doc-O-Matic\graphics
+
+[Configurations\HTML Help\OutputFormat]
+ID={D3A588E0-9472-11D3-BDD1-0080C8BA053D}
+
+[Configurations\HTML\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\AdditionalFiles]
+Count=4
+File0=C:\Program Files\Doc-O-Matic 6 Express\graphics\footer-bkg-whitegradient.gif
+File1=C:\Program Files\Doc-O-Matic 6 Express\graphics\header-bkg-whitegradient.gif
+File2=C:\Program Files\Doc-O-Matic 6 Express\graphics\html_fullframegradient.gif
+File3=C:\Program Files\Doc-O-Matic 6 Express\graphics\html_titlebkg.jpg
+
+[Configurations\HTML\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\General]
+ClearDirectoryBeforeExport=0
+HelpKind=0
+OutputDir=C:\Documents and Settings\mf75rt\My Documents\Doc-O-Matic\output
+TargetBOM=0
+TargetCodepage=utf-8
+
+[Configurations\HTML\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\General Build Options]
+ShowDocumentation=1
+
+[Configurations\HTML\{D3A588E0-9472-11D3-BDD1-0080C8BA053D}\HTMLHelp]
+CompileFullTextSearchInformation=1
+CreateBinaryIndex=1
+CreateBinaryTOC=0
+CreateCHIFile=0
+CreateHelp2FullTextIndex=1
+DontIncludeFolders=0
+Help2AdditionalXMLData=
+Help2Collection=
+Help2Compiler=
+Help2CompileResult=0
+Help2Dexplore=
+Help2GenericInfoType=kbRef
+Help2ID=
+Help2Locale=kbEnglish
+Help2Namespace=
+Help2PluginNamespace=0
+Help2PluginNamespaceName=MS.VSIPCC+
+Help2RegisterType=0
+Help2UnregisterNamespace=1
+HelpCompiler=
+HTMLHelp2Filename=
+HTMLHelpDisplayFontScaleButton=0
+HTMLHelpFilename=
+SaveUserPosition=1
+SupportEnhancedDecompilation=0
+WindowPosition_Bottom=0
+WindowPosition_Left=0
+WindowPosition_Right=0
+WindowPosition_Top=0
+
+[Configurations\HTML\Image Paths]
+Count=2
+Path0=C:\Program Files\Doc-O-Matic 6 Express\graphics
+Path1=C:\Program Files\Doc-O-Matic 6 Express\graphics\en
+
+[Configurations\HTML\Output Options]
+ClassHierarchyLayoutOutputDir=C:\Documents and Settings\mf75rt\My Documents\Doc-O-Matic\graphics
+
+[Configurations\HTML\OutputFormat]
+ID={D3A588E0-9472-11D3-BDD1-0080C8BA053D}
+
+[General]
+Author=Felipe Monteiro de Carvalho
+AuthorEmail=
+Copyright=Copyright (c) 2008
+Summary=Free Pascal Spreadsheet Library
+Title=FPSpreadsheet
+VersionBuild=0
+VersionMajor=1
+VersionMinor=0
+VersionRelease=0
+
+[Macro Header Files]
+Count=0
+
+[Parsing]
+AssignedCommentsToFollowingSymbols=1
+CommentDistance=0
+DocUseTripleSlashOnly=0
+HeaderUnderlineCharactersCenter==
+HeaderUnderlineCharactersJustify=#
+HeaderUnderlineCharactersLeft=-
+HeaderUnderlineCharactersRight=~
+HeadlineDelimiterChars=*#-
+IgnoredTrimLineCount=1
+IgnoredTrimLine_0=__________________________________________________
+InitOptional=0
+InitSequence=@@
+ListBulletChars=*+-#
+NamespaceOption=2
+ParameterDelimiterChars=:-
+ParameterDescriptionMode=0
+ParametersAllowSpaceAsDelimiter=0
+SectionAllowSpaceAsDelimiter=0
+SectionDelimiterChars=:*-
+SupportJavaDoc=1
+SupportJavaDocBackslashTags=0
+SupportXMLDoc=0
+TabExpandCount=4
+TrailerChars=-+~/#
+WallCharacters="#$%&'*+-/=@[\]^_`{|}~
+
+[Project File Info]
+Version=601
+
+[Section\0]
+Count=3
+Description=Contains a short summary of the purpose of an object. This text usually contains one or two sentenses.
+DisplayName=Summary
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=4
+Value0=Summary
+Value1=Brief
+Value2=Short
+XMLDocTags=summary
+
+[Section\1]
+Count=2
+Description=Contains the general description of an object. This text describes the purpose of the item.
+DisplayName=Description
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=Description
+Value1=Long
+XMLDocTags=value
+
+[Section\10]
+Count=3
+Description=Contains text that describes bugs or problems related to the object.
+DisplayName=Bugs
+EditorName=
+JavaDocTags=bug
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=Bugs
+Value1=Known Bugs
+Value2=Current Bugs
+XMLDocTags=
+
+[Section\11]
+Count=2
+Description=Contains documentation parts which should only be available to internal team members and not to the general public.
+DisplayName=Internal
+EditorName=
+JavaDocTags=internal
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=Internal
+Value1=Special Info
+XMLDocTags=
+
+[Section\12]
+Count=1
+Description=Contains text that describes which parts of the object are incomplete and need additional work.
+DisplayName=Todo
+EditorName=
+JavaDocTags=todo
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=TODO
+XMLDocTags=
+
+[Section\13]
+Count=3
+Description=Contains text that describes which exceptions can be raised by a function or method.
+DisplayName=Exceptions
+EditorName=
+JavaDocTags=exception,exceptions,throws,raises
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=12
+Value0=Exceptions
+Value1=Throws
+Value2=Raises
+XMLDocTags=exception
+
+[Section\14]
+Count=2
+Description=Contains text that describes conditions (for example pre- and postconditions) for a function call.
+DisplayName=Conditions
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=Conditions
+Value1=Preconditions
+XMLDocTags=permission
+
+[Section\15]
+Count=2
+Description=Contains information who the author(s) of a topic are
+DisplayName=Author
+EditorName=
+JavaDocTags=author
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=Author
+Value1=Authors
+XMLDocTags=
+
+[Section\16]
+Count=1
+Description=Contains text which describes the history of the object
+DisplayName=History
+EditorName=
+JavaDocTags=since,history
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=History
+XMLDocTags=
+
+[Section\17]
+Count=2
+Description=Contains version information of the object.
+DisplayName=Version
+EditorName=
+JavaDocTags=version
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=Version
+Value1=Current Version
+XMLDocTags=
+
+[Section\18]
+Count=1
+Description=Contains the text for the glossary entry of the topic.
+DisplayName=Glossary
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=7
+Value0=Glossary
+XMLDocTags=
+
+[Section\19]
+Count=3
+Description=Contains the text for the description of the implementation source of functions.
+DisplayName=Source Description
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=9
+Value0=Source Description
+Value1=Implementation Notes
+Value2=Implementation Description
+XMLDocTags=
+
+[Section\2]
+Count=1
+Description=Used for one or more short notes on an object.
+DisplayName=Notes
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=Note
+XMLDocTags=
+
+[Section\20]
+Count=4
+Description=Like a standard section with the additional possibility to give it an individual name in each topic it is used.
+DisplayName=Link List
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=10
+Value0=Link List
+Value1=Membergroups
+Value2=Member Groups
+Value3=Class Group
+XMLDocTags=
+
+[Section\21]
+Count=1
+Description=Contains the Pascal declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=Pascal
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=21
+Value0=Pascal Syntax
+XMLDocTags=
+
+[Section\22]
+Count=1
+Description=Contains the C++ declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=C++
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=22
+Value0=C++ Syntax
+XMLDocTags=
+
+[Section\23]
+Count=1
+Description=Contains the C# declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=C#
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=23
+Value0=C# Syntax
+XMLDocTags=
+
+[Section\24]
+Count=1
+Description=Contains the Visual Basic declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=Visual Basic
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=24
+Value0=Visual Basic Syntax
+XMLDocTags=
+
+[Section\25]
+Count=1
+Description=Contains the Java declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=Java
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=25
+Value0=Java Syntax
+XMLDocTags=
+
+[Section\26]
+Count=1
+Description=Contains the IDL declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=IDL
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=26
+Value0=IDL Syntax
+XMLDocTags=
+
+[Section\27]
+Count=1
+Description=Contains the JavaScript declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=JavaScript
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=27
+Value0=JavaScript Syntax
+XMLDocTags=
+
+[Section\28]
+Count=1
+Description=Contains the MATLAB declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=MATLAB
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=29
+Value0=MATLAB Syntax
+XMLDocTags=
+
+[Section\29]
+Count=1
+Description=Contains the PHP declaration syntax. It will be generated automatically if it does not exist in a topic.
+DisplayName=PHP
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=9
+Type=30
+Value0=PHP Syntax
+XMLDocTags=
+
+[Section\3]
+Count=1
+Description=Used for special remarks on a topic. Contrary to Notes, this text can be fairly long.
+DisplayName=Remarks
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=0
+Value0=Remarks
+XMLDocTags=remarks
+
+[Section\30]
+Count=0
+Description=Automatically filled section that contains a table of members of structs, records, enumerations and unions along with their descriptions.
+DisplayName=Members
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=13
+XMLDocTags=
+
+[Section\31]
+Count=0
+Description=Automatically filled section that contains navigation listings for class and namespace members and sub-topics.
+DisplayName=Navigation
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=14
+XMLDocTags=
+
+[Section\32]
+Count=0
+Description=Automatically filled section that contains the body source code for functions and files.
+DisplayName=Body Source
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=16
+XMLDocTags=
+
+[Section\33]
+Count=0
+Description=Automatically filled section that contains the local class hierarchy for a single class.
+DisplayName=Class Hierarchy
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=17
+XMLDocTags=
+
+[Section\34]
+Count=0
+Description=Automatically filled section that contains a link to the file in which a symbol is declared.
+DisplayName=File
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=18
+XMLDocTags=
+
+[Section\35]
+Count=0
+Description=Automatically filled section that contains a list of links to pages with more information about the topic that are maintained by Doc-O-Matic
+DisplayName=Links
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=19
+XMLDocTags=
+
+[Section\36]
+Count=0
+Description=Automatically filled section that contains the reports assigned to a topic.
+DisplayName=Reports
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=20
+XMLDocTags=
+
+[Section\37]
+Count=0
+Description=Automatically filled section that contains an overview for overloaded member functions.
+DisplayName=Overload List
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=28
+XMLDocTags=
+
+[Section\38]
+Count=1
+Description=Contains multiple glossary entries of the topic.
+DisplayName=Multi Item Glossary
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=12
+Value0=Multi Item Glossary
+XMLDocTags=
+
+[Section\39]
+Count=1
+Description=Contains a parameter descriptions for generic types.
+DisplayName=Type Parameters
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=1
+Value0=Type Parameters
+XMLDocTags=typeparam
+
+[Section\4]
+Count=4
+Description=Used for detailed descriptions of each parameter of a global or member function.
+DisplayName=Parameters
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=1
+Value0=Parameters
+Value1=Arguments
+Value2=Inputs
+Value3=Input
+XMLDocTags=
+
+[Section\5]
+Count=4
+Description=Used to generally describe the return value of a global or member function.
+DisplayName=Returns
+EditorName=
+JavaDocTags=return,returns
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=2
+Value0=Returns
+Value1=Return Value
+Value2=Result
+Value3=Output
+XMLDocTags=returns
+
+[Section\6]
+Count=2
+Description=Used to describe all possible return values in detail in form of a value-description list.
+DisplayName=Return Values
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=11
+Value0=Return Value List
+Value1=Return Value Details
+XMLDocTags=
+
+[Section\7]
+Count=3
+Description=Used for one or more usage examples of the object being described.
+DisplayName=Example
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=3
+Value0=Example
+Value1=Examples
+Value2=Sample
+XMLDocTags=example
+
+[Section\8]
+Count=8
+Description=All text that appears in this section is ignored.
+DisplayName=Ignore
+EditorName=
+JavaDocTags=beaninfo,component,deprecated,event,exclude,index,obsolete,seealso,serial,serialdata,serialfield,tutorial
+OutputFind=
+OutputReplace=
+SectionFlags=8
+Type=6
+Value0=Ignore Text
+Value1=$Log
+Value2=$Filename
+Value3=$Revision
+Value4=$Date
+Value5=$Author
+Value6=$History
+Value7=$Id
+XMLDocTags=
+
+[Section\9]
+Count=2
+Description=Contains a comma separated list of topic IDs which build the see also list of the topic.
+DisplayName=See Also
+EditorName=
+JavaDocTags=
+OutputFind=
+OutputReplace=
+SectionFlags=15
+Type=5
+Value0=See Also
+Value1=Seealso
+XMLDocTags=
+
+[Sections]
+Count=40
+DefID=1
+ID0=0
+ID1=1
+ID10=10
+ID11=11
+ID12=12
+ID13=13
+ID14=14
+ID15=15
+ID16=16
+ID17=17
+ID18=18
+ID19=19
+ID2=2
+ID20=20
+ID21=21
+ID22=22
+ID23=23
+ID24=24
+ID25=25
+ID26=26
+ID27=27
+ID28=28
+ID29=29
+ID3=3
+ID30=30
+ID31=31
+ID32=32
+ID33=33
+ID34=34
+ID35=35
+ID36=36
+ID37=37
+ID38=38
+ID39=39
+ID4=4
+ID5=5
+ID6=6
+ID7=7
+ID8=8
+ID9=9
+SavedValues=1
+
+[Source Files]
+Count=1
+File0=fpspreadsheet.pas
+
+[Source Include Directories]
+Count=0
+
diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas
new file mode 100755
index 000000000..33378bab5
--- /dev/null
+++ b/components/fpspreadsheet/fpspreadsheet.pas
@@ -0,0 +1,588 @@
+{
+fpspreadsheet.pas
+
+Writes an spreadsheet document
+
+AUTHORS: Felipe Monteiro de Carvalho
+}
+unit fpspreadsheet;
+
+{$ifdef fpc}
+ {$mode delphi}
+{$endif}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ TsSpreadsheetFormat = (sfExcel2, sfExcel3, sfExcel4, sfExcel5, sfExcel8,
+ sfOOXML, sfOpenDocument, sfCSV);
+
+const
+ { Default extensions }
+ STR_EXCEL_EXTENSION = '.xls';
+
+const
+ { TokenID values }
+
+ { Binary Operator Tokens }
+ INT_EXCEL_TOKEN_TADD = $03;
+ INT_EXCEL_TOKEN_TSUB = $04;
+ INT_EXCEL_TOKEN_TMUL = $05;
+ INT_EXCEL_TOKEN_TDIV = $06;
+ INT_EXCEL_TOKEN_TPOWER = $07;
+
+ { Constant Operand Tokens }
+ INT_EXCEL_TOKEN_TNUM = $1F;
+
+ { Operand Tokens }
+ INT_EXCEL_TOKEN_TREFR = $24;
+ INT_EXCEL_TOKEN_TREFV = $44;
+ INT_EXCEL_TOKEN_TREFA = $64;
+
+type
+
+ {@@ A Token of a RPN Token array for formulas }
+
+ TRPNToken = record
+ TokenID: Byte;
+ Col: Byte;
+ Row: Word;
+ DoubleValue: double;
+ end;
+
+ {@@ RPN Token array for formulas }
+
+ TRPNFormula = array of TRPNToken;
+
+ {@@ Describes the type of content of a cell on a TsWorksheet }
+
+ TCellContentType = (cctFormula, cctNumber, cctString, cctWideString);
+
+ {@@ Cell structure for TsWorksheet }
+
+ TCell = record
+ Row, Col: Cardinal;
+ ContentType: TCellContentType;
+ FormulaValue: TRPNFormula;
+ NumberValue: double;
+ StringValue: string;
+ WideStringValue: widestring;
+ end;
+
+ PCell = ^TCell;
+
+type
+
+ TsCustomSpreadWriter = class;
+
+ {@@ TsWorksheet }
+
+ TsWorksheet = class
+ private
+ procedure RemoveCallback(data, arg: pointer);
+ public
+ FCells: TFPList;
+ Name: string;
+ { Base methods }
+ constructor Create;
+ destructor Destroy; override;
+ { Data manipulation methods }
+ function FindCell(ARow, ACol: Cardinal): PCell;
+ function GetCell(ARow, ACol: Cardinal): PCell;
+ procedure RemoveAllCells;
+ procedure WriteAnsiText(ARow, ACol: Cardinal; AText: ansistring);
+ procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double);
+ procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TRPNFormula);
+ end;
+
+ {@@ TsWorkbook }
+
+ TsWorkbook = class
+ private
+ FWorksheets: TFPList;
+ procedure RemoveCallback(data, arg: pointer);
+ public
+ { Base methods }
+ constructor Create;
+ destructor Destroy; override;
+ function CreateSpreadWriter(AFormat: TsSpreadsheetFormat): TsCustomSpreadWriter;
+ procedure WriteToFile(AFileName: string; AFormat: TsSpreadsheetFormat);
+ procedure WriteToStream(AStream: TStream; AFormat: TsSpreadsheetFormat);
+ { Worksheet list handling methods }
+ function AddWorksheet(AName: string): TsWorksheet;
+ function GetFirstWorksheet: TsWorksheet;
+ function GetWorksheetByIndex(AIndex: Cardinal): TsWorksheet;
+ function GetWorksheetCount: Cardinal;
+ procedure RemoveAllWorksheets;
+ end;
+
+ {@@ TsSpreadReader class reference type }
+
+ TsSpreadReaderClass = class of TsCustomSpreadReader;
+
+ {@@ TsCustomSpreadReader }
+
+ TsCustomSpreadReader = class
+ protected
+ FWorkbook: TsWorkbook;
+ FCurrentWorksheet: TsWorksheet;
+ public
+ { General writing methods }
+ procedure ReadFromFile(AFileName: string; AData: TsWorkbook); virtual;
+ procedure ReadFromStream(AStream: TStream; AData: TsWorkbook); virtual; abstract;
+ { Record reading methods }
+ procedure ReadFormula(AStream: TStream); virtual; abstract;
+ procedure ReadLabel(AStream: TStream); virtual; abstract;
+ procedure ReadNumber(AStream: TStream); virtual; abstract;
+ end;
+
+ {@@ TsSpreadWriter class reference type }
+
+ TsSpreadWriterClass = class of TsCustomSpreadWriter;
+
+ {@@ TsCustomSpreadWriter }
+
+ TsCustomSpreadWriter = class
+ public
+ { General writing methods }
+ procedure WriteCellCallback(data, arg: pointer);
+ procedure WriteCellsToStream(AStream: TStream; ACells: TFPList);
+ procedure WriteToFile(AFileName: string; AData: TsWorkbook); virtual;
+ procedure WriteToStream(AStream: TStream; AData: TsWorkbook); virtual; abstract;
+ { Record writing methods }
+ procedure WriteFormula(AStream: TStream; const ARow, ACol: Word; const AFormula: TRPNFormula); virtual; abstract;
+ procedure WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string); virtual; abstract;
+ procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double); virtual; abstract;
+ end;
+
+ {@@ List of registered formats }
+
+ TsSpreadFormatData = record
+ ReaderClass: TsSpreadReaderClass;
+ WriterClass: TsSpreadWriterClass;
+ Format: TsSpreadsheetFormat;
+ end;
+
+var
+ GsSpreadFormats: array of TsSpreadFormatData;
+
+procedure RegisterSpreadFormat(
+ AReaderClass: TsSpreadReaderClass;
+ AWriterClass: TsSpreadWriterClass;
+ AFormat: TsSpreadsheetFormat);
+
+implementation
+
+{@@
+ Registers a new reader/writer pair for a format
+}
+procedure RegisterSpreadFormat(
+ AReaderClass: TsSpreadReaderClass;
+ AWriterClass: TsSpreadWriterClass;
+ AFormat: TsSpreadsheetFormat);
+var
+ len: Integer;
+begin
+ len := Length(GsSpreadFormats);
+ SetLength(GsSpreadFormats, len + 1);
+
+ GsSpreadFormats[len].ReaderClass := AReaderClass;
+ GsSpreadFormats[len].WriterClass := AWriterClass;
+ GsSpreadFormats[len].Format := AFormat;
+end;
+
+{ TsWorksheet }
+
+{@@
+ Helper method for clearing the records in a spreadsheet.
+}
+procedure TsWorksheet.RemoveCallback(data, arg: pointer);
+begin
+ FreeMem(data);
+end;
+
+{@@
+ Constructor.
+}
+constructor TsWorksheet.Create;
+begin
+ inherited Create;
+
+ FCells := TFPList.Create;
+end;
+
+{@@
+ Destructor.
+}
+destructor TsWorksheet.Destroy;
+begin
+ RemoveAllCells;
+
+ FCells.Free;
+
+ inherited Destroy;
+end;
+
+{@@
+ Tryes to locate a Cell in the list of already
+ written Cells
+
+ @param ARow The row of the cell
+ @param ACol The column of the cell
+
+ @return Nil if no existing cell was found,
+ otherwise a pointer to the desired Cell
+
+ @see TCell
+}
+function TsWorksheet.FindCell(ARow, ACol: Cardinal): PCell;
+var
+ i: Integer;
+ ACell: PCell;
+begin
+ i := 0;
+ Result := nil;
+
+ while (i < FCells.Count) do
+ begin
+ ACell := PCell(FCells.Items[i]);
+
+ if (ACell^.Row = ARow) and (ACell^.Col = ACol) then
+ begin
+ Result := ACell;
+ Exit;
+ end;
+
+ Inc(i);
+ end;
+end;
+
+{@@
+ Obtains an allocated cell at the desired location.
+
+ If the Cell already exists, a pointer to it will
+ be returned.
+
+ If not, then new memory for the cell will be allocated,
+ a pointer to it will be returned and it will be added
+ to the list of Cells.
+
+ @param ARow The row of the cell
+ @param ACol The column of the cell
+
+ @return A pointer to the Cell on the desired location.
+
+ @see TCell
+}
+function TsWorksheet.GetCell(ARow, ACol: Cardinal): PCell;
+begin
+ Result := FindCell(ARow, ACol);
+
+ if (Result = nil) then
+ begin
+ Result := GetMem(SizeOf(TCell));
+ FillChar(Result^, SizeOf(TCell), #0);
+
+ Result^.Row := ARow;
+ Result^.Col := ACol;
+
+ FCells.Add(Result);
+ end;
+end;
+
+{@@
+ Clears the list of Cells and releases their memory.
+}
+procedure TsWorksheet.RemoveAllCells;
+begin
+ FCells.ForEachCall(RemoveCallback, nil);
+end;
+
+{@@
+ Writes ansi text to a determined cell.
+
+ The text must be encoded on the system default encoding.
+
+ On formats the support unicode the text will be converted to the unicode
+ encoding that the format supports.
+
+ @param ARow The row of the cell
+ @param ACol The column of the cell
+ @param AText The text to be written encoded with the system encoding
+}
+procedure TsWorksheet.WriteAnsiText(ARow, ACol: Cardinal; AText: ansistring);
+var
+ ACell: PCell;
+begin
+ ACell := GetCell(ARow, ACol);
+
+ ACell^.ContentType := cctString;
+ ACell^.StringValue := AText;
+end;
+
+{@@
+ Writes a floating-point number to a determined cell
+
+ @param ARow The row of the cell
+ @param ACol The column of the cell
+ @param ANumber The number to be written
+}
+procedure TsWorksheet.WriteNumber(ARow, ACol: Cardinal; ANumber: double);
+var
+ ACell: PCell;
+begin
+ ACell := GetCell(ARow, ACol);
+
+ ACell^.ContentType := cctNumber;
+ ACell^.NumberValue := ANumber;
+end;
+
+{@@
+ Writes a formula to a determined cell
+
+ @param ARow The row of the cell
+ @param ACol The column of the cell
+ @param AFormula The formula in RPN array format
+}
+procedure TsWorksheet.WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TRPNFormula);
+var
+ ACell: PCell;
+begin
+ ACell := GetCell(ARow, ACol);
+
+ ACell^.ContentType := cctFormula;
+ ACell^.FormulaValue := AFormula;
+end;
+
+{ TsWorkbook }
+
+{@@
+ Helper method for clearing the spreadsheet list.
+}
+procedure TsWorkbook.RemoveCallback(data, arg: pointer);
+begin
+ TsWorksheet(data).Free;
+end;
+
+{@@
+ Constructor.
+}
+constructor TsWorkbook.Create;
+begin
+ inherited Create;
+
+ FWorksheets := TFPList.Create;
+end;
+
+{@@
+ Destructor.
+}
+destructor TsWorkbook.Destroy;
+begin
+ RemoveAllWorksheets;
+
+ FWorksheets.Free;
+
+ inherited Destroy;
+end;
+
+{@@
+ Convenience method which creates the correct
+ writer object for a given spreadsheet format.
+}
+function TsWorkbook.CreateSpreadWriter(AFormat: TsSpreadsheetFormat): TsCustomSpreadWriter;
+var
+ i: Integer;
+begin
+ Result := nil;
+
+ for i := 0 to Length(GsSpreadFormats) - 1 do
+ if GsSpreadFormats[i].Format = AFormat then
+ begin
+ Result := GsSpreadFormats[i].WriterClass.Create;
+
+ Break;
+ end;
+
+ if Result = nil then raise Exception.Create('Unsuported spreadsheet format.');
+end;
+
+{@@
+ Writes the document to a file.
+
+ If the file doesn't exist, it will be created.
+}
+procedure TsWorkbook.WriteToFile(AFileName: string; AFormat: TsSpreadsheetFormat);
+var
+ AWriter: TsCustomSpreadWriter;
+begin
+ AWriter := CreateSpreadWriter(AFormat);
+
+ try
+ AWriter.WriteToFile(AFileName, Self);
+ finally
+ AWriter.Free;
+ end;
+end;
+
+{@@
+ Writes the document to a stream
+}
+procedure TsWorkbook.WriteToStream(AStream: TStream; AFormat: TsSpreadsheetFormat);
+var
+ AWriter: TsCustomSpreadWriter;
+begin
+ AWriter := CreateSpreadWriter(AFormat);
+
+ try
+ AWriter.WriteToStream(AStream, Self);
+ finally
+ AWriter.Free;
+ end;
+end;
+
+{@@
+ Adds a new worksheet to the workbook
+
+ It is added to the end of the list of worksheets
+
+ @param AName The name of the new worksheet
+
+ @return The instace of the newly created worksheet
+
+ @see TsWorkbook
+}
+function TsWorkbook.AddWorksheet(AName: string): TsWorksheet;
+begin
+ Result := TsWorksheet.Create;
+
+ Result.Name := AName;
+
+ FWorksheets.Add(Pointer(Result));
+end;
+
+{@@
+ Quick helper routine which returns the first worksheet
+
+ @return A TsWorksheet instance if at least one is present.
+ nil otherwise.
+
+ @see TsWorkbook.GetWorksheetByIndex
+ @see TsWorksheet
+}
+function TsWorkbook.GetFirstWorksheet: TsWorksheet;
+begin
+ Result := TsWorksheet(FWorksheets.First);
+end;
+
+{@@
+ Gets the worksheet with a given index
+
+ The index is zero-based, so the first worksheet
+ added has index 0, the second 1, etc.
+
+ @param AIndex The index of the worksheet (0-based)
+
+ @return A TsWorksheet instance if one is present at that index.
+ nil otherwise.
+
+ @see TsWorkbook.GetFirstWorksheet
+ @see TsWorksheet
+}
+function TsWorkbook.GetWorksheetByIndex(AIndex: Cardinal): TsWorksheet;
+begin
+ Result := TsWorksheet(FWorksheets.Items[AIndex]);
+end;
+
+{@@
+ The number of worksheets on the workbook
+
+ @see TsWorksheet
+}
+function TsWorkbook.GetWorksheetCount: Cardinal;
+begin
+ Result := FWorksheets.Count;
+end;
+
+{@@
+ Clears the list of Worksheets and releases their memory.
+}
+procedure TsWorkbook.RemoveAllWorksheets;
+begin
+ FWorksheets.ForEachCall(RemoveCallback, nil);
+end;
+
+{ TsCustomSpreadReader }
+
+procedure TsCustomSpreadReader.ReadFromFile(AFileName: string; AData: TsWorkbook);
+begin
+
+end;
+
+{ TsCustomSpreadWriter }
+
+{@@
+ Helper function for the spreadsheet writers.
+
+ @see TsCustomSpreadWriter.WriteCellsToStream
+}
+procedure TsCustomSpreadWriter.WriteCellCallback(data, arg: pointer);
+var
+ ACell: PCell;
+ AStream: TStream;
+begin
+ ACell := PCell(data);
+ AStream := TStream(arg);
+
+ case ACell.ContentType of
+ cctFormula: WriteFormula(AStream, ACell^.Row, ACell^.Col, ACell^.FormulaValue);
+ cctNumber: WriteNumber(AStream, ACell^.Row, ACell^.Col, ACell^.NumberValue);
+ cctString: WriteLabel(AStream, ACell^.Row, ACell^.Col, ACell^.StringValue);
+ end;
+end;
+
+{@@
+ Helper function for the spreadsheet writers.
+
+ Iterates all cells on a list, calling the appropriate write method for them.
+
+ @param AStream The output stream.
+ @param ACells List of cells to be writeen
+}
+procedure TsCustomSpreadWriter.WriteCellsToStream(AStream: TStream; ACells: TFPList);
+begin
+ ACells.ForEachCall(WriteCellCallback, Pointer(AStream));
+end;
+
+{@@
+ Default file writting method.
+
+ Opens the file and calls WriteToStream
+
+ @param AFileName The output file name.
+ If the file already exists it will be replaced.
+ @param AData The Workbook to be saved.
+
+ @see TsWorkbook
+}
+procedure TsCustomSpreadWriter.WriteToFile(AFileName: string; AData: TsWorkbook);
+var
+ OutputFile: TFileStream;
+begin
+ OutputFile := TFileStream.Create(AFileName, fmCreate or fmOpenWrite);
+ try
+ WriteToStream(OutputFile, AData);
+ finally
+ OutputFile.Free;
+ end;
+end;
+
+finalization
+
+ SetLength(GsSpreadFormats, 0);
+
+end.
+
diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas
new file mode 100755
index 000000000..c158577d6
--- /dev/null
+++ b/components/fpspreadsheet/xlsbiff2.pas
@@ -0,0 +1,305 @@
+{
+xlsbiff2.pas
+
+Writes an Excel 2.x file
+
+Excel 2.x files support only one Worksheet per Workbook, so only the first
+will be written.
+
+An Excel file 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 file format specification obtained from:
+
+http://sc.openoffice.org/excelfileformat.pdf
+
+AUTHORS: Felipe Monteiro de Carvalho
+}
+unit xlsbiff2;
+
+{$ifdef fpc}
+ {$mode delphi}
+{$endif}
+
+interface
+
+uses
+ Classes, SysUtils,
+ fpspreadsheet;
+
+type
+
+ { TsSpreadBIFF2Writer }
+
+ TsSpreadBIFF2Writer = 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 WriteFormula(AStream: TStream; const ARow, ACol: Word; const AFormula: TRPNFormula); 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;
+ end;
+
+implementation
+
+const
+ { Excel record IDs }
+ INT_EXCEL_ID_NUMBER = $0003;
+ INT_EXCEL_ID_LABEL = $0004;
+ INT_EXCEL_ID_FORMULA = $0006;
+ INT_EXCEL_ID_BOF = $0009;
+ INT_EXCEL_ID_EOF = $000A;
+
+ { Cell Addresses constants }
+ MASK_EXCEL_ROW = $3FFF;
+ MASK_EXCEL_RELATIVE_ROW = $4000;
+ MASK_EXCEL_RELATIVE_COL = $8000;
+
+ { BOF record constants }
+ INT_EXCEL_SHEET = $0010;
+ INT_EXCEL_CHART = $0020;
+ INT_EXCEL_MACRO_SHEET = $0040;
+
+{
+ Endianess helper functions
+
+ Excel files are all written with Little Endian byte order,
+ so it's necessary to swap the data to be able to build a
+ correct file on big endian systems.
+}
+
+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;
+
+{ TsSpreadBIFF2Writer }
+
+{*******************************************************************
+* TsSpreadBIFF2Writer.WriteToStream ()
+*
+* DESCRIPTION: Writes an Excel 2 file to a stream
+*
+* Excel 2.x files support only one Worksheet per Workbook,
+* so only the first will be written.
+*
+*******************************************************************}
+procedure TsSpreadBIFF2Writer.WriteToStream(AStream: TStream; AData: TsWorkbook);
+begin
+ WriteBOF(AStream);
+
+ WriteCellsToStream(AStream, AData.GetFirstWorksheet.FCells);
+
+ WriteEOF(AStream);
+end;
+
+{*******************************************************************
+* TsSpreadBIFF2Writer.WriteBOF ()
+*
+* DESCRIPTION: Writes an Excel 2 BOF record
+*
+* This must be the first record on an Excel 2 stream
+*
+*******************************************************************}
+procedure TsSpreadBIFF2Writer.WriteBOF(AStream: TStream);
+begin
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOF));
+ AStream.WriteWord(WordToLE($0004));
+
+ { Unused }
+ AStream.WriteWord($0000);
+
+ { Data type }
+ AStream.WriteWord(WordToLE(INT_EXCEL_SHEET));
+end;
+
+{*******************************************************************
+* TsSpreadBIFF2Writer.WriteEOF ()
+*
+* DESCRIPTION: Writes an Excel 2 EOF record
+*
+* This must be the last record on an Excel 2 stream
+*
+*******************************************************************}
+procedure TsSpreadBIFF2Writer.WriteEOF(AStream: TStream);
+begin
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_EOF));
+ AStream.WriteWord($0000);
+end;
+
+{*******************************************************************
+* TsSpreadBIFF2Writer.WriteFormula ()
+*
+* DESCRIPTION: Writes an Excel 2 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 TsSpreadBIFF2Writer.WriteFormula(AStream: TStream; const ARow,
+ ACol: Word; const AFormula: TRPNFormula);
+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;
+
+{*******************************************************************
+* TsSpreadBIFF2Writer.WriteLabel ()
+*
+* DESCRIPTION: Writes an Excel 2 LABEL record
+*
+* Writes a string to the sheet
+*
+*******************************************************************}
+procedure TsSpreadBIFF2Writer.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(AValue[1], L);
+end;
+
+{*******************************************************************
+* TsSpreadBIFF2Writer.WriteNumber ()
+*
+* DESCRIPTION: Writes an Excel 2 NUMBER record
+*
+* Writes a number (64-bit IEE 754 floating point) to the sheet
+*
+*******************************************************************}
+procedure TsSpreadBIFF2Writer.WriteNumber(AStream: TStream; const ARow,
+ ACol: Cardinal; const AValue: double);
+begin
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_NUMBER));
+ AStream.WriteWord(WordToLE(15));
+
+ { BIFF Record data }
+ AStream.WriteWord(WordToLE(ARow));
+ AStream.WriteWord(WordToLE(ACol));
+
+ { BIFF2 Attributes }
+ AStream.WriteByte($0);
+ AStream.WriteByte($0);
+ AStream.WriteByte($0);
+
+ { IEE 754 floating-point value }
+ AStream.WriteBuffer(AValue, 8);
+end;
+
+{*******************************************************************
+* Initialization section
+*
+* Registers this reader / writer on fpSpreadsheet
+*
+*******************************************************************}
+initialization
+
+ RegisterSpreadFormat(TsCustomSpreadReader, TsSpreadBIFF2Writer, sfExcel2);
+
+end.
+
diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas
new file mode 100755
index 000000000..0a8f75750
--- /dev/null
+++ b/components/fpspreadsheet/xlsbiff5.pas
@@ -0,0 +1,879 @@
+{
+xlsbiff5.pas
+
+Writes an Excel 5 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 5 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&
+
+Microsoft BIFF 5 writer example:
+
+http://support.microsoft.com/kb/150447/en-us
+
+AUTHORS: Felipe Monteiro de Carvalho
+}
+unit xlsbiff5;
+
+{$ifdef fpc}
+ {$mode delphi}
+{$endif}
+
+interface
+
+uses
+ Classes, SysUtils, fpcanvas,
+ fpspreadsheet, fpolestorage;
+
+type
+
+ { TsSpreadBIFF5Writer }
+
+ TsSpreadBIFF5Writer = class(TsCustomSpreadWriter)
+ public
+// constructor Create;
+// destructor Destroy; override;
+ { General writing methods }
+ procedure WriteToFile(AFileName: string; AData: TsWorkbook); override;
+ procedure WriteToStream(AStream: TStream; AData: TsWorkbook); override;
+ { Record writing methods }
+ procedure WriteBOF(AStream: TStream; ADataType: Word);
+ function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
+ procedure WriteDimensions(AStream: TStream);
+ procedure WriteEOF(AStream: TStream);
+ procedure WriteFont(AStream: TStream; AFont: TFPCustomFont);
+ procedure WriteFormula(AStream: TStream; const ARow, ACol: Word; const AFormula: TRPNFormula); override;
+ procedure WriteIndex(AStream: TStream);
+ 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 WriteStyle(AStream: TStream);
+ procedure WriteWindow1(AStream: TStream);
+ procedure WriteWindow2(AStream: TStream; ASheetSelected: Boolean);
+ procedure WriteXF(AStream: TStream; AFontIndex: Word; AXF_TYPE_PROT: Byte);
+ end;
+
+implementation
+
+const
+ { Excel record IDs }
+ INT_EXCEL_ID_BOF = $0809;
+ INT_EXCEL_ID_BOUNDSHEET = $0085;
+ INT_EXCEL_ID_EOF = $000A;
+ INT_EXCEL_ID_DIMENSIONS = $0200;
+ INT_EXCEL_ID_FONT = $0031;
+ INT_EXCEL_ID_FORMULA = $0006;
+ INT_EXCEL_ID_INDEX = $020B;
+ INT_EXCEL_ID_LABEL = $0204;
+ INT_EXCEL_ID_NUMBER = $0203;
+ INT_EXCEL_ID_STYLE = $0293;
+ INT_EXCEL_ID_WINDOW1 = $003D;
+ INT_EXCEL_ID_WINDOW2 = $023E;
+ 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 }
+ INT_BOF_BIFF5_VER = $0500;
+ 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;
+
+ { XF record constants }
+ MASK_XF_TYPE_PROT = $0007;
+ MASK_XF_TYPE_PROT_PARENT = $FFF0;
+
+ MASK_XF_VERT_ALIGN = $70;
+
+{
+ Endianess helper functions
+
+ Excel files are all written with Little Endian byte order,
+ so it's necessary to swap the data to be able to build a
+ correct file on big endian systems.
+}
+
+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 }
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteToFile ()
+*
+* DESCRIPTION: Writes an Excel BIFF5 file to the disc
+*
+* The BIFF 5 writer overrides this method because
+* BIFF 5 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 TsSpreadBIFF5Writer.WriteToFile(AFileName: string; AData: TsWorkbook);
+var
+ MemStream: TMemoryStream;
+ OutputStorage: TOLEStorage;
+begin
+ MemStream := TMemoryStream.Create;
+ OutputStorage := TOLEStorage.Create;
+ try
+ WriteToStream(MemStream, AData);
+
+ OutputStorage.WriteStreamToOLEFile(AFileName, MemStream);
+ finally
+ MemStream.Free;
+ OutputStorage.Free;
+ end;
+end;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteToStream ()
+*
+* DESCRIPTION: Writes an Excel BIFF5 record structure
+*
+* Be careful as this method doesn't write the OLE
+* part of the document, just the BIFF records
+*
+*******************************************************************}
+procedure TsSpreadBIFF5Writer.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);
+
+ 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);
+
+ { Write each worksheet }
+
+ 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(CurrentPos);
+ AStream.Position := CurrentPos;
+
+ WriteBOF(AStream, INT_BOF_SHEET);
+
+ WriteIndex(AStream);
+
+ WriteDimensions(AStream);
+
+ WriteWindow2(AStream, True);
+
+ WriteCellsToStream(AStream, AData.GetWorksheetByIndex(i).FCells);
+
+ WriteEOF(AStream);
+ end;
+
+ { Cleanup }
+
+ SetLength(Boundsheets, 0);
+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; ADataType: Word);
+begin
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOF));
+ AStream.WriteWord(WordToLE(8));
+
+ { BIFF version. Should only be used if this BOF is for the workbook globals }
+ if ADataType = INT_BOF_WORKBOOK_GLOBALS then
+ AStream.WriteWord(WordToLE(INT_BOF_BIFF5_VER))
+ else AStream.WriteWord(0);
+
+ { Data type }
+ AStream.WriteWord(WordToLE(ADataType));
+
+ { Build identifier, must not be 0 }
+ AStream.WriteWord(WordToLE(INT_BOF_BUILD_ID));
+
+ { Build year, must not be 0 }
+ AStream.WriteWord(WordToLE(INT_BOF_BUILD_YEAR));
+end;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteBoundsheet ()
+*
+* DESCRIPTION: Writes an Excel 5 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 TsSpreadBIFF5Writer.WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
+var
+ Len: Byte;
+begin
+ Len := Length(ASheetName);
+
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_BOUNDSHEET));
+ AStream.WriteWord(WordToLE(6 + 1 + Len));
+
+ { Absolute stream position of the BOF record of the sheet represented
+ by this record }
+ Result := AStream.Position;
+ AStream.WriteDWord(WordToLE(0));
+
+ { Visibility }
+ AStream.WriteByte(0);
+
+ { Sheet type }
+ AStream.WriteByte(0);
+
+ { Sheet name: Byte string, 8-bit length }
+ AStream.WriteByte(Len);
+ AStream.WriteBuffer(ASheetName[1], Len);
+end;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteIndex ()
+*
+* DESCRIPTION: Writes an Excel 5 DIMENSIONS record
+*
+* nm = (rl - rf - 1) / 32 + 1 (using integer division)
+*
+*******************************************************************}
+procedure TsSpreadBIFF5Writer.WriteDimensions(AStream: TStream);
+begin
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_DIMENSIONS));
+ AStream.WriteWord(WordToLE(10));
+
+ { Index to first used row }
+ AStream.WriteWord(0);
+
+ { Index to last used row, increased by 1 }
+ AStream.WriteWord(33);
+
+ { Index to first used column }
+ AStream.WriteWord(0);
+
+ { Index to last used column, increased by 1 }
+ AStream.WriteWord(10);
+
+ { Not used }
+ AStream.WriteWord(0);
+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;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteFont ()
+*
+* DESCRIPTION: Writes an Excel 5 FONT record
+*
+* The font data is passed in an instance of TFPCustomFont
+*
+*******************************************************************}
+procedure TsSpreadBIFF5Writer.WriteFont(AStream: TStream; AFont: TFPCustomFont);
+var
+ Len: Byte;
+begin
+ Len := Length(AFont.Name);
+
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_FONT));
+ AStream.WriteWord(WordToLE(14 + 1 + Len));
+
+ { Height of the font in twips = 1/20 of a point }
+ AStream.WriteWord(WordToLE(200));
+
+ { Option flags }
+ AStream.WriteWord(0);
+
+ { Colour index }
+ AStream.WriteWord($7FFF);
+
+ { Font weight }
+ AStream.WriteWord(WordToLE(INT_FONT_WEIGHT_NORMAL));
+
+ { Escapement type }
+ AStream.WriteWord(0);
+
+ { Underline type }
+ AStream.WriteByte(0);
+
+ { Font family }
+ AStream.WriteByte(0);
+
+ { Character set }
+ AStream.WriteByte(0);
+
+ { Not used }
+ AStream.WriteByte(0);
+
+ { Font name: Byte string, 8-bit length }
+ AStream.WriteByte(Len);
+ AStream.WriteBuffer(AFont.Name[1], Len);
+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: TRPNFormula);
+var
+ FormulaResult: double;
+ i: Integer;
+ RPNLength: Word;
+ TokenArraySizePos, RecordSizePos, FinalPos: Int64;
+begin
+ RPNLength := 0;
+ FormulaResult := 0.0;
+
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMULA));
+ RecordSizePos := AStream.Position;
+ AStream.WriteWord(WordToLE(22 + RPNLength));
+
+ { BIFF Record data }
+ AStream.WriteWord(WordToLE(ARow));
+ AStream.WriteWord(WordToLE(ACol));
+
+ { Index to XF Record }
+ AStream.WriteWord($0000);
+
+ { Result of the formula in IEE 754 floating-point value }
+ AStream.WriteBuffer(FormulaResult, 8);
+
+ { Options flags }
+ AStream.WriteWord(WordToLE(MASK_FORMULA_RECALCULATE_ALWAYS));
+
+ { Not used }
+ AStream.WriteDWord(0);
+
+ { 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.WriteWord(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(22 + RPNLength));
+ AStream.position := FinalPos;
+end;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteIndex ()
+*
+* DESCRIPTION: Writes an Excel 5 INDEX record
+*
+* nm = (rl - rf - 1) / 32 + 1 (using integer division)
+*
+*******************************************************************}
+procedure TsSpreadBIFF5Writer.WriteIndex(AStream: TStream);
+begin
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_INDEX));
+ AStream.WriteWord(WordToLE(12));
+
+ { Not used }
+ AStream.WriteDWord(0);
+
+ { Index to first used row, rf, 0 based }
+ AStream.WriteWord(0);
+
+ { Index to first row of unused tail of sheet, rl, last used row + 1, 0 based }
+ AStream.WriteWord(33);
+
+ { 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($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;
+
+{*******************************************************************
+* 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: Word;
+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));
+
+ { Index to XF record }
+ AStream.WriteWord(15);
+
+ { Byte String with 16-bit size }
+ AStream.WriteWord(L);
+ AStream.WriteBuffer(AValue[1], 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;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteStyle ()
+*
+* DESCRIPTION: Writes an Excel 5 STYLE record
+*
+* Registers the name of a user-defined style or
+* specific options for a built-in cell style.
+*
+*******************************************************************}
+procedure TsSpreadBIFF5Writer.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(WordToLE($FF));
+end;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteWindow1 ()
+*
+* DESCRIPTION: Writes an Excel 5 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 TsSpreadBIFF5Writer.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(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($00);
+
+ { Index of first visible tab in the worksheet tab bar }
+ AStream.WriteWord($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;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteWindow1 ()
+*
+* DESCRIPTION: Writes an Excel 5 WINDOW1 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 TsSpreadBIFF5Writer.WriteWindow2(AStream: TStream;
+ ASheetSelected: Boolean);
+var
+ Options: Word;
+begin
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_WINDOW2));
+ AStream.WriteWord(WordToLE(10));
+
+ { 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 RGB colour }
+ AStream.WriteDWord(WordToLE(0));
+end;
+
+{*******************************************************************
+* TsSpreadBIFF5Writer.WriteXF ()
+*
+* DESCRIPTION: Writes an Excel 5 XF record
+*
+* Writes a number (64-bit floating point) to the sheet
+*
+*******************************************************************}
+procedure TsSpreadBIFF5Writer.WriteXF(AStream: TStream; AFontIndex: Word;
+ AXF_TYPE_PROT: Byte);
+var
+ XFOptions: Word;
+ XFAlignment, XFOrientationAttrib: Byte;
+begin
+ { BIFF Record header }
+ AStream.WriteWord(WordToLE(INT_EXCEL_ID_XF));
+ AStream.WriteWord(WordToLE(16));
+
+ { Index to FONT record }
+ AStream.WriteWord(WordToLE(AFontIndex));
+
+ { Index to FORMAT record }
+ AStream.WriteWord($00);
+
+ { XF type, cell protection and parent style XF }
+ XFOptions := AXF_TYPE_PROT and MASK_XF_TYPE_PROT;
+
+ 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));
+
+ { Alignment and text break }
+ XFAlignment := MASK_XF_VERT_ALIGN_BOTTOM;
+
+ AStream.WriteByte(WordToLE(XFAlignment));
+
+ { Text orientation and flags for used attribute groups }
+ 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(WordToLE(XFOrientationAttrib));
+
+ { Cell border lines and background area }
+ AStream.WriteDWord($000020C0);
+ AStream.WriteDWord($00000000);
+end;
+
+{*******************************************************************
+* Initialization section
+*
+* Registers this reader / writer on fpSpreadsheet
+*
+*******************************************************************}
+initialization
+
+ RegisterSpreadFormat(TsCustomSpreadReader, TsSpreadBIFF5Writer, sfExcel5);
+
+end.
+
diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas
new file mode 100755
index 000000000..05e47e978
--- /dev/null
+++ b/components/fpspreadsheet/xlsbiff8.pas
@@ -0,0 +1,430 @@
+{
+xlsbiff5.pas
+
+Writes an Excel 5 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 5 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: TRPNFormula); 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: TRPNFormula);
+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.
+
diff --git a/components/fpspreadsheet/xlsxooxml.pas b/components/fpspreadsheet/xlsxooxml.pas
new file mode 100755
index 000000000..4934d0472
--- /dev/null
+++ b/components/fpspreadsheet/xlsxooxml.pas
@@ -0,0 +1,383 @@
+{
+xlsxooxml.pas
+
+Writes an OOXML (Office Open XML) document
+
+An OOXML document is a compressed ZIP file with the following files inside:
+
+[Content_Types].xml
+_rels\.rels
+xl\_rels\workbook.xml.rels
+xl\workbook.xml
+xl\styles.xml
+xl\sharedStrings.xml
+xl\worksheets\sheet1.xml
+...
+xl\worksheets\sheetN.xml
+
+Specifications obtained from:
+
+http://openxmldeveloper.org/default.aspx
+
+AUTHORS: Felipe Monteiro de Carvalho
+
+IMPORTANT: This writer doesn't work yet!!! This is just initial code.
+}
+unit xlsxooxml;
+
+{$ifdef fpc}
+ {$mode delphi}
+{$endif}
+
+interface
+
+uses
+ Classes, SysUtils, {zipper,}
+ fpspreadsheet;
+
+type
+
+ { TsSpreadOOXMLWriter }
+
+ TsSpreadOOXMLWriter = class(TsCustomSpreadWriter)
+ protected
+// FZip: TZipper;
+ FContentTypes: string;
+ FRelsRels: string;
+ FWorkbook, FWorkbookRels, FStyles, FSharedString, FSheet1: string;
+ public
+ { General writing methods }
+ procedure WriteStringToFile(AFileName, AString: string);
+ procedure WriteToFile(AFileName: string; AData: TsWorkbook); override;
+ procedure WriteToStream(AStream: TStream; AData: TsWorkbook); override;
+ { Record writing methods }
+ procedure WriteLabel(AStream: TStream; const ARow, ACol: Word; const AValue: string); override;
+ procedure WriteNumber(AStream: TStream; const ARow, ACol: Cardinal; const AValue: double); override;
+ end;
+
+implementation
+
+const
+ { OOXML general XML constants }
+ XML_HEADER = '';
+
+ { OOXML Directory structure constants }
+ OOXML_PATH_TYPES = '[Content_Types].xml';
+ OOXML_PATH_RELS = '_rels\';
+ OOXML_PATH_RELS_RELS = '_rels\.rels';
+ OOXML_PATH_XL = 'xl\';
+ OOXML_PATH_XL_RELS = 'xl\_rels\';
+ OOXML_PATH_XL_RELS_RELS = 'xl\_rels\workbook.xml.rels';
+ OOXML_PATH_XL_WORKBOOK = 'xl\workbook.xml';
+ OOXML_PATH_XL_STYLES = 'xl\styles.xml';
+ OOXML_PATH_XL_STRINGS = 'xl\sharedStrings.xml';
+ OOXML_PATH_XL_WORKSHEETS = 'xl\worksheets\';
+
+ { OOXML schemas constants }
+ SCHEMAS_TYPES = 'http://schemas.openxmlformats.org/package/2006/content-types';
+ SCHEMAS_RELS = 'http://schemas.openxmlformats.org/package/2006/relationships';
+ SCHEMAS_DOC_RELS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
+ SCHEMAS_DOCUMENT = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument';
+ SCHEMAS_WORKSHEET = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet';
+ SCHEMAS_STYLES = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles';
+ SCHEMAS_STRINGS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings';
+ SCHEMAS_SPREADML = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
+
+ { OOXML mime types constants }
+ MIME_XML = 'application/xml';
+ MIME_RELS = 'application/vnd.openxmlformats-package.relationships+xml';
+ MIME_SPREADML = 'application/vnd.openxmlformats-officedocument.spreadsheetml';
+ MIME_SHEET = MIME_SPREADML + '.sheet.main+xml';
+ MIME_WORKSHEET = MIME_SPREADML + '.worksheet+xml';
+ MIME_STYLES = MIME_SPREADML + '.styles+xml';
+ MIME_STRINGS = MIME_SPREADML + '.sharedStrings+xml';
+
+{ TsSpreadOOXMLWriter }
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteStringToFile ()
+*
+* DESCRIPTION: Writes a string to a file. Helper convenience method.
+*
+*******************************************************************}
+procedure TsSpreadOOXMLWriter.WriteStringToFile(AFileName, AString: string);
+var
+ TheStream : TFileStream;
+ S : String;
+begin
+ TheStream := TFileStream.Create(AFileName, fmCreate);
+ S:=AString;
+ TheStream.WriteBuffer(Pointer(S)^,Length(S));
+ TheStream.Free;
+end;
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteToFile ()
+*
+* DESCRIPTION: Writes an OOXML document to the disc
+*
+*******************************************************************}
+procedure TsSpreadOOXMLWriter.WriteToFile(AFileName: string; AData: TsWorkbook);
+var
+ TempDir: string;
+begin
+ {FZip := TZipper.Create;
+ FZip.ZipFiles(AFileName, x);
+ FZip.Free;}
+
+ WriteToStream(nil, AData);
+
+ TempDir := IncludeTrailingBackslash(AFileName);
+
+ { files on the root path }
+
+ ForceDirectories(TempDir);
+
+ WriteStringToFile(TempDir + OOXML_PATH_TYPES, FContentTypes);
+
+ { _rels directory }
+
+ ForceDirectories(TempDir + OOXML_PATH_RELS);
+
+ WriteStringToFile(TempDir + OOXML_PATH_RELS_RELS, FRelsRels);
+
+ { xl directory }
+
+ ForceDirectories(TempDir + OOXML_PATH_XL_RELS);
+
+ WriteStringToFile(TempDir + OOXML_PATH_XL_RELS_RELS, FWorkbookRels);
+
+ WriteStringToFile(TempDir + OOXML_PATH_XL_WORKBOOK, FWorkbook);
+
+ WriteStringToFile(TempDir + OOXML_PATH_XL_STYLES, FStyles);
+
+ WriteStringToFile(TempDir + OOXML_PATH_XL_STRINGS, FSharedString);
+
+ { xl\worksheets directory }
+
+ ForceDirectories(TempDir + OOXML_PATH_XL_WORKSHEETS);
+
+ WriteStringToFile(TempDir + OOXML_PATH_XL_WORKSHEETS + 'sheet1.xml', FSheet1);
+end;
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteToStream ()
+*
+* DESCRIPTION: Writes an Excel 2 file to a stream
+*
+* Excel 2.x files support only one Worksheet per Workbook,
+* so only the first will be written.
+*
+*******************************************************************}
+procedure TsSpreadOOXMLWriter.WriteToStream(AStream: TStream; AData: TsWorkbook);
+begin
+// WriteCellsToStream(AStream, AData.GetFirstWorksheet.FCells);
+
+ FContentTypes :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ '';
+
+ FRelsRels :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '';
+
+ FWorkbookRels :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '' + LineEnding +
+ '';
+
+ FWorkbook :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ '';
+
+ FStyles :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ '';
+
+ FSharedString :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ ' ' + LineEnding +
+ ' First' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' Second' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' Third' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' Fourth' + LineEnding +
+ ' ' + LineEnding +
+ '';
+
+ FSheet1 :=
+ XML_HEADER + LineEnding +
+ '' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 1' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 2' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 3' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 4' + LineEnding +
+ ' ' + LineEnding +
+ '
' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 0' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 1' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 2' + LineEnding +
+ ' ' + LineEnding +
+ ' ' + LineEnding +
+ ' 3' + LineEnding +
+ ' ' + LineEnding +
+ '
' + LineEnding +
+ ' ' + LineEnding +
+ '';
+
+end;
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteLabel ()
+*
+* DESCRIPTION: Writes an Excel 2 LABEL record
+*
+* Writes a string to the sheet
+*
+*******************************************************************}
+procedure TsSpreadOOXMLWriter.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(AValue[1], L);
+end;
+
+{*******************************************************************
+* TsSpreadOOXMLWriter.WriteNumber ()
+*
+* DESCRIPTION: Writes an Excel 2 NUMBER record
+*
+* Writes a number (64-bit IEE 754 floating point) to the sheet
+*
+*******************************************************************}
+procedure TsSpreadOOXMLWriter.WriteNumber(AStream: TStream; const ARow,
+ ACol: Cardinal; const AValue: double);
+begin
+ { BIFF Record header }
+// AStream.WriteWord(WordToLE(INT_EXCEL_ID_NUMBER));
+// AStream.WriteWord(WordToLE(15));
+
+ { BIFF Record data }
+// AStream.WriteWord(WordToLE(ARow));
+// AStream.WriteWord(WordToLE(ACol));
+
+ { BIFF2 Attributes }
+ AStream.WriteByte($0);
+ AStream.WriteByte($0);
+ AStream.WriteByte($0);
+
+ { IEE 754 floating-point value }
+ AStream.WriteBuffer(AValue, 8);
+end;
+
+{*******************************************************************
+* Initialization section
+*
+* Registers this reader / writer on fpSpreadsheet
+*
+*******************************************************************}
+initialization
+
+ RegisterSpreadFormat(TsCustomSpreadReader, TsSpreadOOXMLWriter, sfOOXML);
+
+end.
+