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. +