From 4f04afdb1c6ae9b1803e06d7ca5389cc47435826 Mon Sep 17 00:00:00 2001 From: sekelsenmat Date: Sun, 24 Feb 2008 13:18:34 +0000 Subject: [PATCH] Initial commit of fpspreadsheet. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@362 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/builddoc.bat | 2 + .../examples/excel5demo/excel5demo.lpi | 251 +++++ .../examples/excel5demo/excel5demo.lpr | 59 ++ components/fpspreadsheet/fpolestorage.pas | 78 ++ components/fpspreadsheet/fpsallformats.pas | 18 + components/fpspreadsheet/fpsopendocument.pas | 407 ++++++++ components/fpspreadsheet/fpspreadsheet.chm | Bin 0 -> 119150 bytes .../fpspreadsheet/fpspreadsheet.dox-express | 847 +++++++++++++++++ components/fpspreadsheet/fpspreadsheet.pas | 588 ++++++++++++ components/fpspreadsheet/xlsbiff2.pas | 305 ++++++ components/fpspreadsheet/xlsbiff5.pas | 879 ++++++++++++++++++ components/fpspreadsheet/xlsbiff8.pas | 430 +++++++++ components/fpspreadsheet/xlsxooxml.pas | 383 ++++++++ 13 files changed, 4247 insertions(+) create mode 100755 components/fpspreadsheet/builddoc.bat create mode 100755 components/fpspreadsheet/examples/excel5demo/excel5demo.lpi create mode 100755 components/fpspreadsheet/examples/excel5demo/excel5demo.lpr create mode 100755 components/fpspreadsheet/fpolestorage.pas create mode 100755 components/fpspreadsheet/fpsallformats.pas create mode 100755 components/fpspreadsheet/fpsopendocument.pas create mode 100755 components/fpspreadsheet/fpspreadsheet.chm create mode 100755 components/fpspreadsheet/fpspreadsheet.dox-express create mode 100755 components/fpspreadsheet/fpspreadsheet.pas create mode 100755 components/fpspreadsheet/xlsbiff2.pas create mode 100755 components/fpspreadsheet/xlsbiff5.pas create mode 100755 components/fpspreadsheet/xlsbiff8.pas create mode 100755 components/fpspreadsheet/xlsxooxml.pas 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 0000000000000000000000000000000000000000..b78b128f5e00f70825b041a8dbb1d848407a889b GIT binary patch literal 119150 zcmeEvbyQVb)bF9Y8${{u4(XO|kd8whx;q4sZlp_)?k)l82I&UrmJkF*-$A_hdh>hl zk2l79W4IZcx%aF!*Kf_eVh6~msY*ft002Yq4gub0>4aC{paB5%TL|xBuW^h_WI*-+ z+4dpEU(WCJ?>78<0&xF9)mZKihJVnncTT~Z?Zl6R`%{37nyS(rM-99q-to`@07x+T zZNFpP$-jZOyZ8{mFM@ayV`N<4$8=|VC;x3zl9!TG0Q2s)JKmi=1T**-8Z#-W zn4E~Js)Q<&g|iI^;#rber6|T7S5ZM#O+-QMH+~;Y69@&&mz0tFO*be2JCE~DS5jR; zOic!CaPK21U#3u(9?X}QkQbFuQPhx7(U6hQG*ELAb8&LEvr)Bo1R9yDfOnwd4~tRC zG5i0~LetUG`8SK;Oq>A>(tj#L)6UV_*v`)S-gEE{)wm+rzv($y0D;c;iq9Kd=H!`v zS5(thk}#081cFSR{v+;?7}08O%6~Ds3*@H)Lf(2*m@@xaS03nWVfWu9oawdy?^*qf z&Wmc10qVcR^+#EMM(D+7iva6s+xp6Aka6534EreC z){O*(k$_LtRph|t5H(q*slNB8YNB!wHOa^!T42wbG792~nyL^LJ469;NX!(HGG@+} z@TNPiY(B>&Cb#EcRSp_F-6rJfk23gBh>(Zx$njO$wb`9*$ATY z_)#}-HPmpo7SOpo+mG=BCJg7!@B!JqUj zZB2phKf9U|3eq9_HcV?uSE3J4bL_KZKDJgvF@D%og8=$bSt;I{=S} z`EJxUFmnNc%p8qu{u1(reT*mOy@s=;GYAN-8IzU0IYj=2RlpZk1ZGQ9_xoTXKhp#x zA|f$=zb@`s(J^chR(N+DOA{k!J4XW}V<%@vBa?eHN)Bm)WUzmdnt+U)obJI~X9|3R z^^*u_>tb^+`A89{^x$WSI~Z?UBai_I+?Re=Dfs9U!1}Xav%4Yq-g=KMKpf{MYn$(b z=e=x;J5U(^Cz-t?xG7DXfj><5T?5#D7K3ZzV&d%L2)wtxat_qT{mI(Z($U$)2y`#} z;1I-4$Sk9vW+0&@CLw2_rYI|+V4x-@LQFDW5!!>l$DpLBDWUR1rBkK|m+0>*DiV?+KQ#6Yim?A#mDo1eBFulNimJNkeIc>i_(dp4%t~TF5a^y07fo3xq`|BLG`DmDxAxD;F0A$= z_wT2IVs|O@JMVrv^Jm`otM2|n`Qg05w3Ho**~!Gw(jI*6SUEww%A^hKvLs*z5Bxvc zOF~OcLO~TgJN;p@iC5@{#H{v5gz=HM<(jzn9I^WWPR+v}c&{GcVp9T%!K|jDq;OZT z3&`jPf7`A|6YV=+&CVKVd(TaXku0$$Wd8SwZbB<-1tGydE7;qbLzH|$3$$kVN4bHd zn2RHLpmF}olq}&uwm_WXUrhcy)k};=E1+lkXVtql_)$Y)HC1Qu4Bh6wPKnKYMGtxZ z!^qLjM$!@l6b4^ZmbT{L2K-B*8(PH>kN?~LzP#Vc{hN!VNbO>je?jFgZaE`kAm}&i z`>2x|%!=?B|GiWN7aL=s<6msndg>b>0H zJGo0bF(VMj7(9Ctwzsnc&!!xIqxW&YY+@|K7Whlte|6!cX7^XbB$edA*Vhjp+afiJ z693`jFYPQ0ZZOmPUw_N<pMnv_~g{fCo3w}!Bok)x?4 zcwG47z|_vg81&jAW#IV_Z)$e`m5(V|o{h--|7reLaQ~9gDRZzzAbhZgpF6X= z+4+5KQv)1JZSJzl75{BNCF>YQU4A$VQ!AKM#LEfgDMPoyu1yok{V zAK>-ici`mU`eEf^hhQ*Z`AykC#MJce_7IQn)EK=ey^p$pIRsDgzB}?pUoxj`pO?zWGBJBrn&3?|1g&XPfYJLes7825ANg4 zZe?!br2mr%xE=q~;-hH`DtH?3Qy~iA8TGxpoJz;nnk0V`{@!vw1)4J}Q^0UH0Q?c? zcics6oh;SB3%lQg{Num~Ec&^x%=zk4Ps8xgTmh5l~*$L!^gf`9k) zw?0324_GUX&Bu4wrK#E*nE;vZfVdl90D#9dKl4?=3j-r_AhW!xn4JxHVdUflG-duV zhXUj=JWqFJ_}@}@v;*B02C#ZS`#B&f35$2t^a?m|9`Fjf35$2t^fb>aLqvSZXWW-!?0iL z|GP(y26rp6Kd(6NCwWP~*8hK-3;tUF+x+cz)c+6b|4Zmc&^S;kkRA|80Ft})zsv&K z_heF5cq|_F9d!e@C$)o2>d^e03_fc z`0D{=fI7eyU;?lM*Z}MSjsPIQ3H$>Dn1Z862FL?c0b>6s^8#=KB*8K^01to{zyV+Z zFoLN#fE~aJK4Jy`h=X~wr^w$?fCc}Sd#WY&+t&(r^zLheyY24y?!^Xp@J<8Xf3tCo z`k~dG(gq8Rtf=t3y_u;A<+OjI$7G;l5j9_b1?C6v#^2X6o9+$OYXKiyL(&kK3Z()Pn7V-} zGML_NDB!P5)W}uknaD06?g~=*&b|Lu2RW zg}^Taej)G+fnNyxLf{tyzYzF^z%K-TA@Khc0%$+(1)!tf-LGJWQX&Tc^wlkk_XWwe zv}_?09(@5WvZ}#_LJKAl1`4JP!Gefh+MpGbWxd&K2joHlILYlL?Ez{?Iw1;36sB1m z=G|+7o<6lY6Tu0e62`pa_^v@rFwnN|O6yv0GY~sd1_x{Np*UN~fdJp$&pjeg2bW^6;_~cFUdCg+7k6eu3PXibc<9DH|yPEBRn8 zF@lYrgHneI4qJ+J-l?+Y-dWc?`E^jnt44C#YfD~ae&JYyW-Ut3Hw&6KGf`3`dfO^B zx>?Z~(J18|Da$gi(oh1qCT-m^_{a;&UJhcxrb|uO)@Ls-Dsk-dNv>E;w9JaW;VBJo z)AtY7n3NxU!d}tTs#6z}Bqe-g_qJtk&rGb2WXJ%n%7?Y(*pPkMddjiEi+F5zPZYOe z$&c-E`e9M6*%3tYeDcOCQ2iX80q)5DNNIyjpwx{^GiOr{ZykO^9sFtV)A%0ipn5K` z$&VG@tMZeJt^%(=2gw)e*pAu}NAe43t{LfQZ&T+A4u6Ej2*GLiUZLoIvzu0_+_B?N=29AxpDWgC8+9cZNrEm|tZnmsE^Y zjjtnDVN70>nUOD?w;=0V%$@1!(s= z@uK7&lSaJT%vx1>PJKo-oI=l6gZl~XNV`y*Rt5ef$WA_2INJQR^4#2G_dzMp*1N8spca~6nW8jK8>=gA>3_~NKfNwqTUIFa zo64gGwHrC-Jiif|-?6m)=u6W*-4gBGSgoh868B-6P{V2VNQ21Q6k8xj@{G&D_@;?5 z1e9KHhW$jYV-8iG*X)o6*%C4)B|8yA!4{fTRMoBSK;lj$~8Eg4;Cm%Il^Hvpl5vm<4f+m(UC$GNtU-ley@~Z=+Fd{+Xa0!kW1{G&6Q%6g z+!&!-;(bu|9@g>bob`%HrOfwLV2J3@7>MXth76yT>FNB~tfS>u{cG;L9lI7C7U&om9l8cq1=)-Y;e(k( zXxh(hZ4b$XT6`jfNhF9EVsL0eB?IYw;rgQyEpj&t5xCgv6C8EsY z<4Yqame$;w!uCB6HMduA=mdIFef>;l2z&x`-NXxXfJdpv2=4DOD$nuiyLDa_zZOE> zi*i(}N|--=?}{JKDaA{4~IO1hrZ)@$l* z9kP8y#!1AiOIV;Yl)MBvoNZdM{m91;#L zu4+>!D9qN%4P<(4y=&$=;czhVD4U-*U1k~U7GWV{GHXg?B1cHnpfNmFH{zjYq3p@kP`h6v9ibH7rx^ijkPWvPywP#9=T#!F>G4fjCX& zZBCrYYQn9^(d$C!rKptnbi7H3_S0cmLuNA{yL>6Lr0V>|31wPK|BE-@ls^~tkL>kd z2r52Pb?noBUbz9|i^m z`lM8E6tK8_+HFj!zP(Ar=x%uDV3m;H_e8&D&LRs(aU|?Tl;>96j3S@SzHk#L$!~>K z9--(t`Daxd0cu{_?uelA`kLuypRKYR+m4{{)SauXqt%aVeNq|WI7Q2$LYW&fz4jA1 z_RpL-UIYzG9N9bIGDanOOTRv4_!1GbD8MRHNhQYt5~I+iW`@}+%@WEw2Q)7Ro+^P4b( zGUP88Wac|78-#tIav@67Q~JL~OjA?9Y@Af!MaJn)7a;CVWK&u~X0(**Vcf^)V(Ceq zx9;nw1!?GyLoP-xS$T?~E?&3#f0Ty;q^o3&)Mdg9$gI!I%XH<`g;WtgDF7k6v+;!8 zEO_x=Xo2jmZhCJ&Xd0?dqd=b8t+Nr%#OLM^8>v?!-eh$gYq`V{;z&GetfzTJn&6w5 zmUyNyVrWN1hlaYPxW#;vc2PfK^yAIkW?^*z(C(+yL;xf7XuVH+3)o2g9s7>fhruX>XC4cHZ zh)PB?(Cc)wHjwK1L`Y*>%k8W2mFh(zcVDD}dW>i@luSNlEYMkhz-GP+TAIp#j6P0k zl?varn0~7#YabEuG+Ho8)&DJh;Bt+cNPMf^*S@&wqg!oJlIE*5?&xlcmbZTU(D`5d z4j_ABzdSc^tX}_^91zR$3>jw^5>f;LTce-~CeGhWhX7LEy7T(PAGan0?zF|$iKl9q zM9Kmtz(4Vs2Lt4G*4GIP-?!rxmmHH?`D;!$&$N&lh=g%3b+f0E@v5b25pE0IeW6rI z;}5#Jj(14%R?Gsw5WR+W?%`RNjFb7IiwO&VG|+`Yd}*A0x|)Ob^ld19VTJ_0RD6}) zx94G;d!z%3m6rSqmFhI9Z&KHSp7y%m^bH1>_URyq2i7%97szL~Ag7_%!++WJ{=laL zVDBFj!G_?#w+-LZ;7MkOp~ha0P>9nJ6=j}y@>x23X|(XYm5mS0E1bS1wNFdK3NM~w zzv|}b;h>VFxJgEau8vI134bC~DSMsVQ>|M*ObVUq4rBqWq58K72r2I3uEvWr-X4Fh zlCO2>!&!m1o%4;aY8UXU)+?N$%;!>j4=`OEtVzVj3p@!bSxrXFzDRm8i=p&|;`VYI zUfUby03vK8?<6*%KQM1@SgD^1@|ClxFKs~)ZVr%J*mV7TKGh&JvSlpM8B);I&WMdV znxN`weNci>Cn8={ZKO$lWCD~MSF<|0=O)8ImSim^bSd16h*Ap)9bOlA0sxI=p2Rw= zS0O!nzrTtktQv}wMl)H2;S`Z5z>aMyH*8jDKb^cYP;wWJH0fD?NM^2pvawvFXRwR% zE|FE27K45gxfWy2rpbQFA{L`6DlU3H%%Iisemf47}DzT0DxG#*i&)9;(_7ELDcg(a|oF{j)&;)==|a6;a;=}x?m_jjxH7xP9LnT#wy3N zP!-S!lg10qOeB#2VnPJgdn=169Yc3-BT`bLA!GV<1}XzC0^ZVyU{7r`zX&6W?;52< z93p(RMg`#P#rWtICSF--7T3-rhs!VLXcN3hjGMzBR|~V7fRyAHS&2a6MuM|QX&`G1 zCp%haL5LN`>~4%F$VwY(tV2Wd#QWVQ4RU@$3y4O_A+ZfJ%h>pMl+%ug7gLJW9p*#|VI z4-&m8BzirD;O`xSy#QP@ns4vw3BO;iKZS3HD^nef8 zUx-ZT^UBfo@d|H3Xp8w*X_zBFO{f^ctWIEZgekWKi#<1$kzz%MX9?V8ipBWCGslL9 ziEti#LoKMxr!r29lZ{?%BQ9F1gX}XdY;S=hE);`)iXaT1)7yj?`zW``ZUqm9_Bg61hNS(0pN%vrdrZUFZ4(Po%r4(>DqHzh&sUg*HFzm?0O+k}uv@jY3k!|69g2HpX z@TJ*}&*_7&<CkPB<&HQ(_~gl9wL(lD<&uT1Gh+ZI8yFw^3^;-Yj;F zY zs|YEY@7K{{+@DV*X#h{^Dkmff^FYy@^|H>AO(`!>4#149 zw#^8@`J61VB=+#23zfw8z<^D{Mnw2Cbt_5kmf&?}QnBVhGo**C5Ix!%E>f%1W8W_Q zA=#2t$=b3pzEt%TAq5%4!CR^G9YnRyIFuMhL)s(?`K__fOGe1g*(p3uA|??ele|}Z zde6Uv`4K+#6Gp#%plHZ^rYhhEiJmdOcBb*h78WIo&LsAH^}(}mrL<+14v#Pd>(V}+ zV?#RQI7pMOzhh_(+^zpq)IeKW%Zo4k^%H@#Eh7$3lP0a9dq!F16-3C*lbn%h!?)%9 zKEW|ePvDTz$CLq*cn%{H%NqDegyE60!YKPTQBr=m&`BNC9eVqJ|_F#+PxemdL`t1S3dWLPY@#F?jVfs+g*WpBH_PTNcQ^=!n>VZA-zAqP6 zh_aj3kBibF1owg@LjB%+bS49xSfhNUm*=sJ&^XO6sXl&#AKB5i#qbWoI{%E$J3iN= zbYf0qg{r1%T7HWXclua0?=jbqN@_i15BEcgz5}_sh?l)o_&`P;NQk2p5}&XqfvpN% z{hYm~;V}_rFX|z;C1_a)%UDGG1^0`}v~`QT-C?ecO3@p7LC%9K3DZf9k94uZSK$q& zd^~7PyOT_$S0~K}?4K>;d^mB~F;u;TlYdBIkYxd@u*D?n>xipe0aPk zRjU}qru_!vvkrI0+ZGMexO49Kw&ryzoqPjiPw7XkV10=$SBbr|VCA!9-FfE3d2@_& zXvx~#2QFuE`Uk}>4YygMlNelym)f<^}h6?#4R zxn0@CN!hYr8I80)v?1eUmZymgWU-b|IoT^LaVe@p{K%=%B;pg2Y!0jZFv{nl!#IW- zLprNX^f_%7Jj$oc_I;WcQ^e$Rctfd^6*q(WJc-p;RDB9ZMIE%kmd7Z6XhyWPUDNNuF0cvK0Tb%;yI7Tm`-@l(I~pg1V{r)h(gHPdo-Q9<<3ah`Viu2UV|cYHoB* zP2Y|dGAYb(GrgYYPLgTERY&>~_TiZ-E~jAUj)LJvD0UIwaVWX}{%`lpSio;0DXNpu zSUe&bwUG6KBjc@(wC%zgax~l$jhDe*E4SW^Yd)&WP>qB7cKfY^1OSNaUn9Tl6N0~rza zs{uqrH!K2%C>qZ21T9Lja$HJqfIbuSyd{d{BWxg&1Mi4=fV2NTDT7>1~Y2(H)+|m>#_!B_v|tJVp*v zpC4%YSmbLB*~anK`~cqkID3*KI919thHg5B{>eyjD&x|rcWG>d0#Xho*|q?gJh`?j zu|U)tzU$!yeh_HUsaD;rMzNg>xFz$XyLz0fw;7!<^ z#s;Y>tW9sQjPc99CyM7L`SNN4=?Q7IG3m>m0vK;IxappH=CA-E*W&1#usq~5x5z_n zPC`~B%lhgLMRG1%){kBtdXCMHTd4)~Ctl~c~wfr%+d6;BNkG<;N+tsAFuP5H4U3hUs=8xA<6B1*Yli+<|J0t#-T~I zi@n#u-1>$p8deX|WvXh+L`sK*Ydnd!*312Z`-rbL*}@B7Zep@o3U2@c78_LedCK9S zfcx4ob{WV~5PP_;alS(uHy(FWUyy!Che1m|k#+FE(M2O}0OP|$l`KoC3CHBYat+H2 z=UNlnfhUVeW6?nK{CwG&;o+7i$LSH?pNwMu68zaS{T_)nn{?-Jo^hoOMIYEIm;t0)N}ck-t_*D@zMX}B6dGN*jFkc5rsG6x zjJb+PkvNywSgjig9^O`?O9I;CLJ(e{YDnkV>XD%`@$qv?;12(z3GSKfG^?BomqTSn zS!bjVjXH{j9P#oXthmRg*TY{i6*{i1oR!a9uV=&-Zn1^-w>GJv{mlz!L2XJ*2%U+V`%p;ybn4A6|}F{!alagBk-CJ?&q z?Z|2y)rg-X<|2FA(_89|lZePl8jsm0TvU7-MjS`9Vp*cZyU35CV$mFFU>OasSg+5* zY_m|vN~J`wLXO($>`6XJP4*JSv*j-$hAtN+sgtiB4Me*Hv0Y@n zXwAq!@b*)Q%^`MM0daY-l11&JG&;o+IXBl+`8PF zVJN%T6UI*(vWX05j0upP2lzg{EqZhF&5gHM7Gr!$UNNGPf04{bp}MdWMcllNyeY1*5~aabtT9Z^7$QIy`o3E@zffNpC7OW!#({ZrZ`_7SW2c~ zVR)n`@O4rY7e;By-#Xo0Rk5MreI3P8R=(786Ln9F4kS;ivW$1-ko>`CFt(ynFIN_+ z2d7J?*A8A8Qk`3~%j_Z^Scj(*j;M90y4uy+vjvx-+_a0gnbAHIUz0tM%+w-MX?~AN z3S}dUmxHV@Q4A!@^^>V*W+MV9hA(PN(!Q->)oj3;l`Wd?j->_UZBdwQZ{?g?%E)`r zTar@`Y0L~Cghf-&jhSTVP+tvSPB&SN%^iUZ{1Or!D?HW9-RLu&+44^wJVuyxj)ww- zeS33yo`uOYyjn4zkhF8C26>j8xSb)P_I?zb+FU^+Y0tq5=EQ7r0QVpuFz#zmx${>B zwkDXYuj@h|OG*VdsyVZnxH*-HyhuqMQG^lsV-gF@3J0%rCJHnPfJYg}8wrc~3SLxY zdqJ*ROD&=_6*l?GrZnHleBNN~e^eoqqK5|=;=CNOIOFhD=uzu%JMQ7jc$ehUqt^3|v0GpesRYr* zW{xxl8Qn9h(=?`zl2gGdrLssW8d^Nun}cr(qw-e6p@LhiL7$iv2Lhgcu708SDdUiI zNu~x4T^_J9{h*#8rh(bgcbWtfjr5f3;Z1x?PT3pLP4?ookf%Z6)7~{X%*b0FA*Ayp z;Uli^qMcIbDD^b1G#^mEri#sQ=%Ib2*EoXEev3~3;#1vH2#wn7*Sj_d+6+rdFZPhF zyAv2|B!XI<7*O62K|8}*pBs0tIve-%^r@}$%6-L$6;_!o{s0gTfwf$kukAy*(B3k{ z#{*mv0XCBrj*i<4Qonssr&DxfRk1ys66*;NK;>Jbi-`*Uh7HH>SCL~ce9Zwk!~*S$ zidh}qlxsUkaF&0GeO*yh@%D9=N{?Q%I_jig`+Ogbuw=^~8nNlHm1Lg2kGJ>JP~RCJ zz{J41r2td$NV38>yotT;lMueTezi3ni41v*&q{I;OjQM~<}jio$L#LJon7J{S2sWv=zWb6hzn$-V8c#!Qy~=}fpOMay&c2N7WeSmU$ z8cG!k0^oXKZU89WLV;hq2RuNL3oP{f^6lXRzOcAF`j^4JA-Z-TFnQ?kwM9!-=R-_)I&-93R-hJ} z^l~pK;62yjhZ?rFhgDnV^t45m`x9YErnVBZdQbKp3XtMpclmobT!QkG7-+1vOE?ts zYm#p+`EHK`$ZI1xuzeWV=3A0Ykd&MA#_i1y5IZX>VBZ{%p7dLB6BFioC3Eh)`A`gF zV@_IS@tUwzNA=yQa4Qr8=VvG}O{2~RmaiKdT>Nucp2g~Z^$p|YJjTL^?k)r;yldFtQQ`0ezj27yCPNhLn%ShNH=oxmMA){)| zrxFDTulG)b_VuQsSiv0my=emH4I;%&CJW8NUUi%U@~57G(+vZD}Gy{S~YPU zGEnU`&=bbJ4n_1v`5=*NPqNJp{df> zn%$Al_RL(2G_e0RdE2(XbJArc9O9wlzSJw-nmgtE1~NJP8~4)jH)dbSgxGfnTfG}7 z+J!kFmxAl6ZgZdOr%vTmAjXr$cLoar`+;S^mwvMG0mC+eCIzA#&Vur_PvSBZ1alv5 za|f)(S0Mo_GFFTAJrPoO8Qmk^jiovheD+uzwmovx;qn;4=$_PU1;V!)@mS0Zlu4O< z+nT1TCt#LhFx~oaNW}JB`0C>&51O{5jmC4agWiRLaBtr)IX#vHJpXeqG}nVcDtvD3rJ<|#eV}JBLgO00Q3VP7;xs==j#V%EP)*#%sy#X(^Fwn#+O#d zL9`s|w;XEHV7n$RH^HI0g!XG5AL>QdJ(JHC;z;P)R2r9XmPZRIW0hj_*)4kM7j$7+ zPYX)Qi|f$G%>4uqh6Pwiu4SVkU#%>+9Ur@19js)uxwOOYMGq4y)3 zy4DKi*Cjl)mW7}d&sIWFrJ*aFnbv@2`~)G~52WxGC55J@5bg#90Younpy|Gdzu$C> zINTtX(Rrfw{JHjcUUZ_k!@M>tr0%d0*SIJ}FK;dRGajOL`ok&Ar%{7JD#* z7gR6kIXYApYq?e5Vf70rD=I@O<4;;wj7FQzN-A6odYjc9dS1^<6;%vtF*obX*Q#9w z9$ERkdYtl*;Vf_HcA&_BM>WqJdS6VigB!-@&6Ah4)?v>9=o+IAQ-=$W5SO0}kAAvY zdwGr_a?;f>CgUn#zNGZ_lHZOPndO1<_IdVjm}nk?$=d}-=JVMiOHpznyc|OmA`-u6 z`vJ3ZCrw)v=V`|)$CDpS*4cqiH_*D2TMt6k%%%^6NAQ4+W057EiapQM&DkDJm)aUl z9(I{$xi=4OH0!(X07FNmh&HC5L?N`T_mNLO>Add12~hPR@L>*ibjFLjy-o{pUQJ zPpq3e^FFD!hBCpo*(*uJ^It|818EgsR<3lymFGzAx(AlmPVj(52+e54KG%F>yG?+g_d7?4||Xc zW)TjW9T(|#-&@sqFE)bd066y6QG);{JPGglG2;+_G!Fo(zg}6a=lgRBahk|G4^5_KI-&cv8||Jb51hU zo^+G3Lm;L!X+L9@9~~#1CI(s@fk;w_9v2uxuY!8i91_&Fqtv%O`nnJe)~lKoL|`!} zGNI2Y3CuXNFe$_L>iu5iEyg=^DlZ^>G@Cyiqw^M(I;?=ATG&O&+_MRZ3|nbL(YwU2 zN`O;p#%54VpC~XF!FVj4XGws9r+pUrV3*k}f!2hAJ#L|XEkZ@P+(yqDQ}4jYv6s_z zikAS@M&M$zYDUi1)=A+?EWmIzQ+hbg-w>L-&10*?)w?NKr{bmPn}@W5;KlKI{F7;S zyz+Q*FI>w7=l4fb6a$S5>EE_ots*S0n}>&Q1$1VT*~N7;66R}Ur`e0Q>0P~5%0<*i zkmTw=yr(gm<|wuNJd@BsoU!A=_({fbCzmrrdUX)|08Y@c@pD($abWtTLCS~?SLMo# zjP2_Rbz9si&+OKE;)QnW6I(H5*Wsi<9(vWmsXT332pr#7=chd=&r$3aPQsqntRwqW zQH?^xVK20>EFFr0{mzvI&Ol1*k(h3&SdA!4>KsX}Xn)YsGUTus@J2h^sO->iZN>}<#8Y{m2&~l?w=9uOq)(zj?xQOe&UR`pu(-SZ2@KCWymgd7%lybMYwoF&hX9RvR zLlk$ed9H{SE&)>F!H;Ly+x(F(9Iw8lHPc*o7PZk3a4$B=>`P3oV=HhP0i_waqwP;; zH(dfU$}wsLHfY}zHMo#NBlFbJ&7Yef0*2i;O}D44o^pE?rn8%CW5Lgq=UstcFB=Vd z)GjH(NvPxeJ`CAv*~K=&#%QNF{?PB!ew#4$8c#%vf4QWE5C6*zfH)d}X|J_6MN*%b za@o@42)Oc)ieV@~*K8V@=kash32lx6S3##;{(SD|;{06@ThLV7(P8}DTPY1tI03cW zI5(Okya6J8iD>9-1$TV3g?C>tcG3@vC}X&#kS+yUJVjJYn`7EOwP8 z&qEp1ay9>Yd0o7;S2<$|{c*iL*1`JKQtNYEEWK>~MAesJSV2A*0rBy-LkJ(BmCv%r zs0L3dn8s(Lx-c@$xVCtWy^`6oZvc3B^=fTwUgWsUbk;0oF=!^DRGwyqPDo+rQaczf zL>E3uA!ZS6i*4tboAymY(|U| zbS3bI4Bthl6tGANzW@MI&>#uBoJz%sfqQ9H@ADD$D*Njcyj4U;UzWGbiLPpkKxWb! z)MgRCX~enG=0m*pa@>6%sGTts(T+J;!;RWCgyGVfEk2kzOF8-KRe%2DcuajkHLR?| zFZy(80DAtq+Seika~bnMQ3(^|t7X%+daOt9NM(1CMLw!GHw%TQHLx@{F`!#7zIDeR zE$ZEq6pYOra#9wAWpD8(5))JR9auZA{Uk3~@M0aK8?rZ%rkj7pIPIx#dPncdOHGK} zW`xRynvsiSbZfpjsGYMnB#+@Xpibpi2Z$Xhp2g2yk04!3#!w*at%Uaa9VFsw^aqaU zoU6zmDwzpfh>>)u*5czeYz5*YoTg2iRhcYHn4-mEL2QbPJZ8?zG*3vu$asquUp_%x zy%9;7QNkAyYQeJsC2m;S=}N27xm3fD;~7$-Ebs|{qK`VwCULH0pmt(~r3AOZdG*<8 z6D8qe1>V$HBJniNN=cMWuxGRR<p1*MX8IcF%E$`bOluHr7WkvNB+uULz5G zGh#0Nn8d=P?6n${`bC1KR4@f}j-{JvDH(yc@@?%~^(EAom<1N?bf4zq_oCGDW}%D5 zJjvhUPukT^-X|Q>sUCWl;CgJYnmEiL%q+V~Xv*%V2;q`|axvKah;Gn$IbN=>072sV zX`&7i$9pO~TsN>=0(riVZ%L0AYJ*L#yDvCj=OSxX)J1pbIZ(5AiPsxvghvJV4N!kv z%cKju@^)8wCdP-2I5WATN>~8S$lF;1INUI(E(tSNv=4at2Va*_~6b(3iPvdkKV)G`U$g(${9;G$0hPOC_Qt8jt^z4I}c8#3T z%0QQb1UsVjm7Zh~>C{P6>;e zJrpl2Eqc+3VDE7K$!376@3D81WVdfrANs^WYJPH&fw2R=h>}?t#5Q8NBAxszF5b*w zje(68@q&v>J7vri&H7p1r)>g~m)Sp| zqW~o15YjfzL&k>r*cv#RsA)w%0qL|+h>2qQS3LqyE)Xxap+zj^Z5TM(Z0%eH=OH`D zTG4uytECspLcGBgXzzPhP>I)EgrZG}OGK(CW=1fvaTDD|`by7d2U_M3A@(8$AY9>v zNz;$u1AwpukhlSQ$mxV?wvw)xpKc83MWva;?l`bvLQj`GqzjjoQu~-bQqfm#xUN4h zd3}l_RULQa<~DHD74@)gZuwo!vAnvL0OlAA=Ol~?wXtoMH-fiIt{eMMcMe@>0=8N} zZqRZQqD+F4)-&Vb(L-MiE8kDP2s-6_45#ewQ5mbuqz6yN5DO^^7u2PjF8UV&%9WY= zvk7D4MGhG|C3z|I^{?7u#*v<{VX6ogY$eH~PIWml7y`jB0g|m-A!fc+sRps9`tvGO zGSrV^22W3xQyuj37xl16Lppop-O}h(;ieiPUVgl|_8DE=5(NLRjiXIhuS)+)L@f#- ztcUydcE01Uf?ctW+wc}9EhV~*O+S{tB_NVA% ztrkV$?-X@m)M}T$Si#nn+8aCy;U=TB-XWg?V%+j`w5EoUCX>D0iXtNxJYY5@7p2)S z$a@SV4fb($7dtmTBX>eL7?IkcT#&2X>MO*A!BZNeuYJ4Afv>>TZD5)C< z#h7b+ZQV10mM8hvMl4J53?2N6%RBmZy5^f$zrB^~P(^bARg5YNTf6O@8UO`^XLlqi z{n4p(4U`qcjyTMAl<;(SsbB26N6*(6cCb_9BQ8k1jn1Z@#OGS%aaAVP@UDb~&c-g* zUDJ6wBFKN2o;&R>?O+yQtSi>Pz8Z&%Qt*!i2 zZ09s7%q57}BDZ~J$hWm@_s}ZX-Ogu0iV-*3LR;K>hSW#Y(!^h1R$;&)+lQ3@=xtT~ zGnW)ei!=g<9c9vT{LA&3F$__`3r=!D_rnR5LM3Qg-g6IC4zHfg*_U&IbHaMp%?z6u zIb2GZHC$9AZ1;CO#GsYHe`!QteW0|3U6wZ~CE+lOMu%pwSw}5fMiSZWB|(l-Ck(N> zE<)`R`$-DRHK3y(fD;Muhr&;xjXg^%cumoZ_DM%(IK|YE0F-E$+<^*0yUdL))HId# z_-y;Edit=*Ku`rw&1zDZx{V@|Gs^cSTa!k6B1%C3X&zIUZ?9Gi2_VBLK$wInJ$Q2W zd5)RnT383*NtD$Xqx%{AhhC@ehvGmeSZ#K=Qx)yzrLG9@*$slwjhOCS* zl?LdQk$=w%0E}cL0P;&937N4ti9bC0Cy`C5PZ(=b0E}}nUboerL-T8w5g|-Vbt2`s zAU(ty)CT@^E_%-p_y2;DvGvlbHC+(R9zdtH2M15KJNPD;uucU-|Xq&1fy^Wd`?^Z>3y0EGf z+2px|Uzsq5hcX-1Yq;MOdmFwx!rba9X>q4;`zjYgCc}{bptrXo~Bk zV7fL}xc89RR1J+-Nmn(x(XER%#=E>$&9H^H70BKz&iAe%AlhBJ214{$H68z(R8*S) z|ERZzS6^3^tJTn_VyhB*I^DhPgpm}#ymz|3k)%>tolEShg#$4$7_a7Cly&BLGnq#8 z>5t+jn_^trw%k(k!o5y>-kbDoC%-Jrs?B|Mo|jrM7a7_ynPLAPLldf@;3$cMJnZdk zuWmyA?&bVPH>vkD$yfU;+%@<(^rU(27G8ETH+^4ZMQh}0>pmNlf$xE;(R8#kAT>>x zEF`dClSg6cFG~m4-fHVV7RKRX-$fdm50H!{-2MCn0|TcN-c~0Mg@10}f0_*nM~ z78)pZ0PvaqJlM7}B&(-kK9=aO1h2W%G?5(;&lG{bZ}ylj`gJ)1P0=j=x98Q+A{$qb zIars>>8I&26GEc0H=AAbv3p}5-b0kQrY3_BPz&KW_Y=G5pUIId0H?pYZN%CT^GIl|DO(orxN!-t^uc$#QuCrQ?X6L`T`vZ?C*7**lSWa7FxzZEbiyGT zz`XotBHzO%oX58~`fS*Ma;-ied1Y1^$VB#;A8(E!gVh?B zlxPl*r6tQ;`#tiL+tYo!895AH|NjNIKuEuA{k~l!Et=CHNkP8VQo@grVd8r<{EL6y z7f)=4V+M*ow)ask-ORz}ot9nc_pebcqfqk(oe9pE*U9)LdFiQsZv7%d9BTa$abubm z_}VYu>DBy>VsYj1>w7tXv*LJ)A7#MwE8_MdQ-4%zK4|gYb`L~lx6uV!Nbm~yx6q<* z!`Rm4@J5i?kGy!)y_I~fqUI&07P)cVS`)E3y9q9&@9w*^7o*^6c!fctm|xS|zr+`B za_>BuXPq!}i{D+czvKZ)+H$jDT*stqy1pB?T05j`syoWL=o>+JkMDXSXyfSR-*X^- zycNZ09&&Wu0X9zpC`$w06Ot zYfQGiqbNEC1-%hb)k_oMJUiwXgJqHirQG&5U{b|ZQNoX$N8x zQGDnvYOLOTht0sQvopydAJ*^b%z{+jA&<*iz}J8SqaJ7^>8W!Ex%C-}w>MLysW`KVwwRFeRUdKW{`>neK05WoA6i&PYG!-9 z=>f-FrFY}MzY)v@Htz*4CaPwAHxYF8YwoCTg_hJRf~f16x!y4n$npjYJ<|~HyWYr( z735nju{70+dU2Ix>e(sQyYJLG$tlq)TdF0d#OarT}*3{En8tq z1niI+#EIG#Ywuk&q7b&o<0gFk-ZlSD!g`}1k(*75P_Ta8a3jDb*@ znjgxJ@xS}3ge$jVbVZo*pP1J*6~49IXH=J-o9hLY8>%|yXPyK$JXdWEK2rqik&VrN za0P{bYfZ3IE`M`;Yd1f#qB6v`-;7n+Dlk}{Cg1mcq$NHtydh{>QWfi zW|z+63Y>Xef}7Rf#i*vyBJfAOVU&0t0^|~p=QQi8svyo;^r3UwONYuboZtiR9paqu z{%L{N!8(VXavY)}2h5hDX}ksLQvlhnv^vQb-sB z%Had>Hc^bA+CM=gaZ!*XWI#=|T>z&Ebee>gDECf=Fp^k4~u!6;@&qDxBG( zb)L@Cg6hf*eGvT*7$KG}Yn*<>J56k!<0P0|>X5s&7LDV9+Ek{b8kt8+%eRhv#z26l zcEwuK!CrfSpH?4wK!JZi#{qZ_-pUXBV;@7DyID1UgfZ@^=Wub z0%rY+G-#+sEDs6<)AKscPms7zN(&$6RnDLpscnu(`6JwY>9#HPwsq%}brv%f)JMA& zP-cb24t)BTQR*KEQ`bd7(e#N;hqc1!F`}lFF7wrDYe!z3A)|k=A?O?aT}+!c0y7|F+9lLklmAnD!xs}8e!lu z@<=I4cSe9D29*-f{^BlyRA!)?U5BIu#D4cJ^!g3*NGK{u`9FxA{{MomOXP!wDstc& zb*X>obon;qyPBbEGwWu;Jg3|HKqmkgZ~*zbNEcT=ezKFIZ4)C$=Jl7Cj3~1f!isMn z$w6+`Beda)mdzoa-j;Nq_K*^&%$V!p3{K-=Oza*Fhgl%c8$C$!u|kg5f;}lyhf*`~ z#?Oan?@oLuCvoqn9E|Sdqr2Bj!_41<3=1b?cJ+<}fT+PJ1hn#%LObUJatOMqe1?ZD_Ht&KV@s7-P`QXWq>Od`_W%=(LCfq94p|bn%218-C}^ z2r62G#|~fupo2aD-}I8TgkS&=y#mQ%fzFZ*d&F|eL{d{yjP#A>1?ERQJLb}icwhjq z9{~Q}iUM2^T#ZdEZwU`s%3|$Vrc`Xc2Ce@9@*~&s~5Jr+b7F{hVGg#DiAh+Os!vCZQ z0Q|z&-&NSNlrHJ!bV*be_J!N2@mYuT_89EX6daPGHyrjo-Z1vlrC;4S*YO8R2FzL{ zQYzwazi+b5>j2to`v57)Fk?Cj?$-x$^3jm>J_B7% zWz!!rO6RG%jV2Da=H}@&V?a*TP+}&~pUoKkaBhH@MVjiK<(mkeiFF6{9O9%yMfxvw zZ5DUSzyYm(pkAb%_N4EZNuNXplXTN)6huU0qMC@oVkmUJ+ zp_4T)m|^7tQC2onljGFoC^KN<5)y4KIX?- zav2LJgUlG&HKeXN4$wvTORzvN;(a+*^@mc30fJNB=2!|3IiaVGA2USkNrkH_Kn5gU z_9viaZTsE8RSq-Q(*lX@-+W{P|5Lm;4(%)}0V%Q3R8i^cWGM`4mZ23UD!pj31w0N? zKm}Id2@Yehqob(o187IQCAS>bHV_>Wr$v?0n7M_z#&>8lV?qZd)DbN*Xs}ZVV@RqJ z%tC%3-7Tszg%hUkcX~*S!oW&AkX6HGen{^uSd)cpLsm&0wDss>w5QL_UhTphnX5nt zZkj@{J0NPS)5d*1*l{kU6Flo66+82@m}+W9y!-oq?Ij;6N87Pqj0Gt&r}dA+ZyC0! zd(I}*gxd~Bx5e5)7&&TC6c%|8XYD|q`G+{!MgGOuVD9j ztPb(UDeQRfTcJ3d2GXsh8Xk$yDcNm_mR0e3yUV3% zE`%20xse0~DZas@9+j2Fk^{-PL`ZM6W~L61LU35~Rxp?U#v#Z{bu=ByS(WS$X4v(ub<|e(-M`qbx8L{UcC5FZ)b2C?I zDR6Vsn0c1E(UgeAGI^nNozbN(7i&c)huQvI&SY+vl{r`>4CdrY25C3OXdg0_wM%ZD zCf$$+{n|v^uA^W9_$wdBH}i}dH_c5RVDhlq@SLf~ALnuoZP(zpBZFdwxAuxJ%1Fjn zSxJH13>oqf-u1WtH{-&ZN9gNNd&LgO3HU0(&Mp}<-H21l{}agtz9Jw$tbE{f*~mlS zH3A;3qRtmC&~YMD5&1NEDPD_0)=0-8c0KQKI^5k+p#fuz@jnp~N1D-`@(H$;GN=N# zyy0YK8GGVcIyp+^aL-R1qaeyq*8wf(6#8Drv!c^T2?z)y816OZ;}VW8v_)|Yp_OEd zK3zE@8fOvZnv#}{?%IlIGYpigYh;i1sz+^{$6_rdTx~dHso*s_JwNGfAeVPMreuTl92h?hPIRzyf^Y~DMDaY|!y5lq^j)TkKoEZHOs|Zt- z<|--d3PBZgnjC`TDwqH;!w=#%g0C<=Nc{Reu-Ihx?NAW)xmU$bb;bK-9(f+wyt#8J z(|iv)@0c77bv~N%)OVJ5!^!ij<=e$B;eH*ui#b*rG^u4Q^*thdRf}L^keyL~2cVl) zBFqm!Wq|Px8M9Y?2%vEw5vBzWP`pSm000L70MuUuu{-k%9dO1k8Jdf682%MWsp)I^ zi?0~>^jyUpYB2&k)IOZbD^ND506?+)AD1Y0=oP9S-fytn0{|aTC?XP&3<&@Lm;-

36c*U{ucJlV>t>H^TN^WG<3ziWiyt&`e2)-WSj6w&1QFf=cq4BJeG!0Q005x@ zfTAEU`!;Rc@AmJ#x7(L&Z7jLPAP@}Qk~|xP%)EjLpmHWo5=Cc2<2rL9GqQn&8Voap zS&bK~IJE>9g%fb{UT#9a$LL(?LcZ4lPd7HqjPs7~p+@)VyL8iikOBdNX$XWJ#7~uA z>FN5K0nSd3O{VGbl~Z&}O`{8t4iC?y;U(+)mk+S=-sFC9QW5e{BuK8p!ms9G02#PZ zlJIL$=VvTQ>lAs}!mJ{n-gUDeU2t#9)t;rbcF8&4jI+uz;?DA+A8-Wp2Ye)@v5D5- z7B25Nprw8_LAsL{#0|L2v8sY}Aam)D6d^FDmb_B(l=}VjwOOHhsm?Pun6s&S+cws0 z!`bYzT4rYL4!hG5Z?`QAzF(NF&Ht)3A2(SRrgP$6)ZPg;$=BS8EJ!%E+4{(u)ym8{ z9PNL&j%tws>*y5?R^MLeK|&2mzfn$d8XlQcD=rzzDH67OV$c)|X? z*?~K=9YIvd)s@%L=M~~G^Q9{j&R@JTzCtQv)ZNfkvtRE2KMQ1q*O{u!wYqF+S!a;@ zrb#>_&D&`Ddp)j+NDrg?#o~8iI1`u6DGUcnsr{PL34j?tG&KUtrj!6IMCY z5ITw#pw-gcU8ypfS!v^y=6bc$Q4XEEam>nSJF?VDSop5uq;t+=XN&w+5xTy@-puUc z?5U_fot;dZo2sp}$ySrv^Qd#@mK(mDC~Ad6K|07d>b$T18jOZ?Ea0ouw%f1$eY4x-J>l0ubi&ArICOHrQ3HbdhIQ*(P$s`Vfq6l=g3Gcl;m2sPvxBj(vA zI_50F%p0|Q51AgW2%HK?2t8S(>F7)8mrPuxik#Ab`pS|;sCa%$^v z)DQWpMTj%j1u>t7j%o<7{^Jll1EL{~x0tf|Zc$N*CyNEI;}R9R88mUU^2neJ67$nF4Ef ziFNRI&yM7S1PXDV=-9gPpNnT^flItKP{~QXI*Zz>`KY2&rfGY#r;p7mo&)nj;;=;L z8R?Y1=>B<7zvI%8wfL-eOIT)alB64n8~moif0o^e0nOQH@!n#ycG%vROkw?ok2;CS z+VcXYID5H&8E5hE*i|^s>@ab-s`OMm473CJ1+dop1!$=*lNoI-`i;^N!!CZer%k3B zJ6&(qJSc0eJLLv0>M2t{iB)r`$y>3ZEyhJ*;l~RlS0e@(E&8?RK##u+DOBmCYns;?@u zYYUI!J?WMlbKd5@pRwN+H4B{a^|iiQVah2chkfpLu3eOkn3BQzRwW9K;vOx1`gZia z_!nQ_!yo*nnD0vNOdOcfdfipOq7*ky7IQ6MpOaVU`8xJ##k0ITu>2;ssUCF}nZBOZ zQ><%R?>lL+-s>z#xVql@NotY#uKnj)tj*Lq} z0)XJq004d)Bm|j+n;nz{+WQI7HZYELl99Sz0_IsgN% z04o42aNi050YIbWkRf1_v;Y7&1pov7!);)yyv`tw3tl<9kc?WAvDzXDoHPC&&8f+z zgeVq}ZU8^hBOxik0syt#InVtoa=Gvk8UTR9004tsbF3#jZiKSwqf2g4xSXtRAuv+M zjBuk%At}cbTt^p4J`jqI@hP=a_(Pd4BBZR~y91}+?Me`&BZ*a?r~e)Gh>#Z`&$>&D z*Q#Ls(b|du-i{L-AbpAu0Dv6U008s@-QH5e^Z$~IvG{bTcLgd{kEiIO>4)Rrkz7KOSlTKLNJL{TJ<`$W`BId9zoM*II< zT$FjK-objJPh%o9l`DE8hkd-VqMY-X>O)+{_VF;}tcbS*KuuYZj=n3ss;sFf?Y))K z!w9$VVQ9k)XFD+j_GqWBh$ce@fEu!2v9vtd004&;006rHj%82odko*R_}}a;8|}vv zt{o%2?_}{IllpI#bzw_qd{HX>5>i$y+W`l&PkIhoRl%Qw?_>`!eCbCQ(i?f&x@3DYpRt{AsjP$(U2V|(U4#W4$Lt&6Run#xCDa2j0^m_4%Iu(W zaO7laADUg>N_R_WFQlnPk?9yx%g-5ktw&bI{GJ4ZSGaN*?U{zD$zPRBqv&vmjxJP2 zcym33D_0o*n~KVXRLfb_nYd=lU$Z6G9&2c*k5nR9Pu*lgPFy~(>|&i1_p8&WLW z+kKadQ*~=`#%mg=O>-=zZE?jn=CZkxgrCn4@DVv665to{fQZ3B00062MnrG`eo*iG zyT9(fxBK1g-R@_znRX#Fa~2;X(=6K%i^MZ^>={E@z%3QTUKDvYUKo}ng@qY-8A-WW zJR@z0QV8%0K0o<66VR=#UtxUaGi!EI?Q1hd_QPmjH)@VaMBjPI8?2@9JC)Ta#eB6- ziGqp2iY6@(RQF&LycuHd#^?v_BgiwS%}8aOyWmTkbv;Og6#htYgA4a{;;%fT6w|e! z8I3epx~vp6xlN11>hLYp*RYsIO7_uKl)IGUBre6EZ|ZK}?>Dn6>uWi$R~)AP=e#|9 zYk)fbq-t8htIfPRzZY!jRCS2MwOG%Jad(<@_7-;W^uTnFNqh$*9ub}>=}{YT)1@$1 zImD{AIwjwO$=er&OL5?&IXt=;c%R11?VYE3_#f#%yk|JQ zj(x0)iVs=&P)^@h)s5*52-aVj(>0*&OyApiq-~RF*FZgq+?JF9S45s zHN7feRHUWSOPz^IG#hEB$yLGfd7B5g%_zjpTX8l8Cpn*J1P5nitTpCvxY@a1DSMw0 zDOVE=&5T1%wHaw9cFCo|hg{(1qcoe(c8GF1DESJZwhX}h%SFS(;x#JTKPSy9j=NjJ z`X{rC`d3gMWOyg*eEAn!K_K9TgMq5npMrE<$ zH&hiJjsCA4$}C?0iz=zrzY6tpw|ru?IK_H8ZEDk>@nUobQm8e*-7|Kv{)73T$EZ-Z z)uwdRxIoLnFezdCp5$v|*ZtYj-+UmMqz(G@UQ>-jCs_9)I|0HMS&k=>gH4i-^D^)^ z`D!%HB$uxi%8%73#7`>ySZukx?%^d=MC)GLD%Z_Wn`0R({99e^wT_IbWGS(|YpHB` zx8>CVr!iw>WR#Jn!T%z4xtP1Y_ViQsr@OxTFLwwknYVLjh`eKQtLiiJYU;`#AxCZ> zDLpRVjTFOVp_lth`h?q#6yclR`9#-s&qP{fYfDry5SFFc%1W-HHlip~cAC}`I{1rp zmnRpo3NCZFMmn+Vxt0F$?kM|jLmwGe?&Lc=l6E9Yu5S$PC753HSK<~sHSXJuVY#H1 z^71KcSf3~1C|l&CROw^>1$>_eVnl1l_6~)SI|S{VPqllWk{pA`C~4qLprlQhnjxcHBNeDWl;@al z_L}D6{cSxpY=Zx!N^C>7M6Zs*h5ziBU*@a<3IzqqZS)nY6txpOn)(fzexG~C zHwz;RphL>3nvXW9bD}@=KgH+XMCU0d4$_g0ul0W50@EpD57YLwEE|5G*q0$1!m_(0 zXEk+i^RFl*pVP@yjsLc3aIf;8pwAI!gm>xG@~dt?i%SKQ|S) zVd)~sx2|=Jt7mG0XVK3eR?hW$<(I3?t9wtQDj<)#%49ytfNE;9tww{+w4H!(up)rgmx|F}N##w)-t9NFnwadNpwHX2_cu_Cs zc|s!8*>7Jw3Xp78-V=wfO7Cl19ed;FcgiVWb z&uj5lE?+X6%Zss!WEVhoojtL0&zZe1Z2$r-DElxnrky91g>T!sgusjZ0os$yURYi# zz5m+VsCT_aH?dk16^{Tr>Lx|{OtScwkV2TtSVb+TS)rTu!D~!OAB(~HAV&i zDtfjir#c%BF1TG>8~W{sO_F<)TP;m{!hW#vx7OO&Ev;W5g1nx!zEmyQfooui*clAe z4zX?}(X}zH{(1KU?-WUH0i=mbqFG;s^*k5z)V;0YXI03WFu2t%oTE3wlJeKMP}=6Jd`c4<+vPKW>hYsb#(o+FSxK zx9EK`RtF!;mxdzUUPlK2{O9c3^7Fdaq(it&%OzFcbnA~^tT;)kKEsiC7rJ=_tyRnM z;G0b{vW&{>$(g|BGJ5y)p6SFmL(&^`W7A2uBPGTkS+}T&5L?FN4egC6xAZx$;~{N^fe0QdkN0N@z_he<|*kTrORYCT22*pY-ZSDI@rwQM}jeuw%uieqkv=urB74jFmW zX1aCA0H9w6|4Cfgx-$@`0PNoZ01g2FwUnWb9en4XzUJ%rOCeN4r+z%e`yPB)vwm+gr zBb;%3#ns?T)ioOYc83EY4Z>1OF%BIK=6Mn9xP{~kowCAAhVrRz8}$|Y%a{SUH$PLfk&|NMRVU0GBo zC=&7z<>f$;0Kj(-fqH@>1AuTK005c~U-pU+IUr3eb98?1k%bmOpp$_jj}MtjP-^Z) zInB&)%H=N&4W+>9LlFSLE)Edw3PlD0;6MNXml+F!p@^TO3tdA)haxvEMLrw}8GYKt zgrML_WdxD6Kqd|V^e#jw0GdDs0N@+|6`tXPj5@Y_$0(35@;2|$N2JGrp3-?l+W@xe z9uWY5P7euuCglLe2nWV+05}l9M+qAF4+yIBU03cVp)e>Ir_XOX$jo)m6`scO| zKb9|Th{8UP3;@dOg>&U4fATwj?aXX`wO;C3Cy{t47UG4yiJd0+(?@Swb1?=Y*ytUb z`eh!u#`ECHKtZ1T_=wo=!51l_x@62{8&bpiL7&3Rg|^=T%P0-qo92wu-y}BkVSP+$ zM1D~RK~I*t!lR~Atv}}%UN@2AZ&tQ${yKJWnt&6MH(MkQhMNsNZHNd4Sk#g^n32jx zZQAnP=js3_+P`{Q$gj9D5CBhji>fxm$=#|_?(Ww(XzvHb%W0by;+IBD0H1vZHWnF$ z)WgL4ch{>CZw4VO>clyA{R6~rj*c(Bbmer*uRd`n9+v=M5D)_Z002V-Kq3GD4^_!5 z+cTCrP-YO3R8e#(sFX>WgaS(!;F;x&UU^>8d+djjGynSk0AT?D8kx8=C;+ws_74Zb z1z5f$PZQ7xWPmPYfr!S^>PI`)tz^vEcBfxithwzTP8iticE3(&%jMa4jaSYXmN_%- zaVoQw!QmTbe@T=C5S-o#5+hE@KnNq=NO%AU005#91F(RD?|b*Y@4N1M-ut`z+k4wC zb-Smcw^heRxV3gtMkLGZ5~lt_i4L$`kzkM_`KzxWkmPZWC##ttCZX|{uSUP-B!j-6 zv1T`!G6Okoc!o>VkXwh~t0qrRF+|=h-Jx<2=w$7ttLSb%cnp7=9X+L}8>K^BTvBL_ zQae}%-%*ADJ~-5l@zglzN6t=qX$mIQP*?1#QkkB^n6k%uYRs*wrmcf2zqbNhK0c?W zTE(jQXrDz~G+tf9)k5a3cr4z6P4`&w6J?#U#%k2mO{=e7G8fhF43WRxAS6>bSBnFy zFdr&a^QZfEL>DZ@h_KAFQeJW6xbA8_3Zt*J`=ejDXtjIO_joo+!Pj09Ws=5JmrOkl~{!ZRePiDDZr)K#@&@OqXgI?v#TlT zMDD3I$MvQXYMrpLX0%}>Zg)fmS%=D|nB14MN}ol{%P8)GG7qZc)0N2ODpgdkX0|Yr z_xMpWgB+BK8IeeikG^MS%Oq*O$JbnthCuFUk1J)+RpxH)Tuxbej5xi_Hwb~JiP5Zm zpCvX<78u|@IA;C!OmfFd-!9!G*CUDOu5IE_&Bxul;>qU+&lqyhpD7XQ9K_(LWT8>i zk5|1Sm|CQ=McU^*xXBH3eL1TLmmS0({v$0GWD!T6db|ohxloUMXBrqmq+KsH%&a zV+JIi-LhyCcU$SH&t~`Q-EH%^=I3Y(9_3{fMBS^&R%o)(8Ke+C^HiDBLnKSsdY_OL z)Kgh+OxKh#_C0QJ0N-ple+nO-o=pl#))p#4& zOJ}uZkoviRujoTEV8{g7dz7bO?oO89Nj&{ANkgC=^j)$VjY~2k#tP-+RusJCl=&ji z>(04}cT=K{&AL`*+Bo+0F;@6@>-q&7QG>)%V|%kuu5n#oYQWPH8K$Uvn2Byr3MsOa zhuzz=os5&!^|hI~N>*-YujzY5n3!S9&H+>Cp&xxX6eU)U< z$7Dfo@WwZ}4%qIk7j2u7@JL{y$W~O!>D_8j(__a>Tzl;;q2kB|Xk5NF%#Gq6whEKj z2Qh(l_|UGJBG0#>Mz(qw-$rh4RW!~GqwBGYyIQqg`f9r_JyUqZ&8x*raYK6=>E!q9 zcxDeO79#M&7X|NGn0d&TC^LHdp*qvPp?n2h-re35*)=5zZP!OQSTXbR_+;Phm(kXLZT>e4xVX9!xz!SW^C||Dw$Q>SJK5fJQ;*}$ ziBnZa#lz*pyLCclDC}jonVIYbetR)P&Cxb~G0&!zKoUZOVs0l;`T*C2q|^o2n);|I zub*r3w_EYyxrj-9H8q12vT7fS+e0k(e8IESU%Sd16!d_$Pw^3clood03^MjM2-9yP zAy1gMGTtgUR1^y9R9Hn< z9x==pM?Ep_siPQ^&a!nIx5s(OB7!ik%*f-~)+RCzsNa{($3h1GSewH(UVkHfg**oC z$e*lNG)dwmxvXwNNON4IE}!ExUOM%kgLDP`hubn&xX3#KKG&o{S?FkFi6_Hrm2C&;3-1)yv+X z=Nt$u)=L1x-nleyeJaezAZz+C0W(u*Wm%6fk>S^9H0=&5smMlYA0`q)a8g5M)U`UJ zkMWh~ubUw{HJYhyC|d(@Z~N;YI{SGkk=&~znDupsrmcs=;~snmm6=JaE)Rm68eMek zv>~wk#|{Sqgk(-NvVGeK+ku*28$9Oi7%0V)T%)sJdl{MnSbl370z#-V002%NnExpw z0DuW!0KfwP-*?1V&&ZyjMr;jRgWdhSLps*ovky8a^Wn46iGfjmkr^Huj2IZiC$h|l z%K`Nc?XZ#E5DwNL#@8Rs@ld=OP5@+JZAJh94+GwVn-Ktjgf9T#0RTU{T9q>*U^rD? zFnQ{v)?nA)bw@(Hx5}0gUlAi*hAf>fd;!<;LihEM&cdFF6km&(VdF8P8)!*s5$%AB z007{_Ug-eqb4(zJ8UVyMY_{3> zAJ=PCXT%T>005-Xvk7X4=^l#1pGG(ZfpOCE!rJYuv4#htd%)86)n5bA0O(Nz0I)g$ zZawbEW8K_k4TeK%+$-Kh4FIg7!QiE01u5)#pG_?c&sq5z6QKaW^#i*8yu@>>x0pSq z)v9)u%eA+K<|*MNk>LDA2$rJ9ln8qPdg3DTC@@cDDva^XMAp{x@C+T6?NTsu<}4|a zH>1VrL-Loj$$ah5idVB3TPw`Wc`ybS<~+i5cfm}izDVq)f!n+p-vA~>-*AZqn-KtjLN)+!007e5Py4(({pUiQib}$H^okiVHnb$9 zthnapn@_6mP3c7+En0p9Q)S-T8(gf9Dn&321_P>orZfn1A*B^{-4-~YAl}vXwzj$z5$kk+d&izbcwM@9 z-bHA7N@U=DFdYB`vMcaLxDE#ZN#{rE0)L9Y02(?QONHM%+1)xlm5N_gVE0!KIh?Ch zNUDe}HYpf;X|-=;kwYvM0|2M@7|&R6YHJ$H%hm9O|E-<)cGiX4g0+j?FAWj%gOL}l zyVj1QiX_O;FoST#A#T=?Xhq5Efqos}(6v6C$B!;b6dFv5r2aW1tRN-jx7<)I{F?W$ zmjl$ZL%uU&Snww-o>Tb?)09VpY)pDSexr>HaL+?AI>487p=fy$T6CaPP08)as9^2n zFVydE$mf46N0awS?KM@9qlX1JeL|ISo{vpHi!L z?b+$p-uIhh2tGVlZ$x2SR2~BWNrSbz-xs035o5f12&#f9a>+LlrnrEk2VzRPAt@vxkPv+ItPdk2yW29^~L=5D)_Z002@{KqCME z4^`wW_M9or1v6HWt=b2oC0NmGL6E({o(ate>$Zk&yW6{)b5)uC{r`Y400525+!+)A z%K`rhLoftdz9jJxPa2p3P0|BVS)+^|yK7x7Wwx_ByJLfzD7(!a`$f>~)t&sREjx_i z9VdzN+HuE{x7#7^jrsQpP)1pai=a%~|3-gJ>skqQ$T5{o~cxwn&l_o*uR$~(sB!qz% zd5geXnWaGf2+gA?9gHrad0UlAcB&kTuH!?-D6@)%KdS%#$F#Yd@ER9u!HrK2OIsFz zltX7r@h+L=*>}{E_x3V%oxi5W*`$R&YJCn@?r7x6cN%if56-%@J8G!iAiATLni4~4 zn;ukW%|Rp$$-~YqN+jo_g)kOZJ%-LESEp~zF-Z5%MUC$Y46gfiO*TE_$#cbcjM(aa z&LWHdVmG>VG&kbRgf{(pPR~c(UgU&imiSm;5cTrf%g8K$ z(IX|}$EI1>XR}$jci&2EIf11&|sdj zhp}K0iMBSe=r{Y#keWqqF~Yh}ldhKF{{M{bql=*ft+OX;8lCRsJ$Sbn7!MV93B(Ob z%VIUhLwg}FW<9|~VB``FQd?fFB>FvvsS^Jw*;y)(`M#D)$`e~!MQWDR>(s>6=zdpS zmTpK~Z4x;xIEV?Ulg>V@Qj@{Y)l$%@k-R(vOF`-Bob-!GrPykT{j$Qz_fwCf4-8*3 z$Z&J38~sEPK00lICN*x$u3BQ180MSDUV`Y%(i^KBjX%SIU$Hl{NBkE;LOFV_8(px_H-#NlPeOwa%o9=4+0m-~r6!@Yg)Zm7(vqQxI&OpL{+e{%2J6so zW9OBBA+ZPPc!*ckth=qD{nnnxSU$&hi%fMXrc*(T3JV!4_+R6q8t9u8gF@5Il8va-i)Oro!PNMG;JEa0*<&-ZP28D z=R$mw#WfRy9O>-g7qR5Fgq>SEhu*D)_IVqzABPTY0y?V?-7$R>85hD22W-^I@WU4o4a+Tr;D=oW4Tp8!z!r1t=sg0+Eb9u#Ns)Rjl)bs0 zy+wLQ)au%KMz5cq@6v+HBi$R{o|CN(I^J%p_YrVcEi=Y%{%#(PyNjQS&q%}TWSm+V z9O((aqY)vqExbmFk=~`zXipq^aA<9)=uq0>IQFo+OSsW7cZXgdU*~(Kh^~3+T-z^T z&6D=#UO&XXNSi8@mcAD0R9>KxXb4JGfmG6#qNJR-ZgQx!T`3FJJeGEQfQlAvLpgis zpklvI=m97%EdmGfKw-u17=Ht;W_%^o z_mM>eVAstvO;i?lF$=QPQnh;IBbm`qHbLi!%3Weh+2ALtxQeY!2r&B#A z*Q~W%jMeFNHK#Tfr@biKEwjC@c_RMHKX%B83dDxxr7b-1}bX*f+<~7h6bW?_* z=64pFzlJNh7n&3H{ZSm)>nbhTa$(LU~xzgFMV!e5!88$rn?VNS_Z+ zP5FIU$s@Tm& z>(AXWD%O$LcBQ1$XAL^QeZz>;Hb7-9k7 z@xwYjN%N)l9DgAyfRQ->97T?MP`F|j5$XWAH~@u&#={#-wyUSL?EWjfne6>YzG(04 zh|5{+#wL@Dgw#|t?&%sLcwk*@X*o{e(Zwlgv@R+JD|pq^IlBvVd*+bT35(;P55}L= z*?8a5`95I(E$eY5bqxXXPI(034Mdv_yekWzGf>!%8ZElbTQJuAcaW0yk*0Ph9~%4S zQl;$Kf;ZsB&G8~4gfJ+I5brhqJlH�ALyb(%+4weRTkk!~+Es)X)F`#|8cx{4U~G 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 ( z_MSSZ(Tuc_a-Kg`(g_s*PT))BXvOG#^GWMmViRLe3Ox7^D*KK66>%JvLOq{OR7CwH zf5V^H?0kqriv@xRwwrmRHxudQJyrJML0tZ$%V$qobn`T$&W87wAEZZUPWjX8%tBH4 zFEC1im$lGV{|yv{Q4LzY!=n;Tm+1cHqRZb5i!Hx=&Hwc4mZCo@C}~f*Qwr@0A_gr) zdy~lInGW)v^yyDu0^dVIs6=0=-(F%xpTwktfsegTc)^^h^%2YxNIv>H{c3~GCs)>f zS8ZEo8UOp{y9|?oibE}@GTnbT1nCXEQ_a8O(>ruWK~Foblu$@VF*c2~K{M2;^k)Hr zeuc_~NeyO3!JLwv4xOm~J8&d>Qr}$r2Jn)1c>Q`vf~v$Z z{mnrP=lxG{Ib?PBkF3mcjK{$rG*6MkrPH3jP*b}9Ido^!Pb@3m+(J0+pif1<^^bW@ z_=`XR3)$lSdFRN9c`ztmX*p$uK0X*TbI$okq*+D(W1&vYE&pc71uh_e7ubg}dj)KPMnQ^!HCEo^^9I6KK@zRX=(ocnOTXg|34$6_xxuwk%<0qUk&-RRS1Oc$y9AR!Mj@eQ&PeqIP7Rz7Nrl9Pez{n;Y=$*blWK>2vlWYD2jC>_gNJf)`=;$i6tahyD3;kUuRQgmS`C z@SFtXU&9&zRBt}L*o(H*_g+T+hf_Lg@V)*wH~TAXvC=>+InR7do4oXlf3lDJ6~Dii zu7dr8I(uLVK#dFt+>>@f)NzM2V1_-6%uhVhitZrvAF;RZ5TM~Wj&+doe+M7zYgJM^ z@@)qsY(4mM)_qn)IZdmvyp>F`e1OoPyTBA6;j=KKmeovq@0r%yzHJth!G-$ Q2q8j<5F$hf5l@5w@D-4*asU7T literal 0 HcmV?d00001 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. +